diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index f8026c1..fa142b1 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -18,6 +18,10 @@ use kawari::world::ipc::{ GameMasterCommandType, GameMasterRank, ObjectKind, OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType, }; +use kawari::world::{ + Actor, ClientHandle, ClientId, EffectsBuilder, FromServer, LuaPlayer, PlayerData, ServerHandle, + StatusEffects, ToServer, WorldDatabase, +}; use kawari::world::{ ChatHandler, Inventory, Zone, ZoneConnection, ipc::{ @@ -25,10 +29,6 @@ use kawari::world::{ SocialList, }, }; -use kawari::world::{ - ClientHandle, ClientId, EffectsBuilder, FromServer, LuaPlayer, PlayerData, ServerHandle, - StatusEffects, ToServer, WorldDatabase, -}; use mlua::{Function, Lua}; use std::net::SocketAddr; use tokio::io::AsyncReadExt; @@ -49,10 +49,12 @@ struct ExtraLuaState { #[derive(Default, Debug)] struct Data { clients: HashMap, + actors: Vec, } async fn main_loop(mut recv: Receiver) -> Result<(), std::io::Error> { let mut data = Data::default(); + let mut to_remove = Vec::new(); while let Some(msg) = recv.recv().await { match msg { @@ -60,9 +62,7 @@ async fn main_loop(mut recv: Receiver) -> Result<(), std::io::Error> { data.clients.insert(handle.id, handle); } ToServer::Message(from_id, msg) => { - let mut to_remove = Vec::new(); - - for (id, handle) in data.clients.iter_mut() { + for (id, handle) in &mut data.clients { let id = *id; if id == from_id { @@ -75,16 +75,44 @@ async fn main_loop(mut recv: Receiver) -> Result<(), std::io::Error> { to_remove.push(id); } } + } + ToServer::ActorSpawned(from_id, actor) => { + for (id, handle) in &mut data.clients { + let id = *id; - // Remove any clients that errored out - for id in to_remove { - data.clients.remove(&id); + if id == from_id { + continue; + } + + let msg = FromServer::ActorSpawn(actor); + + if handle.send(msg).is_err() { + to_remove.push(id); + } + } + } + ToServer::ActorMoved(from_id, actor_id, position) => { + for (id, handle) in &mut data.clients { + let id = *id; + + if id == from_id { + continue; + } + + let msg = FromServer::ActorMove(actor_id, position); + + if handle.send(msg).is_err() { + to_remove.push(id); + } } } ToServer::FatalError(err) => return Err(err), } } - + // Remove any clients that errored out + for id in to_remove { + data.clients.remove(&id); + } Ok(()) } @@ -1016,7 +1044,9 @@ async fn client_loop( } msg = internal_recv.recv() => match msg { Some(msg) => match msg { - FromServer::Message(msg) => connection.send_message(&msg).await, + FromServer::Message(msg)=>connection.send_message(&msg).await, + FromServer::ActorSpawn(actor) => connection.spawn_actor(actor).await, + FromServer::ActorMove(actor_id, position) => connection.set_actor_position(actor_id, position).await, }, None => break, } diff --git a/src/packet/parsing.rs b/src/packet/parsing.rs index b343bb3..23bcc11 100644 --- a/src/packet/parsing.rs +++ b/src/packet/parsing.rs @@ -136,7 +136,7 @@ struct Packet { fn dump(msg: &str, data: &[u8]) { write("packet.bin", data).unwrap(); - panic!("{msg} Dumped to packet.bin."); + tracing::warn!("{msg} Dumped to packet.bin."); } pub async fn send_packet( diff --git a/src/world/connection.rs b/src/world/connection.rs index 2c1ce93..e379021 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -9,7 +9,7 @@ use std::{ use tokio::{net::TcpStream, sync::mpsc::Sender, task::JoinHandle}; use crate::{ - common::{GameData, ObjectId, Position, timestamp_secs}, + common::{GameData, ObjectId, ObjectTypeId, Position, timestamp_secs}, opcodes::ServerZoneIpcType, packet::{ CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, parse_packet, @@ -19,10 +19,11 @@ use crate::{ use super::{ Actor, Event, Inventory, Item, LuaPlayer, StatusEffects, WorldDatabase, Zone, + chat_handler::CUSTOMIZE_DATA, ipc::{ - ActorControlSelf, ActorSetPos, ClientZoneIpcSegment, ContainerInfo, ContainerType, - InitZone, ItemInfo, ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, - StatusEffectList, UpdateClassInfo, WeatherChange, + ActorControlSelf, ActorSetPos, BattleNpcSubKind, ClientZoneIpcSegment, CommonSpawn, + ContainerInfo, ContainerType, InitZone, ItemInfo, NpcSpawn, ObjectKind, ServerZoneIpcData, + ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo, WeatherChange, }, }; @@ -53,6 +54,10 @@ pub struct ClientId(usize); pub enum FromServer { /// A chat message. Message(String), + /// An actor has been spawned. + ActorSpawn(Actor), + /// An actor moved to a new position. + ActorMove(u32, Position), } #[derive(Debug)] @@ -88,6 +93,8 @@ impl ClientHandle { pub enum ToServer { NewClient(ClientHandle), Message(ClientId, String), + ActorSpawned(ClientId, Actor), + ActorMoved(ClientId, u32, Position), FatalError(std::io::Error), } @@ -247,6 +254,67 @@ impl ZoneConnection { } } + pub async fn set_actor_position(&mut self, actor_id: u32, position: Position) { + let ipc = ServerZoneIpcSegment { + op_code: ServerZoneIpcType::ActorMove, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::ActorMove { pos: position }, + ..Default::default() + }; + + self.send_segment(PacketSegment { + source_actor: actor_id, + target_actor: actor_id, + segment_type: SegmentType::Ipc { data: ipc }, + }) + .await; + } + + pub async fn spawn_actor(&mut self, actor: Actor) { + let ipc = ServerZoneIpcSegment { + unk1: 20, + unk2: 0, + op_code: ServerZoneIpcType::NpcSpawn, + server_id: 0, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::NpcSpawn(NpcSpawn { + common: CommonSpawn { + hp_curr: 100, + hp_max: 100, + mp_curr: 100, + mp_max: 100, + look: CUSTOMIZE_DATA, + spawn_index: self.get_free_spawn_index(), + bnpc_base: 13498, + bnpc_name: 10261, + object_kind: ObjectKind::BattleNpc(BattleNpcSubKind::Enemy), + level: 1, + models: [ + 0, // head + 89, // body + 89, // hands + 89, // legs + 89, // feet + 0, // ears + 0, // neck + 0, // wrists + 0, // left finger + 0, // right finger + ], + ..Default::default() + }, + ..Default::default() + }), + }; + + self.send_segment(PacketSegment { + source_actor: actor.id.0, + target_actor: self.player_data.actor_id, + segment_type: SegmentType::Ipc { data: ipc }, + }) + .await; + } + pub async fn change_zone(&mut self, new_zone_id: u16) { self.zone = Some(Zone::load(new_zone_id)); self.player_data.zone_id = new_zone_id;