2025-03-27 22:54:36 -04:00
|
|
|
use mlua::{UserData, UserDataFields, UserDataMethods};
|
2025-03-15 20:36:39 -04:00
|
|
|
use tokio::net::TcpStream;
|
|
|
|
|
|
|
|
use crate::{
|
2025-03-22 22:01:32 -04:00
|
|
|
common::{Position, timestamp_secs},
|
2025-03-26 18:28:51 -04:00
|
|
|
opcodes::ServerZoneIpcType,
|
2025-03-15 20:36:39 -04:00
|
|
|
packet::{
|
2025-03-16 17:43:29 -04:00
|
|
|
CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, parse_packet,
|
2025-03-15 20:36:39 -04:00
|
|
|
send_packet,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2025-03-16 17:43:29 -04:00
|
|
|
use super::{
|
2025-03-23 17:43:06 -04:00
|
|
|
Inventory, Item, Zone,
|
2025-03-16 17:43:29 -04:00
|
|
|
ipc::{
|
2025-03-23 17:43:06 -04:00
|
|
|
ActorSetPos, ClientZoneIpcSegment, ContainerInfo, ContainerType, InitZone, ItemInfo,
|
2025-03-27 23:32:36 -04:00
|
|
|
ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo,
|
|
|
|
WeatherChange,
|
2025-03-16 17:43:29 -04:00
|
|
|
},
|
|
|
|
};
|
2025-03-15 20:36:39 -04:00
|
|
|
|
2025-03-27 22:54:36 -04:00
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
2025-03-21 19:56:16 -04:00
|
|
|
pub struct PlayerData {
|
|
|
|
pub actor_id: u32,
|
|
|
|
pub content_id: u64,
|
|
|
|
pub account_id: u32,
|
|
|
|
}
|
|
|
|
|
2025-03-27 19:24:36 -04:00
|
|
|
impl UserData for PlayerData {
|
|
|
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
|
|
|
fields.add_field_method_get("content_id", |_, this| Ok(this.content_id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-17 17:02:53 -04:00
|
|
|
/// Represents a single connection between an instance of the client and the world server
|
2025-03-15 20:36:39 -04:00
|
|
|
pub struct ZoneConnection {
|
|
|
|
pub socket: TcpStream,
|
|
|
|
|
2025-03-16 17:43:29 -04:00
|
|
|
pub state: PacketState,
|
2025-03-21 19:56:16 -04:00
|
|
|
pub player_data: PlayerData,
|
2025-03-15 20:49:07 -04:00
|
|
|
|
2025-03-22 18:53:53 -04:00
|
|
|
pub zone: Option<Zone>,
|
2025-03-16 14:07:56 -04:00
|
|
|
pub spawn_index: u8,
|
2025-03-23 10:33:49 -04:00
|
|
|
|
|
|
|
pub position: Position,
|
2025-03-23 17:43:06 -04:00
|
|
|
pub inventory: Inventory,
|
2025-03-15 20:36:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ZoneConnection {
|
2025-03-16 17:43:29 -04:00
|
|
|
pub async fn parse_packet(
|
|
|
|
&mut self,
|
|
|
|
data: &[u8],
|
|
|
|
) -> (Vec<PacketSegment<ClientZoneIpcSegment>>, ConnectionType) {
|
2025-03-15 20:36:39 -04:00
|
|
|
parse_packet(data, &mut self.state).await
|
|
|
|
}
|
|
|
|
|
2025-03-16 17:43:29 -04:00
|
|
|
pub async fn send_segment(&mut self, segment: PacketSegment<ServerZoneIpcSegment>) {
|
2025-03-15 20:36:39 -04:00
|
|
|
send_packet(
|
|
|
|
&mut self.socket,
|
|
|
|
&mut self.state,
|
2025-03-18 19:49:52 -04:00
|
|
|
ConnectionType::Zone,
|
2025-03-15 20:36:39 -04:00
|
|
|
CompressionType::Oodle,
|
2025-03-18 19:49:52 -04:00
|
|
|
&[segment],
|
2025-03-15 20:36:39 -04:00
|
|
|
)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn set_player_position(&mut self, position: Position) {
|
|
|
|
// set pos
|
|
|
|
{
|
2025-03-16 17:43:29 -04:00
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::ActorSetPos,
|
2025-03-15 20:36:39 -04:00
|
|
|
timestamp: timestamp_secs(),
|
2025-03-16 17:43:29 -04:00
|
|
|
data: ServerZoneIpcData::ActorSetPos(ActorSetPos {
|
2025-03-15 20:36:39 -04:00
|
|
|
unk: 0x020fa3b8,
|
|
|
|
position,
|
|
|
|
..Default::default()
|
|
|
|
}),
|
2025-03-16 14:07:56 -04:00
|
|
|
..Default::default()
|
2025-03-15 20:36:39 -04:00
|
|
|
};
|
|
|
|
|
2025-03-18 19:49:52 -04:00
|
|
|
self.send_segment(PacketSegment {
|
2025-03-21 19:56:16 -04:00
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
2025-03-15 20:36:39 -04:00
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
2025-03-18 19:49:52 -04:00
|
|
|
})
|
2025-03-15 20:36:39 -04:00
|
|
|
.await;
|
|
|
|
}
|
|
|
|
}
|
2025-03-15 20:49:07 -04:00
|
|
|
|
|
|
|
pub async fn change_zone(&mut self, new_zone_id: u16) {
|
2025-03-22 18:53:53 -04:00
|
|
|
self.zone = Some(Zone::load(new_zone_id));
|
2025-03-15 20:49:07 -04:00
|
|
|
|
|
|
|
// Player Class Info
|
|
|
|
{
|
2025-03-16 17:43:29 -04:00
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::UpdateClassInfo,
|
2025-03-15 20:49:07 -04:00
|
|
|
timestamp: timestamp_secs(),
|
2025-03-16 17:43:29 -04:00
|
|
|
data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo {
|
2025-03-15 20:49:07 -04:00
|
|
|
class_id: 35,
|
|
|
|
unknown: 1,
|
|
|
|
synced_level: 90,
|
|
|
|
class_level: 90,
|
|
|
|
..Default::default()
|
|
|
|
}),
|
2025-03-16 14:07:56 -04:00
|
|
|
..Default::default()
|
2025-03-15 20:49:07 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
self.send_segment(PacketSegment {
|
2025-03-21 19:56:16 -04:00
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
2025-03-15 20:49:07 -04:00
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
|
|
|
// link shell information
|
|
|
|
{
|
2025-03-16 17:43:29 -04:00
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::LinkShellInformation,
|
2025-03-15 20:49:07 -04:00
|
|
|
timestamp: timestamp_secs(),
|
2025-03-16 17:43:29 -04:00
|
|
|
data: ServerZoneIpcData::LinkShellInformation { unk: [0; 456] },
|
2025-03-16 14:07:56 -04:00
|
|
|
..Default::default()
|
2025-03-15 20:49:07 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
self.send_segment(PacketSegment {
|
2025-03-21 19:56:16 -04:00
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
2025-03-15 20:49:07 -04:00
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
2025-03-16 14:07:56 -04:00
|
|
|
// TODO: send unk16?
|
2025-03-15 20:49:07 -04:00
|
|
|
|
|
|
|
// Init Zone
|
|
|
|
{
|
2025-03-16 17:43:29 -04:00
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::InitZone,
|
2025-03-15 20:49:07 -04:00
|
|
|
timestamp: timestamp_secs(),
|
2025-03-16 17:43:29 -04:00
|
|
|
data: ServerZoneIpcData::InitZone(InitZone {
|
2025-03-16 14:07:56 -04:00
|
|
|
server_id: 0,
|
2025-03-22 18:53:53 -04:00
|
|
|
zone_id: self.zone.as_ref().unwrap().id,
|
2025-03-15 20:49:07 -04:00
|
|
|
weather_id: 1,
|
|
|
|
..Default::default()
|
|
|
|
}),
|
2025-03-16 14:07:56 -04:00
|
|
|
..Default::default()
|
2025-03-15 20:49:07 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
self.send_segment(PacketSegment {
|
2025-03-21 19:56:16 -04:00
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
2025-03-15 20:49:07 -04:00
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
}
|
2025-03-16 14:07:56 -04:00
|
|
|
|
2025-03-18 23:48:00 -04:00
|
|
|
pub async fn change_weather(&mut self, new_weather_id: u16) {
|
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::WeatherChange,
|
|
|
|
timestamp: timestamp_secs(),
|
|
|
|
data: ServerZoneIpcData::WeatherChange(WeatherChange {
|
|
|
|
weather_id: new_weather_id,
|
|
|
|
transistion_time: 1.0,
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
self.send_segment(PacketSegment {
|
2025-03-21 19:56:16 -04:00
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
2025-03-18 23:48:00 -04:00
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
2025-03-16 14:07:56 -04:00
|
|
|
pub fn get_free_spawn_index(&mut self) -> u8 {
|
|
|
|
self.spawn_index += 1;
|
2025-03-16 14:09:12 -04:00
|
|
|
self.spawn_index
|
2025-03-16 14:07:56 -04:00
|
|
|
}
|
2025-03-23 17:43:06 -04:00
|
|
|
|
|
|
|
pub async fn send_inventory(&mut self) {
|
|
|
|
// item list
|
|
|
|
{
|
2025-03-23 18:14:14 -04:00
|
|
|
let equipped = self.inventory.equipped;
|
2025-03-23 17:43:06 -04:00
|
|
|
|
|
|
|
let mut send_slot = async |slot_index: u16, item: &Item| {
|
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::ItemInfo,
|
|
|
|
timestamp: timestamp_secs(),
|
|
|
|
data: ServerZoneIpcData::ItemInfo(ItemInfo {
|
|
|
|
container: ContainerType::Equipped,
|
|
|
|
slot: slot_index,
|
|
|
|
quantity: item.quantity,
|
|
|
|
catalog_id: item.id,
|
|
|
|
condition: 30000,
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
self.send_segment(PacketSegment {
|
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
};
|
|
|
|
|
|
|
|
send_slot(0, &equipped.main_hand).await;
|
|
|
|
send_slot(1, &equipped.off_hand).await;
|
|
|
|
send_slot(2, &equipped.head).await;
|
|
|
|
send_slot(3, &equipped.body).await;
|
|
|
|
send_slot(4, &equipped.hands).await;
|
|
|
|
send_slot(6, &equipped.legs).await;
|
|
|
|
send_slot(7, &equipped.feet).await;
|
|
|
|
send_slot(8, &equipped.ears).await;
|
|
|
|
send_slot(9, &equipped.neck).await;
|
|
|
|
send_slot(10, &equipped.wrists).await;
|
|
|
|
send_slot(11, &equipped.right_ring).await;
|
|
|
|
send_slot(12, &equipped.left_ring).await;
|
|
|
|
send_slot(13, &equipped.soul_crystal).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
// inform the client they have items equipped
|
|
|
|
{
|
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::ContainerInfo,
|
|
|
|
timestamp: timestamp_secs(),
|
|
|
|
data: ServerZoneIpcData::ContainerInfo(ContainerInfo {
|
|
|
|
container: ContainerType::Equipped,
|
|
|
|
num_items: self.inventory.equipped.num_items(),
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
self.send_segment(PacketSegment {
|
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
}
|
2025-03-27 22:54:36 -04:00
|
|
|
|
|
|
|
pub async fn send_message(&mut self, message: &str) {
|
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::ServerChatMessage,
|
|
|
|
timestamp: timestamp_secs(),
|
|
|
|
data: ServerZoneIpcData::ServerChatMessage {
|
|
|
|
message: message.to_string(),
|
|
|
|
unk: 0,
|
|
|
|
},
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
self.send_segment(PacketSegment {
|
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn process_lua_player(&mut self, player: &mut LuaPlayer) {
|
|
|
|
for segment in &player.queued_segments {
|
|
|
|
self.send_segment(segment.clone()).await;
|
|
|
|
}
|
|
|
|
player.queued_segments.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct LuaPlayer {
|
|
|
|
pub player_data: PlayerData,
|
|
|
|
queued_segments: Vec<PacketSegment<ServerZoneIpcSegment>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPlayer {
|
|
|
|
fn queue_segment(&mut self, segment: PacketSegment<ServerZoneIpcSegment>) {
|
|
|
|
self.queued_segments.push(segment);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send_message(&mut self, message: &str) {
|
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::ServerChatMessage,
|
|
|
|
timestamp: timestamp_secs(),
|
|
|
|
data: ServerZoneIpcData::ServerChatMessage {
|
|
|
|
message: message.to_string(),
|
|
|
|
unk: 0,
|
|
|
|
},
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
self.queue_segment(PacketSegment {
|
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
});
|
|
|
|
}
|
2025-03-27 23:32:36 -04:00
|
|
|
|
|
|
|
fn give_status_effect(&mut self, effect_id: u16, duration: f32) {
|
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::StatusEffectList,
|
|
|
|
timestamp: timestamp_secs(),
|
|
|
|
data: ServerZoneIpcData::StatusEffectList(StatusEffectList {
|
|
|
|
statues: [StatusEffect {
|
|
|
|
effect_id,
|
|
|
|
param: 0,
|
|
|
|
duration,
|
|
|
|
source_actor_id: self.player_data.actor_id,
|
|
|
|
}; 30],
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
self.queue_segment(PacketSegment {
|
|
|
|
source_actor: self.player_data.actor_id,
|
|
|
|
target_actor: self.player_data.actor_id,
|
|
|
|
segment_type: SegmentType::Ipc { data: ipc },
|
|
|
|
});
|
|
|
|
}
|
2025-03-27 22:54:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UserData for LuaPlayer {
|
|
|
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
|
|
|
methods.add_method_mut("send_message", |_, this, message: String| {
|
|
|
|
this.send_message(&message);
|
|
|
|
Ok(())
|
|
|
|
});
|
2025-03-27 23:32:36 -04:00
|
|
|
methods.add_method_mut(
|
|
|
|
"give_status_effect",
|
|
|
|
|_, this, (effect_id, duration): (u16, f32)| {
|
|
|
|
this.give_status_effect(effect_id, duration);
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
);
|
2025-03-27 22:54:36 -04:00
|
|
|
}
|
2025-03-15 20:36:39 -04:00
|
|
|
}
|