1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-22 15:27:44 +00:00

Improve how the status effect list is sent and created

There is now a dedicated StatusEffects struct that provides a nicer API
to work with these. The list is also sent when needed and only when a
status effect changes, as the client handles the ticking down of
durations itself.
This commit is contained in:
Joshua Goins 2025-03-28 00:22:41 -04:00
parent 13b24e9cfe
commit bbbe426617
3 changed files with 76 additions and 29 deletions

View file

@ -14,7 +14,6 @@ use kawari::packet::{
use kawari::world::ipc::{
ClientZoneIpcData, CommonSpawn, DisplayFlag, GameMasterCommandType, GameMasterRank, ObjectKind,
OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType,
StatusEffect,
};
use kawari::world::{
ChatHandler, Inventory, Zone, ZoneConnection,
@ -23,8 +22,8 @@ use kawari::world::{
SocialList,
},
};
use kawari::world::{LuaPlayer, PlayerData, WorldDatabase};
use mlua::{AnyUserData, Function, Lua};
use kawari::world::{LuaPlayer, PlayerData, StatusEffects, WorldDatabase};
use mlua::{Function, Lua};
use physis::common::{Language, Platform};
use physis::gamedata::GameData;
use tokio::io::AsyncReadExt;
@ -76,6 +75,7 @@ async fn main() {
zone: None,
position: Position::default(),
inventory: Inventory::new(),
status_effects: StatusEffects::default(),
};
let mut lua_player = LuaPlayer::default();
@ -99,7 +99,6 @@ async fn main() {
// collect actor data
connection.player_data =
database.find_player_data(actor_id.parse::<u32>().unwrap());
lua_player.player_data = connection.player_data;
// We have send THEM a keep alive
{
@ -942,7 +941,19 @@ async fn main() {
}
}
// copy from lua player state, as they modify the status effects list
// TODO: i dunno?
connection.status_effects = lua_player.status_effects.clone();
// Process any queued packets from scripts and whatnot
connection.process_lua_player(&mut lua_player).await;
// check if status effects need sending
connection.process_effects_list().await;
// update lua player
lua_player.player_data = connection.player_data;
lua_player.status_effects = connection.status_effects.clone();
}
}
});

View file

@ -1,4 +1,4 @@
use mlua::{UserData, UserDataFields, UserDataMethods};
use mlua::{UserData, UserDataMethods};
use tokio::net::TcpStream;
use crate::{
@ -26,9 +26,34 @@ pub struct PlayerData {
pub account_id: u32,
}
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));
#[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()
}
}
}
@ -44,6 +69,7 @@ pub struct ZoneConnection {
pub position: Position,
pub inventory: Inventory,
pub status_effects: StatusEffects,
}
impl ZoneConnection {
@ -270,11 +296,40 @@ impl ZoneConnection {
}
player.queued_segments.clear();
}
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;
}
}
}
#[derive(Default)]
pub struct LuaPlayer {
pub player_data: PlayerData,
pub status_effects: StatusEffects,
queued_segments: Vec<PacketSegment<ServerZoneIpcSegment>>,
}
@ -302,26 +357,7 @@ impl LuaPlayer {
}
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 },
});
self.status_effects.add(effect_id, duration);
}
}

View file

@ -7,7 +7,7 @@ mod chat_handler;
pub use chat_handler::ChatHandler;
mod connection;
pub use connection::{LuaPlayer, PlayerData, ZoneConnection};
pub use connection::{LuaPlayer, PlayerData, StatusEffects, ZoneConnection};
mod database;
pub use database::{CharacterData, WorldDatabase};