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-28 00:26:31 -04:00
|
|
|
Inventory, Item, LuaPlayer, 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-28 00:22:41 -04:00
|
|
|
#[derive(Debug, Default, Clone)]
|
|
|
|
pub struct StatusEffects {
|
|
|
|
pub status_effects: Vec<StatusEffect>,
|
|
|
|
/// If the list is dirty and must be propagated to the client
|
|
|
|
pub dirty: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StatusEffects {
|
|
|
|
pub fn add(&mut self, effect_id: u16, duration: f32) {
|
|
|
|
let status_effect = self.find_or_create_status_effect(effect_id);
|
|
|
|
status_effect.duration = duration;
|
|
|
|
self.dirty = true
|
|
|
|
}
|
|
|
|
|
|
|
|
fn find_or_create_status_effect(&mut self, effect_id: u16) -> &mut StatusEffect {
|
|
|
|
if let Some(i) = self
|
|
|
|
.status_effects
|
|
|
|
.iter()
|
|
|
|
.position(|effect| effect.effect_id == effect_id)
|
|
|
|
{
|
|
|
|
&mut self.status_effects[i]
|
|
|
|
} else {
|
|
|
|
self.status_effects.push(StatusEffect {
|
|
|
|
effect_id,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
self.status_effects.last_mut().unwrap()
|
|
|
|
}
|
2025-03-27 19:24:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-28 00:22:41 -04:00
|
|
|
pub status_effects: StatusEffects,
|
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();
|
|
|
|
}
|
2025-03-28 00:22:41 -04:00
|
|
|
|
|
|
|
pub async fn process_effects_list(&mut self) {
|
|
|
|
// Only update the client if absolutely nessecary (e.g. an effect is added, removed or changed duration)
|
|
|
|
if self.status_effects.dirty {
|
|
|
|
let mut list = [StatusEffect::default(); 30];
|
|
|
|
list[..self.status_effects.status_effects.len()]
|
|
|
|
.copy_from_slice(&self.status_effects.status_effects);
|
|
|
|
|
|
|
|
let ipc = ServerZoneIpcSegment {
|
|
|
|
op_code: ServerZoneIpcType::StatusEffectList,
|
|
|
|
timestamp: timestamp_secs(),
|
|
|
|
data: ServerZoneIpcData::StatusEffectList(StatusEffectList {
|
|
|
|
statues: list,
|
|
|
|
..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;
|
|
|
|
|
|
|
|
self.status_effects.dirty = false;
|
|
|
|
}
|
|
|
|
}
|
2025-03-27 22:54:36 -04:00
|
|
|
}
|