mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-21 15:07:45 +00:00
Start sharing the Physis GameData instances
Instead of standalone functions in the common module to perform game data tasks, there is now a shared GameData struct that these functions moved to. This speeds up a few things, and can take advantage of Physis' built-in index caching. I know the current solution isn't ideal (each connection has to mutex lock to access gamedata) but it's at least better than before.
This commit is contained in:
parent
68d3b07acb
commit
37c19ee1b8
5 changed files with 251 additions and 242 deletions
|
@ -1,7 +1,7 @@
|
|||
use kawari::common::GameData;
|
||||
use kawari::common::custom_ipc::CustomIpcData;
|
||||
use kawari::common::custom_ipc::CustomIpcSegment;
|
||||
use kawari::common::custom_ipc::CustomIpcType;
|
||||
use kawari::common::get_world_name;
|
||||
use kawari::config::get_config;
|
||||
use kawari::lobby::LobbyConnection;
|
||||
use kawari::lobby::ipc::{ClientLobbyIpcData, ServerLobbyIpcSegment};
|
||||
|
@ -24,7 +24,8 @@ async fn main() {
|
|||
|
||||
tracing::info!("Server started on {addr}");
|
||||
|
||||
let world_name = get_world_name(config.world.world_id);
|
||||
let mut game_data = GameData::new();
|
||||
let world_name = game_data.get_world_name(config.world.world_id);
|
||||
|
||||
loop {
|
||||
let (socket, _) = listener.accept().await.unwrap();
|
||||
|
|
|
@ -2,10 +2,8 @@ use std::collections::HashMap;
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType};
|
||||
use kawari::common::{
|
||||
Position, determine_initial_starting_zone, get_citystate, get_primary_model_id, get_world_name,
|
||||
};
|
||||
use kawari::common::{get_racial_base_attributes, timestamp_secs};
|
||||
use kawari::common::{GameData, timestamp_secs};
|
||||
use kawari::common::{Position, determine_initial_starting_zone};
|
||||
use kawari::config::get_config;
|
||||
use kawari::lobby::CharaMake;
|
||||
use kawari::oodle::OodleNetwork;
|
||||
|
@ -28,8 +26,6 @@ use kawari::world::{
|
|||
};
|
||||
use kawari::world::{EffectsBuilder, LuaPlayer, PlayerData, StatusEffects, WorldDatabase};
|
||||
use mlua::{Function, Lua};
|
||||
use physis::common::{Language, Platform};
|
||||
use physis::gamedata::GameData;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
|
@ -52,6 +48,7 @@ async fn main() {
|
|||
|
||||
let database = Arc::new(WorldDatabase::new());
|
||||
let lua = Arc::new(Mutex::new(Lua::new()));
|
||||
let game_data = Arc::new(Mutex::new(GameData::new()));
|
||||
|
||||
{
|
||||
let lua = lua.lock().unwrap();
|
||||
|
@ -89,6 +86,7 @@ async fn main() {
|
|||
|
||||
let database = database.clone();
|
||||
let lua = lua.clone();
|
||||
let game_data = game_data.clone();
|
||||
|
||||
let state = PacketState {
|
||||
client_key: None,
|
||||
|
@ -113,7 +111,7 @@ async fn main() {
|
|||
|
||||
let mut lua_player = LuaPlayer::default();
|
||||
|
||||
let config = get_config();
|
||||
/*let config = get_config();
|
||||
|
||||
let mut game_data =
|
||||
GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
||||
|
@ -121,7 +119,7 @@ async fn main() {
|
|||
let exh = game_data.read_excel_sheet_header("Action").unwrap();
|
||||
let exd = game_data
|
||||
.read_excel_sheet("Action", &exh, Language::English, 0)
|
||||
.unwrap();
|
||||
.unwrap();*/
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut buf = [0; 2056];
|
||||
|
@ -295,9 +293,14 @@ async fn main() {
|
|||
|
||||
// Stats
|
||||
{
|
||||
let attributes = get_racial_base_attributes(
|
||||
chara_details.chara_make.customize.subrace,
|
||||
);
|
||||
let attributes;
|
||||
{
|
||||
let mut game_data = game_data.lock().unwrap();
|
||||
|
||||
attributes = game_data.get_racial_base_attributes(
|
||||
chara_details.chara_make.customize.subrace,
|
||||
);
|
||||
}
|
||||
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::PlayerStats,
|
||||
|
@ -396,122 +399,106 @@ async fn main() {
|
|||
|
||||
// send player spawn
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::PlayerSpawn,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::PlayerSpawn(PlayerSpawn {
|
||||
account_id: connection.player_data.account_id,
|
||||
content_id: connection.player_data.content_id,
|
||||
current_world_id: config.world.world_id,
|
||||
home_world_id: config.world.world_id,
|
||||
gm_rank: GameMasterRank::Debug,
|
||||
online_status: OnlineStatus::GameMasterBlue,
|
||||
common: CommonSpawn {
|
||||
class_job: connection
|
||||
.player_data
|
||||
.classjob_id,
|
||||
name: chara_details.name,
|
||||
hp_curr: connection.player_data.curr_hp,
|
||||
hp_max: connection.player_data.max_hp,
|
||||
mp_curr: connection.player_data.curr_mp,
|
||||
mp_max: connection.player_data.max_mp,
|
||||
object_kind: ObjectKind::Player(
|
||||
PlayerSubKind::Player,
|
||||
),
|
||||
look: chara_details.chara_make.customize,
|
||||
fc_tag: "LOCAL".to_string(),
|
||||
display_flags: DisplayFlag::UNK,
|
||||
models: [
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.head
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.body
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.hands
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.legs
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.feet
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.ears
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.neck
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.wrists
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.left_ring
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
get_primary_model_id(
|
||||
connection
|
||||
.inventory
|
||||
.equipped
|
||||
.right_ring
|
||||
.id,
|
||||
)
|
||||
as u32,
|
||||
],
|
||||
pos: exit_position
|
||||
.unwrap_or(Position::default()),
|
||||
rotation: exit_rotation.unwrap_or(0.0),
|
||||
..Default::default()
|
||||
},
|
||||
let ipc;
|
||||
{
|
||||
let mut game_data = game_data.lock().unwrap();
|
||||
let equipped = &connection.inventory.equipped;
|
||||
|
||||
ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::PlayerSpawn,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::PlayerSpawn(
|
||||
PlayerSpawn {
|
||||
account_id: connection
|
||||
.player_data
|
||||
.account_id,
|
||||
content_id: connection
|
||||
.player_data
|
||||
.content_id,
|
||||
current_world_id: config.world.world_id,
|
||||
home_world_id: config.world.world_id,
|
||||
gm_rank: GameMasterRank::Debug,
|
||||
online_status:
|
||||
OnlineStatus::GameMasterBlue,
|
||||
common: CommonSpawn {
|
||||
class_job: connection
|
||||
.player_data
|
||||
.classjob_id,
|
||||
name: chara_details.name,
|
||||
hp_curr: connection
|
||||
.player_data
|
||||
.curr_hp,
|
||||
hp_max: connection
|
||||
.player_data
|
||||
.max_hp,
|
||||
mp_curr: connection
|
||||
.player_data
|
||||
.curr_mp,
|
||||
mp_max: connection
|
||||
.player_data
|
||||
.max_mp,
|
||||
object_kind: ObjectKind::Player(
|
||||
PlayerSubKind::Player,
|
||||
),
|
||||
look: chara_details
|
||||
.chara_make
|
||||
.customize,
|
||||
fc_tag: "LOCAL".to_string(),
|
||||
display_flags: DisplayFlag::UNK,
|
||||
models: [
|
||||
game_data.get_primary_model_id(
|
||||
equipped.head.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.body.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.hands.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.legs.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.feet.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.ears.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.neck.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.wrists.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.left_ring.id,
|
||||
)
|
||||
as u32,
|
||||
game_data.get_primary_model_id(
|
||||
equipped.right_ring.id,
|
||||
)
|
||||
as u32,
|
||||
],
|
||||
pos: exit_position
|
||||
.unwrap_or(Position::default()),
|
||||
rotation: exit_rotation
|
||||
.unwrap_or(0.0),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
connection
|
||||
.send_segment(PacketSegment {
|
||||
|
@ -837,10 +824,10 @@ async fn main() {
|
|||
ClientZoneIpcData::ActionRequest(request) => {
|
||||
tracing::info!("Recieved action request: {:#?}!", request);
|
||||
|
||||
let action_row =
|
||||
/*let action_row =
|
||||
&exd.read_row(&exh, request.action_id).unwrap()[0];
|
||||
|
||||
println!("Found action: {:#?}", action_row);
|
||||
println!("Found action: {:#?}", action_row);*/
|
||||
|
||||
let mut effects_builder = None;
|
||||
|
||||
|
@ -858,6 +845,8 @@ async fn main() {
|
|||
.create_userdata_ref_mut(&mut lua_player)
|
||||
.unwrap();
|
||||
|
||||
let config = get_config();
|
||||
|
||||
let file_name = format!(
|
||||
"{}/{}",
|
||||
&config.world.scripts_location,
|
||||
|
@ -993,8 +982,13 @@ async fn main() {
|
|||
|
||||
let chara_make = CharaMake::from_json(chara_make_json);
|
||||
|
||||
let city_state =
|
||||
get_citystate(chara_make.classjob_id as u16);
|
||||
let city_state;
|
||||
{
|
||||
let mut game_data = game_data.lock().unwrap();
|
||||
|
||||
city_state = game_data
|
||||
.get_citystate(chara_make.classjob_id as u16);
|
||||
}
|
||||
|
||||
let (content_id, actor_id) = database.create_player_data(
|
||||
name,
|
||||
|
@ -1087,10 +1081,17 @@ async fn main() {
|
|||
CustomIpcData::RequestCharacterList { service_account_id } => {
|
||||
let config = get_config();
|
||||
|
||||
let world_name;
|
||||
{
|
||||
let mut game_data = game_data.lock().unwrap();
|
||||
world_name =
|
||||
game_data.get_world_name(config.world.world_id);
|
||||
}
|
||||
|
||||
let characters = database.get_character_list(
|
||||
*service_account_id,
|
||||
config.world.world_id,
|
||||
&get_world_name(config.world.world_id),
|
||||
&world_name,
|
||||
);
|
||||
|
||||
// send response
|
||||
|
|
111
src/common/gamedata.rs
Normal file
111
src/common/gamedata.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use physis::common::{Language, Platform};
|
||||
|
||||
use crate::{common::Attributes, config::get_config};
|
||||
|
||||
/// Convenient methods built on top of Physis to access data relevant to the server
|
||||
pub struct GameData {
|
||||
game_data: physis::gamedata::GameData,
|
||||
}
|
||||
|
||||
impl GameData {
|
||||
pub fn new() -> Self {
|
||||
let config = get_config();
|
||||
|
||||
Self {
|
||||
game_data: physis::gamedata::GameData::from_existing(
|
||||
Platform::Win32,
|
||||
&config.game_location,
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the world name from an id into the World Excel sheet.
|
||||
pub fn get_world_name(&mut self, world_id: u16) -> String {
|
||||
let exh = self.game_data.read_excel_sheet_header("World").unwrap();
|
||||
let exd = self
|
||||
.game_data
|
||||
.read_excel_sheet("World", &exh, Language::None, 0)
|
||||
.unwrap();
|
||||
|
||||
let world_row = &exd.read_row(&exh, world_id as u32).unwrap()[0];
|
||||
|
||||
let physis::exd::ColumnData::String(name) = &world_row.data[1] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
name.clone()
|
||||
}
|
||||
|
||||
/// Gets the starting city-state from a given class/job id.
|
||||
pub fn get_citystate(&mut self, classjob_id: u16) -> u8 {
|
||||
let exh = self.game_data.read_excel_sheet_header("ClassJob").unwrap();
|
||||
let exd = self
|
||||
.game_data
|
||||
.read_excel_sheet("ClassJob", &exh, Language::English, 0)
|
||||
.unwrap();
|
||||
|
||||
let world_row = &exd.read_row(&exh, classjob_id as u32).unwrap()[0];
|
||||
|
||||
let physis::exd::ColumnData::UInt8(town_id) = &world_row.data[33] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
*town_id
|
||||
}
|
||||
|
||||
pub fn get_racial_base_attributes(&mut self, tribe_id: u8) -> Attributes {
|
||||
// The Tribe Excel sheet only has deltas (e.g. 2 or -2) which are applied to a base 20 number... from somewhere
|
||||
let base_stat = 20;
|
||||
|
||||
let exh = self.game_data.read_excel_sheet_header("Tribe").unwrap();
|
||||
let exd = self
|
||||
.game_data
|
||||
.read_excel_sheet("Tribe", &exh, Language::English, 0)
|
||||
.unwrap();
|
||||
|
||||
let tribe_row = &exd.read_row(&exh, tribe_id as u32).unwrap()[0];
|
||||
|
||||
let get_column = |column_index: usize| {
|
||||
let physis::exd::ColumnData::Int8(delta) = &tribe_row.data[column_index] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
*delta
|
||||
};
|
||||
|
||||
Attributes {
|
||||
strength: (base_stat + get_column(4)) as u32,
|
||||
dexterity: (base_stat + get_column(6)) as u32,
|
||||
vitality: (base_stat + get_column(5)) as u32,
|
||||
intelligence: (base_stat + get_column(7)) as u32,
|
||||
mind: (base_stat + get_column(8)) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the primary model ID for a given item ID
|
||||
pub fn get_primary_model_id(&mut self, item_id: u32) -> u16 {
|
||||
let exh = self.game_data.read_excel_sheet_header("Item").unwrap();
|
||||
for (i, _) in exh.pages.iter().enumerate() {
|
||||
let exd = self
|
||||
.game_data
|
||||
.read_excel_sheet("Item", &exh, Language::English, i)
|
||||
.unwrap();
|
||||
|
||||
if let Some(row) = exd.read_row(&exh, item_id) {
|
||||
let item_row = &row[0];
|
||||
|
||||
let physis::exd::ColumnData::UInt64(id) = &item_row.data[47] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
return *id as u16;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: just turn this into an Option<>
|
||||
tracing::warn!("Failed to get model id for {item_id}, this is most likely a bug!");
|
||||
|
||||
0
|
||||
}
|
||||
}
|
|
@ -6,18 +6,15 @@ use std::{
|
|||
mod customize_data;
|
||||
use binrw::binrw;
|
||||
pub use customize_data::CustomizeData;
|
||||
use physis::{
|
||||
common::{Language, Platform},
|
||||
gamedata::GameData,
|
||||
};
|
||||
|
||||
use crate::config::get_config;
|
||||
|
||||
pub mod custom_ipc;
|
||||
|
||||
mod position;
|
||||
pub use position::Position;
|
||||
|
||||
mod gamedata;
|
||||
pub use gamedata::GameData;
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -100,46 +97,6 @@ pub fn timestamp_msecs() -> u64 {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
/// Gets the world name from an id into the World Excel sheet.
|
||||
pub fn get_world_name(world_id: u16) -> String {
|
||||
let config = get_config();
|
||||
|
||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
||||
|
||||
let exh = game_data.read_excel_sheet_header("World").unwrap();
|
||||
let exd = game_data
|
||||
.read_excel_sheet("World", &exh, Language::None, 0)
|
||||
.unwrap();
|
||||
|
||||
let world_row = &exd.read_row(&exh, world_id as u32).unwrap()[0];
|
||||
|
||||
let physis::exd::ColumnData::String(name) = &world_row.data[1] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
name.clone()
|
||||
}
|
||||
|
||||
/// Gets the starting city-state from a given class/job id.
|
||||
pub fn get_citystate(classjob_id: u16) -> u8 {
|
||||
let config = get_config();
|
||||
|
||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
||||
|
||||
let exh = game_data.read_excel_sheet_header("ClassJob").unwrap();
|
||||
let exd = game_data
|
||||
.read_excel_sheet("ClassJob", &exh, Language::English, 0)
|
||||
.unwrap();
|
||||
|
||||
let world_row = &exd.read_row(&exh, classjob_id as u32).unwrap()[0];
|
||||
|
||||
let physis::exd::ColumnData::UInt8(town_id) = &world_row.data[33] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
*town_id
|
||||
}
|
||||
|
||||
/// Gets the initial zone for a given city-state id
|
||||
pub fn determine_initial_starting_zone(citystate_id: u8) -> u16 {
|
||||
match citystate_id {
|
||||
|
@ -153,35 +110,6 @@ pub fn determine_initial_starting_zone(citystate_id: u8) -> u16 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets the primary model ID for a given item ID
|
||||
pub fn get_primary_model_id(item_id: u32) -> u16 {
|
||||
let config = get_config();
|
||||
|
||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
||||
|
||||
let exh = game_data.read_excel_sheet_header("Item").unwrap();
|
||||
for (i, _) in exh.pages.iter().enumerate() {
|
||||
let exd = game_data
|
||||
.read_excel_sheet("Item", &exh, Language::English, i)
|
||||
.unwrap();
|
||||
|
||||
if let Some(row) = exd.read_row(&exh, item_id) {
|
||||
let item_row = &row[0];
|
||||
|
||||
let physis::exd::ColumnData::UInt64(id) = &item_row.data[47] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
return *id as u16;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: just turn this into an Option<>
|
||||
tracing::warn!("Failed to get model id for {item_id}, this is most likely a bug!");
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
pub struct Attributes {
|
||||
pub strength: u32,
|
||||
pub dexterity: u32,
|
||||
|
@ -190,38 +118,6 @@ pub struct Attributes {
|
|||
pub mind: u32,
|
||||
}
|
||||
|
||||
pub fn get_racial_base_attributes(tribe_id: u8) -> Attributes {
|
||||
// The Tribe Excel sheet only has deltas (e.g. 2 or -2) which are applied to a base 20 number... from somewhere
|
||||
let base_stat = 20;
|
||||
|
||||
let config = get_config();
|
||||
|
||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
||||
|
||||
let exh = game_data.read_excel_sheet_header("Tribe").unwrap();
|
||||
let exd = game_data
|
||||
.read_excel_sheet("Tribe", &exh, Language::English, 0)
|
||||
.unwrap();
|
||||
|
||||
let tribe_row = &exd.read_row(&exh, tribe_id as u32).unwrap()[0];
|
||||
|
||||
let get_column = |column_index: usize| {
|
||||
let physis::exd::ColumnData::Int8(delta) = &tribe_row.data[column_index] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
*delta
|
||||
};
|
||||
|
||||
Attributes {
|
||||
strength: (base_stat + get_column(4)) as u32,
|
||||
dexterity: (base_stat + get_column(6)) as u32,
|
||||
vitality: (base_stat + get_column(5)) as u32,
|
||||
intelligence: (base_stat + get_column(7)) as u32,
|
||||
mind: (base_stat + get_column(8)) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
blowfish::Blowfish,
|
||||
common::{
|
||||
custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType},
|
||||
get_world_name, timestamp_secs,
|
||||
timestamp_secs,
|
||||
},
|
||||
config::get_config,
|
||||
lobby::CharaMake,
|
||||
|
@ -125,7 +125,7 @@ impl LobbyConnection {
|
|||
index: 0,
|
||||
flags: 0,
|
||||
icon: 0,
|
||||
name: get_world_name(config.world.world_id),
|
||||
name: self.world_name.clone(),
|
||||
}]
|
||||
.to_vec();
|
||||
// add any empty boys
|
||||
|
|
Loading…
Add table
Reference in a new issue