mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-19 22:36:49 +00:00
Add opcode for despawning actors, send that when changing zones
This is to workaround a bigger bug where I don't properly enclose actors in their zone, so you can hit an assert while traveling between zones. But this is something that has been needed anyway, and also fixes that.
This commit is contained in:
parent
55340f4e8c
commit
10c1369119
6 changed files with 86 additions and 20 deletions
|
@ -154,6 +154,11 @@
|
|||
"name": "Equip",
|
||||
"opcode": 468,
|
||||
"size": 72
|
||||
},
|
||||
{
|
||||
"name": "ActorFreeSpawn",
|
||||
"opcode": 144,
|
||||
"size": 8
|
||||
}
|
||||
],
|
||||
"ClientZoneIpcType": [
|
||||
|
|
|
@ -65,8 +65,14 @@ async fn main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::io::Error> {
|
|||
if id == from_id {
|
||||
// send existing player data
|
||||
for (id, common) in &data.actors {
|
||||
let msg =
|
||||
FromServer::ActorSpawn(Actor { id: *id, hp: 100 }, common.clone());
|
||||
let msg = FromServer::ActorSpawn(
|
||||
Actor {
|
||||
id: *id,
|
||||
hp: 100,
|
||||
spawn_index: 0,
|
||||
},
|
||||
common.clone(),
|
||||
);
|
||||
|
||||
handle.send(msg).unwrap();
|
||||
}
|
||||
|
@ -107,6 +113,23 @@ async fn main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::io::Error> {
|
|||
}
|
||||
}
|
||||
}
|
||||
ToServer::ActorDespawned(from_id, actor_id) => {
|
||||
data.actors.remove(&ObjectId(actor_id));
|
||||
|
||||
for (id, handle) in &mut data.clients {
|
||||
let id = *id;
|
||||
|
||||
if id == from_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
let msg = FromServer::ActorDespawn(actor_id);
|
||||
|
||||
if handle.send(msg).is_err() {
|
||||
to_remove.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
ToServer::ActorMoved(from_id, actor_id, position, rotation) => {
|
||||
for (id, handle) in &mut data.clients {
|
||||
let id = *id;
|
||||
|
@ -453,7 +476,7 @@ async fn client_loop(
|
|||
exit_rotation = None;
|
||||
|
||||
// tell the other players we're here
|
||||
connection.handle.send(ToServer::ActorSpawned(connection.id, Actor { id: ObjectId(connection.player_data.actor_id), hp: 100 }, connection.get_player_common_spawn(None, None))).await;
|
||||
connection.handle.send(ToServer::ActorSpawned(connection.id, Actor { id: ObjectId(connection.player_data.actor_id), hp: 100, spawn_index: 0 }, connection.get_player_common_spawn(None, None))).await;
|
||||
}
|
||||
ClientZoneIpcData::Unk1 {
|
||||
category, param1, ..
|
||||
|
@ -713,7 +736,7 @@ async fn client_loop(
|
|||
.copy_from_slice(&effects_builder.effects);
|
||||
|
||||
if let Some(actor) =
|
||||
connection.get_actor(request.target.object_id)
|
||||
connection.get_actor_mut(request.target.object_id)
|
||||
{
|
||||
for effect in &effects_builder.effects {
|
||||
match effect.kind {
|
||||
|
@ -1014,10 +1037,9 @@ async fn client_loop(
|
|||
msg = internal_recv.recv() => match msg {
|
||||
Some(msg) => match msg {
|
||||
FromServer::Message(msg)=> connection.send_message(&msg).await,
|
||||
FromServer::ActorSpawn(actor, common) => {
|
||||
connection.spawn_actor(actor, common).await
|
||||
},
|
||||
FromServer::ActorSpawn(actor, common) => connection.spawn_actor(actor, common).await,
|
||||
FromServer::ActorMove(actor_id, position, rotation) => connection.set_actor_position(actor_id, position, rotation).await,
|
||||
FromServer::ActorDespawn(actor_id) => connection.remove_actor(actor_id).await
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
|
|
|
@ -4,4 +4,5 @@ use crate::common::ObjectId;
|
|||
pub struct Actor {
|
||||
pub id: ObjectId,
|
||||
pub hp: u32,
|
||||
pub spawn_index: u32, // TODO: local to each connection, terrible place to put this
|
||||
}
|
||||
|
|
|
@ -203,13 +203,6 @@ impl ChatHandler {
|
|||
.await;
|
||||
}
|
||||
"!spawnmonster" => {
|
||||
let actor = Actor {
|
||||
id: ObjectId(0x106ad804),
|
||||
hp: 100,
|
||||
};
|
||||
|
||||
connection.add_actor(actor);
|
||||
|
||||
// spawn a tiny mandragora
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
|
|
|
@ -60,6 +60,8 @@ pub enum FromServer {
|
|||
ActorSpawn(Actor, CommonSpawn),
|
||||
/// An actor moved to a new position.
|
||||
ActorMove(u32, Position, f32),
|
||||
// An actor has despawned.
|
||||
ActorDespawn(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -98,6 +100,7 @@ pub enum ToServer {
|
|||
Message(ClientId, String),
|
||||
ActorSpawned(ClientId, Actor, CommonSpawn),
|
||||
ActorMoved(ClientId, u32, Position, f32),
|
||||
ActorDespawned(ClientId, u32),
|
||||
ZoneLoaded(ClientId),
|
||||
Disconnected(ClientId),
|
||||
FatalError(std::io::Error),
|
||||
|
@ -310,11 +313,12 @@ impl ZoneConnection {
|
|||
.await;
|
||||
}
|
||||
|
||||
pub async fn spawn_actor(&mut self, actor: Actor, mut common: CommonSpawn) {
|
||||
pub async fn spawn_actor(&mut self, mut actor: Actor, mut common: CommonSpawn) {
|
||||
// There is no reason for us to spawn our own player again. It's probably a bug!'
|
||||
assert!(actor.id.0 != self.player_data.actor_id);
|
||||
|
||||
common.spawn_index = self.get_free_spawn_index();
|
||||
actor.spawn_index = self.get_free_spawn_index() as u32;
|
||||
common.spawn_index = actor.spawn_index as u8;
|
||||
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
unk1: 20,
|
||||
|
@ -334,6 +338,38 @@ impl ZoneConnection {
|
|||
segment_type: SegmentType::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
|
||||
self.actors.push(actor);
|
||||
}
|
||||
|
||||
pub async fn remove_actor(&mut self, actor_id: u32) {
|
||||
if let Some(actor) = self.get_actor(ObjectId(actor_id)).cloned() {
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
unk1: 20,
|
||||
unk2: 0,
|
||||
op_code: ServerZoneIpcType::ActorFreeSpawn,
|
||||
server_id: 0,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ActorFreeSpawn {
|
||||
spawn_index: actor.spawn_index,
|
||||
actor_id,
|
||||
},
|
||||
};
|
||||
|
||||
self.send_segment(PacketSegment {
|
||||
source_actor: actor.id.0,
|
||||
target_actor: self.player_data.actor_id,
|
||||
segment_type: SegmentType::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
|
||||
self.actors.remove(
|
||||
self.actors
|
||||
.iter()
|
||||
.position(|actor| actor.id == ObjectId(actor_id))
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_class_info(&mut self) {
|
||||
|
@ -359,6 +395,13 @@ impl ZoneConnection {
|
|||
}
|
||||
|
||||
pub async fn change_zone(&mut self, new_zone_id: u16) {
|
||||
// tell everyone we're gone
|
||||
// TODO: check if we ever sent an initial ActorSpawn packet first, before sending this.
|
||||
// the connection already checks to see if the actor already exists, so it's seems harmless if we do
|
||||
self.handle
|
||||
.send(ToServer::ActorDespawned(self.id, self.player_data.actor_id))
|
||||
.await;
|
||||
|
||||
{
|
||||
let mut game_data = self.gamedata.lock().unwrap();
|
||||
self.zone = Some(Zone::load(&mut game_data.game_data, new_zone_id));
|
||||
|
@ -708,12 +751,12 @@ impl ZoneConnection {
|
|||
.await;
|
||||
}
|
||||
|
||||
pub fn add_actor(&mut self, actor: Actor) {
|
||||
self.actors.push(actor);
|
||||
pub fn get_actor_mut(&mut self, id: ObjectId) -> Option<&mut Actor> {
|
||||
self.actors.iter_mut().find(|actor| actor.id == id)
|
||||
}
|
||||
|
||||
pub fn get_actor(&mut self, id: ObjectId) -> Option<&mut Actor> {
|
||||
self.actors.iter_mut().find(|actor| actor.id == id)
|
||||
pub fn get_actor(&mut self, id: ObjectId) -> Option<&Actor> {
|
||||
self.actors.iter().find(|actor| actor.id == id)
|
||||
}
|
||||
|
||||
pub async fn actor_control_self(&mut self, actor_control: ActorControlSelf) {
|
||||
|
|
|
@ -214,6 +214,8 @@ pub enum ServerZoneIpcData {
|
|||
ActionResult(ActionResult),
|
||||
/// Sent to to the client to update their appearance
|
||||
Equip(Equip),
|
||||
/// Sent to the client to free up a spawn index
|
||||
ActorFreeSpawn { spawn_index: u32, actor_id: u32 },
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
|
|
Loading…
Add table
Reference in a new issue