From 3757f4e0dbe46851b83a604f1d7f51b44a228287 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 18 Jun 2025 20:28:03 -0400 Subject: [PATCH] Make !spawnmonster put the NPC at your location again Also included some refactoring that will help networking these better. --- src/bin/kawari-lobby.rs | 8 +++- src/bin/kawari-world.rs | 2 +- src/world/chat_handler.rs | 5 ++- src/world/common.rs | 4 +- src/world/connection.rs | 9 ++--- src/world/server.rs | 82 ++++++++++++++++++++++++++++++++------- 6 files changed, 83 insertions(+), 27 deletions(-) diff --git a/src/bin/kawari-lobby.rs b/src/bin/kawari-lobby.rs index 7bdd752..96f61ac 100644 --- a/src/bin/kawari-lobby.rs +++ b/src/bin/kawari-lobby.rs @@ -104,7 +104,9 @@ async fn main() { serde_json::from_str(&body).ok(); if let Some(service_accounts) = service_accounts { if service_accounts.is_empty() { - tracing::warn!("This account has no service accounts attached, how did this happen?"); + tracing::warn!( + "This account has no service accounts attached, how did this happen?" + ); // request an update, wrong error message lol connection.send_error(*sequence, 1012, 13101).await; @@ -114,7 +116,9 @@ async fn main() { connection.send_account_list().await; } } else { - tracing::warn!("Failed to parse service accounts from the login server!"); + tracing::warn!( + "Failed to parse service accounts from the login server!" + ); // request an update, wrong error message lol connection.send_error(*sequence, 1012, 13101).await; diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index f9a795d..8beb848 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -896,7 +896,7 @@ 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, spawn) => connection.spawn_actor(actor, spawn).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, FromServer::ActorControl(actor_id, actor_control) => connection.actor_control(actor_id, actor_control).await, diff --git a/src/world/chat_handler.rs b/src/world/chat_handler.rs index b51d26b..7d63a26 100644 --- a/src/world/chat_handler.rs +++ b/src/world/chat_handler.rs @@ -106,7 +106,10 @@ impl ChatHandler { "!spawnmonster" => { connection .handle - .send(ToServer::DebugNewNpc(connection.id)) + .send(ToServer::DebugNewNpc( + connection.id, + connection.player_data.actor_id, + )) .await; } "!playscene" => { diff --git a/src/world/common.rs b/src/world/common.rs index cdc80b2..a1a7fbb 100644 --- a/src/world/common.rs +++ b/src/world/common.rs @@ -24,7 +24,7 @@ pub enum FromServer { /// A chat message. Message(String), /// An actor has been spawned. - ActorSpawn(Actor, CommonSpawn), + ActorSpawn(Actor, NpcSpawn), /// An actor moved to a new position. ActorMove(u32, Position, f32), // An actor has despawned. @@ -88,7 +88,7 @@ pub enum ToServer { Disconnected(ClientId), /// A fatal error occured. FatalError(std::io::Error), - DebugNewNpc(ClientId), + DebugNewNpc(ClientId, u32), } #[derive(Clone, Debug)] diff --git a/src/world/connection.rs b/src/world/connection.rs index 81545e8..defbdd6 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -214,20 +214,17 @@ impl ZoneConnection { .await; } - pub async fn spawn_actor(&mut self, mut actor: Actor, mut common: CommonSpawn) { + pub async fn spawn_actor(&mut self, mut actor: Actor, mut spawn: NpcSpawn) { // 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); actor.spawn_index = self.get_free_spawn_index() as u32; - common.spawn_index = actor.spawn_index as u8; + spawn.common.spawn_index = actor.spawn_index as u8; let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::NpcSpawn, timestamp: timestamp_secs(), - data: ServerZoneIpcData::NpcSpawn(NpcSpawn { - common, - ..Default::default() - }), + data: ServerZoneIpcData::NpcSpawn(spawn), ..Default::default() }; diff --git a/src/world/server.rs b/src/world/server.rs index 6cd9d35..4a95107 100644 --- a/src/world/server.rs +++ b/src/world/server.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use tokio::sync::mpsc::Receiver; use crate::{ - common::{ObjectId, Position}, + common::ObjectId, ipc::zone::{ ActorControl, ActorControlCategory, ActorControlSelf, ActorControlTarget, BattleNpcSubKind, ClientTriggerCommand, CommonSpawn, NpcSpawn, ObjectKind, @@ -11,10 +11,26 @@ use crate::{ use super::{Actor, ClientHandle, ClientId, FromServer, ToServer}; +#[derive(Debug, Clone)] +enum NetworkedActor { + Player(NpcSpawn), + NPC(NpcSpawn), +} + #[derive(Default, Debug, Clone)] struct Instance { // structure temporary, of course - actors: HashMap, + actors: HashMap, +} + +impl Instance { + fn find_actor(&self, id: ObjectId) -> Option<&NetworkedActor> { + self.actors.get(&id) + } + + fn insert_npc(&mut self, id: ObjectId, spawn: NpcSpawn) { + self.actors.insert(id, NetworkedActor::NPC(spawn)); + } } #[derive(Default, Debug, Clone)] @@ -81,14 +97,20 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i state.zone_id = zone_id; // send existing player data - for (id, common) in &instance.actors { + for (id, spawn) in &instance.actors { + let npc_spawn = match spawn { + NetworkedActor::Player(npc_spawn) => npc_spawn, + NetworkedActor::NPC(npc_spawn) => npc_spawn, + }; + + // Note that we currently only support spawning via the NPC packet, hence why we don't need to differentiate here let msg = FromServer::ActorSpawn( Actor { id: *id, hp: 100, spawn_index: 0, }, - common.clone(), + npc_spawn.clone(), ); handle.send(msg).unwrap(); @@ -104,9 +126,13 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i // add the connection's actor to the table { let instance = data.find_instance_mut(zone_id); - instance - .actors - .insert(ObjectId(client.actor_id), client.common.clone()); + instance.actors.insert( + ObjectId(client.actor_id), + NetworkedActor::Player(NpcSpawn { + common: client.common.clone(), + ..Default::default() + }), + ); } // Then tell any clients in the zone that we spawned @@ -129,7 +155,10 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i hp: 0, spawn_index: 0, }, - client.common.clone(), + NpcSpawn { + common: client.common.clone(), + ..Default::default() + }, ); if handle.send(msg).is_err() { @@ -180,11 +209,15 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i } ToServer::ActorMoved(from_id, actor_id, position, rotation) => { if let Some(instance) = data.find_actor_instance_mut(actor_id) { - if let Some((_, common)) = instance + if let Some((_, spawn)) = instance .actors .iter_mut() .find(|actor| *actor.0 == ObjectId(actor_id)) { + let common = match spawn { + NetworkedActor::Player(npc_spawn) => &mut npc_spawn.common, + NetworkedActor::NPC(npc_spawn) => &mut npc_spawn.common, + }; common.pos = position; common.rotation = rotation; } @@ -279,11 +312,22 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i } } } - ToServer::DebugNewNpc(_from_id) => { - for (id, (handle, _)) in &mut data.clients { - let id = *id; + ToServer::DebugNewNpc(_from_id, from_actor_id) => { + let spawn; + { + let Some(instance) = data.find_actor_instance_mut(from_actor_id) else { + break; + }; - let msg = FromServer::SpawnNPC(NpcSpawn { + let Some(actor) = instance.find_actor(ObjectId(from_actor_id)) else { + break; + }; + + let NetworkedActor::Player(player) = actor else { + break; + }; + + spawn = NpcSpawn { aggression_mode: 1, common: CommonSpawn { hp_curr: 91, @@ -297,11 +341,19 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i level: 1, battalion: 4, model_chara: 297, - pos: Position::default(), + pos: player.common.pos, ..Default::default() }, ..Default::default() - }); + }; + + instance.insert_npc(ObjectId(1), spawn.clone()); + } + + for (id, (handle, _)) in &mut data.clients { + let id = *id; + + let msg = FromServer::SpawnNPC(spawn.clone()); if handle.send(msg).is_err() { to_remove.push(id);