mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-20 14:47:45 +00:00
Replicate actor spawning and movement to other clients
This now works and Kawari has achieved basic multiplayer! This is of course with a hundred caveats: * Previously spawned players are not backfilled * There is no range or zone detection * They are carbuncle clones of you, not the other character's data But it actually WORKS!
This commit is contained in:
parent
9cb7cd9abd
commit
ae1ca2f92d
8 changed files with 80 additions and 47 deletions
|
@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType};
|
||||
use kawari::common::{GameData, timestamp_secs};
|
||||
use kawari::common::{GameData, ObjectId, timestamp_secs};
|
||||
use kawari::common::{Position, determine_initial_starting_zone};
|
||||
use kawari::config::get_config;
|
||||
use kawari::lobby::CharaMake;
|
||||
|
@ -488,6 +488,9 @@ async fn client_loop(
|
|||
// wipe any exit position so it isn't accidentally reused
|
||||
exit_position = None;
|
||||
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 })).await;
|
||||
}
|
||||
ClientZoneIpcData::Unk1 {
|
||||
category, param1, ..
|
||||
|
@ -608,6 +611,8 @@ async fn client_loop(
|
|||
|
||||
connection.player_data.rotation = *rotation;
|
||||
connection.player_data.position = *position;
|
||||
|
||||
connection.handle.send(ToServer::ActorMoved(connection.id, connection.player_data.actor_id, *position)).await;
|
||||
}
|
||||
ClientZoneIpcData::LogOut { .. } => {
|
||||
tracing::info!("Recieved log out from client!");
|
||||
|
@ -768,6 +773,7 @@ async fn client_loop(
|
|||
EffectKind::Damage => {
|
||||
actor.hp -= effect.value as u32;
|
||||
}
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,26 @@ pub(crate) fn write_quantized_rotation(quantized: &f32) -> u16 {
|
|||
((quantized + pi / (2.0 * pi)) * max) as u16
|
||||
}
|
||||
|
||||
pub(crate) fn read_packed_float(packed: u16) -> f32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn write_packed_float(float: f32) -> u16 {
|
||||
(((float + 1000.0) * 100.0) * 0.32767501) as u16
|
||||
}
|
||||
|
||||
pub(crate) fn read_packed_position(packed: [u16; 3]) -> Position {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn write_packed_position(pos: &Position) -> [u16; 3] {
|
||||
[
|
||||
write_packed_float(pos.x),
|
||||
write_packed_float(pos.y),
|
||||
write_packed_float(pos.z),
|
||||
]
|
||||
}
|
||||
|
||||
/// Get the number of seconds since UNIX epoch.
|
||||
pub fn timestamp_secs() -> u32 {
|
||||
SystemTime::now()
|
||||
|
|
|
@ -154,32 +154,6 @@ impl ChatHandler {
|
|||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// move
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
unk1: 20,
|
||||
unk2: 0,
|
||||
op_code: ServerZoneIpcType::ActorMove,
|
||||
server_id: 0,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ActorMove {
|
||||
pos: Position {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
connection
|
||||
.send_segment(PacketSegment {
|
||||
source_actor: 0x106ad804,
|
||||
target_actor: connection.player_data.actor_id,
|
||||
segment_type: SegmentType::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
"!spawnnpc" => {
|
||||
// spawn another one of us
|
||||
|
|
|
@ -21,9 +21,10 @@ use super::{
|
|||
Actor, Event, Inventory, Item, LuaPlayer, StatusEffects, WorldDatabase, Zone,
|
||||
chat_handler::CUSTOMIZE_DATA,
|
||||
ipc::{
|
||||
ActorControlSelf, ActorSetPos, BattleNpcSubKind, ClientZoneIpcSegment, CommonSpawn,
|
||||
ContainerInfo, ContainerType, InitZone, ItemInfo, NpcSpawn, ObjectKind, ServerZoneIpcData,
|
||||
ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo, WeatherChange,
|
||||
ActorControlSelf, ActorMove, ActorSetPos, BattleNpcSubKind, ClientZoneIpcSegment,
|
||||
CommonSpawn, ContainerInfo, ContainerType, InitZone, ItemInfo, NpcSpawn, ObjectKind,
|
||||
ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo,
|
||||
WeatherChange,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -258,7 +259,11 @@ impl ZoneConnection {
|
|||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::ActorMove,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ActorMove { pos: position },
|
||||
data: ServerZoneIpcData::ActorMove(ActorMove {
|
||||
speed: 24,
|
||||
position,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@ use crate::common::{ObjectTypeId, read_quantized_rotation, write_quantized_rotat
|
|||
#[brw(repr = u8)]
|
||||
pub enum EffectKind {
|
||||
#[default]
|
||||
Miss = 0, // FIXME: is this correct?
|
||||
Damage = 3,
|
||||
BeginCombo = 27,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
|
@ -80,7 +82,7 @@ mod tests {
|
|||
assert_eq!(action_result.effect_count, 1);
|
||||
|
||||
// effect 0: attack
|
||||
assert_eq!(action_result.effects[0].action_type, 3);
|
||||
assert_eq!(action_result.effects[0].kind, EffectKind::Damage);
|
||||
assert_eq!(action_result.effects[0].param0, 0);
|
||||
assert_eq!(action_result.effects[0].param1, 113);
|
||||
assert_eq!(action_result.effects[0].param2, 0);
|
||||
|
@ -89,6 +91,6 @@ mod tests {
|
|||
assert_eq!(action_result.effects[0].value, 22);
|
||||
|
||||
// effect 1: start action combo
|
||||
assert_eq!(action_result.effects[1].action_type, 27);
|
||||
assert_eq!(action_result.effects[1].kind, EffectKind::BeginCombo);
|
||||
}
|
||||
}
|
||||
|
|
17
src/world/ipc/actor_move.rs
Normal file
17
src/world/ipc/actor_move.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use binrw::binrw;
|
||||
|
||||
use crate::common::{Position, read_packed_position, write_packed_position};
|
||||
|
||||
#[binrw]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ActorMove {
|
||||
pub dir: u8,
|
||||
pub dir_before_slip: u8,
|
||||
pub flag1: u8,
|
||||
pub flat2: u8,
|
||||
pub speed: u8,
|
||||
#[brw(pad_before = 1, pad_after = 4)] // empty
|
||||
#[br(map = read_packed_position)]
|
||||
#[bw(map = write_packed_position)]
|
||||
pub position: Position,
|
||||
}
|
12
src/world/ipc/actor_set_pos.rs
Normal file
12
src/world/ipc/actor_set_pos.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use binrw::binrw;
|
||||
|
||||
use crate::common::Position;
|
||||
|
||||
#[binrw]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ActorSetPos {
|
||||
pub unk: u32,
|
||||
pub layer_id: u32,
|
||||
pub position: Position,
|
||||
pub unk3: u32,
|
||||
}
|
|
@ -62,6 +62,12 @@ pub use event_start::EventStart;
|
|||
mod action_result;
|
||||
pub use action_result::{ActionEffect, ActionResult, EffectKind};
|
||||
|
||||
mod actor_move;
|
||||
pub use actor_move::ActorMove;
|
||||
|
||||
mod actor_set_pos;
|
||||
pub use actor_set_pos::ActorSetPos;
|
||||
|
||||
use crate::common::Position;
|
||||
use crate::common::read_string;
|
||||
use crate::common::write_string;
|
||||
|
@ -116,16 +122,6 @@ impl Default for ServerZoneIpcSegment {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: move to their own files
|
||||
#[binrw]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ActorSetPos {
|
||||
pub unk: u32,
|
||||
pub layer_id: u32,
|
||||
pub position: Position,
|
||||
pub unk3: u32,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(repr = u8)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
|
@ -197,10 +193,7 @@ pub enum ServerZoneIpcData {
|
|||
/// Sent by the server
|
||||
ActorControl(ActorControl),
|
||||
/// Sent by the server
|
||||
ActorMove {
|
||||
#[brw(pad_after = 4)] // empty
|
||||
pos: Position,
|
||||
},
|
||||
ActorMove(ActorMove),
|
||||
/// Sent by the server
|
||||
Unk17 { unk: [u8; 104] },
|
||||
/// Sent by the server in response to SocialListRequest
|
||||
|
@ -450,6 +443,10 @@ mod tests {
|
|||
ServerZoneIpcType::ActionResult,
|
||||
ServerZoneIpcData::ActionResult(ActionResult::default()),
|
||||
),
|
||||
(
|
||||
ServerZoneIpcType::ActorMove,
|
||||
ServerZoneIpcData::ActorMove(ActorMove::default()),
|
||||
),
|
||||
];
|
||||
|
||||
for (opcode, data) in &ipc_types {
|
||||
|
|
Loading…
Add table
Reference in a new issue