1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 11:47:45 +00:00

When equipping items, network that to other players

See #50
This commit is contained in:
Joshua Goins 2025-06-28 11:11:42 -04:00
parent ad4edf801d
commit 9a3652fb42
4 changed files with 83 additions and 31 deletions

View file

@ -11,7 +11,7 @@ use kawari::ipc::zone::{
ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSpawn, PlayerStatus, SocialList, ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSpawn, PlayerStatus, SocialList,
}; };
use kawari::ipc::zone::{ use kawari::ipc::zone::{
ClientTriggerCommand, ClientZoneIpcData, CommonSpawn, EventStart, GameMasterRank, OnlineStatus, ClientTriggerCommand, ClientZoneIpcData, EventStart, GameMasterRank, OnlineStatus,
ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType, ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType,
}; };
use kawari::opcodes::{ServerChatIpcType, ServerZoneIpcType}; use kawari::opcodes::{ServerChatIpcType, ServerZoneIpcType};
@ -79,7 +79,6 @@ pub fn spawn_client(connection: ZoneConnection) {
ip: *ip, ip: *ip,
channel: send, channel: send,
actor_id: 0, actor_id: 0,
common: CommonSpawn::default(),
}; };
let _ = my_send.send(handle); let _ = my_send.send(handle);
} }
@ -170,7 +169,6 @@ async fn client_loop(
let mut client_handle = client_handle.clone(); let mut client_handle = client_handle.clone();
client_handle.actor_id = actor_id; client_handle.actor_id = actor_id;
client_handle.common = connection.get_player_common_spawn(connection.exit_position, connection.exit_rotation);
// tell the server we exist, now that we confirmed we are a legitimate connection // tell the server we exist, now that we confirmed we are a legitimate connection
connection.handle.send(ToServer::NewClient(client_handle)).await; connection.handle.send(ToServer::NewClient(client_handle)).await;
@ -326,11 +324,11 @@ async fn client_loop(
.unwrap(); .unwrap();
} }
ClientZoneIpcData::FinishLoading { .. } => { ClientZoneIpcData::FinishLoading { .. } => {
// tell the server we loaded into the zone, so it can start sending us acors
connection.handle.send(ToServer::ZoneLoaded(connection.id, connection.zone.as_ref().unwrap().id)).await;
let common = connection.get_player_common_spawn(connection.exit_position, connection.exit_rotation); let common = connection.get_player_common_spawn(connection.exit_position, connection.exit_rotation);
// tell the server we loaded into the zone, so it can start sending us acors
connection.handle.send(ToServer::ZoneLoaded(connection.id, connection.zone.as_ref().unwrap().id, common.clone())).await;
let chara_details = database.find_chara_make(connection.player_data.content_id); let chara_details = database.find_chara_make(connection.player_data.content_id);
connection.send_inventory(false).await; connection.send_inventory(false).await;
@ -941,6 +939,7 @@ async fn client_loop(
FromServer::ActionComplete(request) => connection.execute_action(request, &mut lua_player).await, FromServer::ActionComplete(request) => connection.execute_action(request, &mut lua_player).await,
FromServer::ActionCancelled() => connection.cancel_action().await, FromServer::ActionCancelled() => connection.cancel_action().await,
FromServer::UpdateConfig(actor_id, config) => connection.update_config(actor_id, config).await, FromServer::UpdateConfig(actor_id, config) => connection.update_config(actor_id, config).await,
FromServer::ActorEquip(actor_id, main_weapon_id, model_ids) => connection.update_equip(actor_id, main_weapon_id, model_ids).await,
}, },
None => break, None => break,
} }

View file

@ -42,6 +42,8 @@ pub enum FromServer {
ActionCancelled(), ActionCancelled(),
/// Update an actor's equip display flags. /// Update an actor's equip display flags.
UpdateConfig(u32, Config), UpdateConfig(u32, Config),
/// Update an actor's model IDs.
ActorEquip(u32, u64, [u32; 10]),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -50,7 +52,6 @@ pub struct ClientHandle {
pub ip: SocketAddr, pub ip: SocketAddr,
pub channel: Sender<FromServer>, pub channel: Sender<FromServer>,
pub actor_id: u32, pub actor_id: u32,
pub common: CommonSpawn,
} }
impl ClientHandle { impl ClientHandle {
@ -86,7 +87,7 @@ pub enum ToServer {
ClientTrigger(ClientId, u32, ClientTrigger), ClientTrigger(ClientId, u32, ClientTrigger),
/// The connection loaded into a zone. /// The connection loaded into a zone.
// TODO: the connection should not be in charge and telling the global server what zone they just loaded in! but this will work for now // TODO: the connection should not be in charge and telling the global server what zone they just loaded in! but this will work for now
ZoneLoaded(ClientId, u16), ZoneLoaded(ClientId, u16, CommonSpawn),
/// The connection left a zone. /// The connection left a zone.
LeftZone(ClientId, u32, u16), LeftZone(ClientId, u32, u16),
/// The connection disconnected. /// The connection disconnected.
@ -103,6 +104,8 @@ pub enum ToServer {
ActionRequest(ClientId, u32, ActionRequest), ActionRequest(ClientId, u32, ActionRequest),
/// We want to update our own equip display flags. /// We want to update our own equip display flags.
Config(ClientId, u32, Config), Config(ClientId, u32, Config),
/// Tell the server what models IDs we have equipped.
Equip(ClientId, u32, u64, [u32; 10]),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -585,35 +585,48 @@ impl ZoneConnection {
// send them an appearance update // send them an appearance update
if send_appearance_update { if send_appearance_update {
let ipc; let main_weapon_id;
let model_ids;
{ {
let mut game_data = self.gamedata.lock().unwrap(); let mut game_data = self.gamedata.lock().unwrap();
let inventory = &self.player_data.inventory; let inventory = &self.player_data.inventory;
ipc = ServerZoneIpcSegment { main_weapon_id = inventory.get_main_weapon_id(&mut game_data);
op_code: ServerZoneIpcType::Equip, model_ids = inventory.get_model_ids(&mut game_data);
timestamp: timestamp_secs(),
data: ServerZoneIpcData::Equip(Equip {
main_weapon_id: inventory.get_main_weapon_id(&mut game_data),
sub_weapon_id: 0,
crest_enable: 0,
pattern_invalid: 0,
model_ids: inventory.get_model_ids(&mut game_data),
}),
..Default::default()
};
} }
self.send_segment(PacketSegment { self.handle
source_actor: self.player_data.actor_id, .send(ToServer::Equip(
target_actor: self.player_data.actor_id, self.id,
segment_type: SegmentType::Ipc, self.player_data.actor_id,
data: SegmentData::Ipc { data: ipc }, main_weapon_id,
}) model_ids,
.await; ))
.await;
} }
} }
pub async fn update_equip(&mut self, actor_id: u32, main_weapon_id: u64, model_ids: [u32; 10]) {
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::Equip,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::Equip(Equip {
main_weapon_id,
model_ids,
..Default::default()
}),
..Default::default()
};
self.send_segment(PacketSegment {
source_actor: actor_id,
target_actor: self.player_data.actor_id,
segment_type: SegmentType::Ipc,
data: SegmentData::Ipc { data: ipc },
})
.await;
}
pub async fn send_message(&mut self, message: &str) { pub async fn send_message(&mut self, message: &str) {
let ipc = ServerZoneIpcSegment { let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::ServerChatMessage, op_code: ServerZoneIpcType::ServerChatMessage,

View file

@ -62,6 +62,10 @@ impl Instance {
self.actors.get(&id) self.actors.get(&id)
} }
fn find_actor_mut(&mut self, id: ObjectId) -> Option<&mut NetworkedActor> {
self.actors.get_mut(&id)
}
fn insert_npc(&mut self, id: ObjectId, spawn: NpcSpawn) { fn insert_npc(&mut self, id: ObjectId, spawn: NpcSpawn) {
self.actors.insert(id, NetworkedActor::Npc(spawn)); self.actors.insert(id, NetworkedActor::Npc(spawn));
} }
@ -132,7 +136,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
data.clients data.clients
.insert(handle.id, (handle, ClientState::default())); .insert(handle.id, (handle, ClientState::default()));
} }
ToServer::ZoneLoaded(from_id, zone_id) => { ToServer::ZoneLoaded(from_id, zone_id, common_spawn) => {
let mut data = data.lock().unwrap(); let mut data = data.lock().unwrap();
// create a new instance if necessary // create a new instance if necessary
@ -181,7 +185,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
instance.actors.insert( instance.actors.insert(
ObjectId(client.actor_id), ObjectId(client.actor_id),
NetworkedActor::Player(NpcSpawn { NetworkedActor::Player(NpcSpawn {
common: client.common.clone(), common: common_spawn.clone(),
..Default::default() ..Default::default()
}), }),
); );
@ -208,7 +212,7 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
spawn_index: 0, spawn_index: 0,
}, },
NpcSpawn { NpcSpawn {
common: client.common.clone(), common: common_spawn.clone(),
..Default::default() ..Default::default()
}, },
); );
@ -601,6 +605,39 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
} }
} }
} }
ToServer::Equip(_from_id, from_actor_id, main_weapon_id, model_ids) => {
// update their stored state so it's correctly sent on new spawns
{
let mut data = data.lock().unwrap();
let Some(instance) = data.find_actor_instance_mut(from_actor_id) else {
break;
};
let Some(actor) = instance.find_actor_mut(ObjectId(from_actor_id)) else {
break;
};
let NetworkedActor::Player(player) = actor else {
break;
};
player.common.main_weapon_model = main_weapon_id;
player.common.models = model_ids.clone();
}
// Inform all clients about their new equipped model ids
let mut data = data.lock().unwrap();
for (id, (handle, _)) in &mut data.clients {
let id = *id;
let msg = FromServer::ActorEquip(from_actor_id, main_weapon_id, model_ids);
if handle.send(msg).is_err() {
to_remove.push(id);
}
}
}
ToServer::Disconnected(from_id) => { ToServer::Disconnected(from_id) => {
let mut data = data.lock().unwrap(); let mut data = data.lock().unwrap();