diff --git a/src/common/leveltable.rs b/src/common/leveltable.rs index 1ca6cd1..08a9222 100644 --- a/src/common/leveltable.rs +++ b/src/common/leveltable.rs @@ -1,7 +1,9 @@ -use std::collections::HashMap; use std::fs::File; use serde_json::Value; use crate::entity::character::CharacterClass; +use std::cell::LazyCell; + +pub const LEVEL_TABLE: LazyCell = LazyCell::new(|| CharacterLevelTable::default()); #[derive(Default, Copy, Clone, Debug, PartialEq, Eq)] pub struct CharacterStats { @@ -27,14 +29,14 @@ struct CharacterLevelEntry { } pub struct CharacterLevelTable { - table: HashMap, + table: Box<[[CharacterLevelEntry; 200]; 12]>, } impl Default for CharacterLevelTable { fn default() -> CharacterLevelTable { let file = File::open("data/char_stats.json").unwrap(); let json: Value = serde_json::from_reader(file).unwrap(); - let mut table = HashMap::new(); + let mut table = Box::new([[CharacterLevelEntry::default(); 200]; 12]); for it in json.as_object().unwrap().iter(){ let cl = match it.0.as_str() { @@ -67,7 +69,7 @@ impl Default for CharacterLevelTable { } } - table.insert(cl, statlist); + table[u8::from(cl) as usize] = statlist; } CharacterLevelTable { @@ -78,7 +80,7 @@ impl Default for CharacterLevelTable { impl CharacterLevelTable { pub fn get_level_from_exp(&self, ch_class: CharacterClass, exp: u32) -> u32 { - if let Some(statlist) = self.table.get(&ch_class) { + if let Some(statlist) = self.table.get(u8::from(ch_class) as usize) { statlist .iter() .filter(|stat| { @@ -92,7 +94,7 @@ impl CharacterLevelTable { } pub fn get_stats_from_exp(&self, ch_class: CharacterClass, exp: u32) -> (u32, CharacterStats) { - if let Some(statlist) = self.table.get(&ch_class) { + if let Some(statlist) = self.table.get(u8::from(ch_class) as usize) { statlist .iter() .filter(|stat| { diff --git a/src/lib.rs b/src/lib.rs index d908e0b..66c081f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ #![feature(inline_const)] #![feature(drain_filter)] #![feature(try_blocks)] +#![feature(once_cell)] extern crate fix_hidden_lifetime_bug; diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index 13c9dd5..7126aba 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -1,6 +1,5 @@ use libpso::packet::ship::*; use crate::common::serverstate::ClientId; -use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{ShipError, Clients}; use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError}; use crate::ship::packet::builder::{player_info}; @@ -11,16 +10,15 @@ pub fn join_lobby(id: ClientId, lobby: LobbyId, client_location: &ClientLocation, clients: &Clients, - item_state: &ItemState, - level_table: &CharacterLevelTable) + item_state: &ItemState) -> Result { let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?; let playerinfo = lobby_clients.iter() .map(|area_client| { let client = clients.get(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client)).unwrap(); - player_info(0x100, client, area_client, item_state, level_table) + player_info(0x100, client, area_client, item_state) }); - + let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap(); let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; let leader = client_location.get_lobby_leader(lobby).map_err(|err| -> ClientLocationError { err.into() })?; @@ -40,8 +38,7 @@ pub fn add_to_lobby(id: ClientId, lobby: LobbyId, client_location: &ClientLocation, clients: &Clients, - item_state: &ItemState, - level_table: &CharacterLevelTable) + item_state: &ItemState) -> Result { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap(); let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; @@ -55,7 +52,7 @@ pub fn add_to_lobby(id: ClientId, block: client.block as u16, event: 0, padding: 0, - playerinfo: player_info(0x100, client, &area_client, item_state, level_table), + playerinfo: player_info(0x100, client, &area_client, item_state), }) } diff --git a/src/ship/packet/builder/mod.rs b/src/ship/packet/builder/mod.rs index 80d9506..76bdf31 100644 --- a/src/ship/packet/builder/mod.rs +++ b/src/ship/packet/builder/mod.rs @@ -6,7 +6,7 @@ pub mod ship; use libpso::character::character::Inventory; use libpso::packet::ship::{PlayerHeader, PlayerInfo}; -use crate::common::leveltable::CharacterLevelTable; +use crate::common::leveltable::LEVEL_TABLE; use crate::ship::character::CharacterBytesBuilder; use crate::ship::ship::ClientState; use crate::ship::location::AreaClient; @@ -23,8 +23,8 @@ pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) - } } -pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_state: &ItemState, level_table: &CharacterLevelTable) -> PlayerInfo { - let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); +pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_state: &ItemState) -> PlayerInfo { + let (level, stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp); let inventory = item_state.get_character_inventory(&client.character).unwrap(); let character = CharacterBytesBuilder::default() .character(&client.character) diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs index 29dc0f2..7e68077 100644 --- a/src/ship/packet/builder/room.rs +++ b/src/ship/packet/builder/room.rs @@ -1,6 +1,5 @@ use libpso::packet::ship::*; use crate::common::serverstate::ClientId; -use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{ShipError, ClientState, Clients}; use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError}; use crate::ship::room::RoomState; @@ -54,7 +53,6 @@ pub fn add_to_room(_id: ClientId, area_client: &AreaClient, leader: &AreaClient, item_state: &ItemState, - level_table: &CharacterLevelTable, _room_id: RoomId, ) -> Result { @@ -68,7 +66,7 @@ pub fn add_to_room(_id: ClientId, block: 0, event: 0, padding: 0, - playerinfo: player_info(0x10000, client, area_client, item_state, level_table), + playerinfo: player_info(0x10000, client, area_client, item_state), }) } diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 1e196d5..ffa9693 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -3,7 +3,7 @@ use rand::Rng; use rand::seq::SliceRandom; use libpso::packet::ship::*; use libpso::packet::messages::*; -use crate::common::leveltable::CharacterLevelTable; +use crate::common::leveltable::LEVEL_TABLE; use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops}; use crate::ship::location::{ClientLocation, ClientLocationError}; @@ -290,7 +290,6 @@ pub async fn shop_request(id: ClientId, client_location: &ClientLocation, clients: &mut Clients, rooms: &Rooms, - level_table: &CharacterLevelTable, shops: &mut ItemShops) -> Result + Send>, anyhow::Error> { @@ -300,7 +299,7 @@ pub async fn shop_request(id: ClientId, .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_ref() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; - let level = level_table.get_level_from_exp(client.character.char_class, client.character.exp) as usize; + let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) as usize; let shop_list = match shop_request.shop_type { SHOP_OPTION_WEAPON => { client.weapon_shop = shops.weapon_shop.get_mut(&(room.mode.difficulty(), client.character.section_id)) diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index fcbff40..16483a9 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -1,6 +1,6 @@ use libpso::packet::ship::*; use crate::common::serverstate::ClientId; -use crate::common::leveltable::CharacterLevelTable; +use crate::common::leveltable::LEVEL_TABLE; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms}; use crate::ship::character::{FullCharacterBytesBuilder}; use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId}; @@ -13,13 +13,12 @@ use crate::ship::map::MapArea; pub fn block_selected(id: ClientId, pkt: &MenuSelect, clients: &mut Clients, - item_state: &ItemState, - level_table: &CharacterLevelTable) + item_state: &ItemState) -> Result, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; client.block = pkt.item as usize - 1; - let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); + let (level, stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp); let inventory = item_state.get_character_inventory(&client.character).unwrap(); let bank = item_state.get_character_bank(&client.character).unwrap(); @@ -51,12 +50,11 @@ pub fn send_player_to_lobby(id: ClientId, _pkt: &CharData, client_location: &mut ClientLocation, clients: &Clients, - item_state: &ItemState, - level_table: &CharacterLevelTable) + item_state: &ItemState) -> Result, anyhow::Error> { let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?; - let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, level_table)?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, level_table)?; + let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state)?; + let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state)?; let neighbors = client_location.get_client_neighbors(id).unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() @@ -70,7 +68,6 @@ pub async fn change_lobby(id: ClientId, client_location: &mut ClientLocation, clients: &Clients, item_state: &mut ItemState, - level_table: &CharacterLevelTable, ship_rooms: &mut Rooms, mut entity_gateway: EG) -> Result, anyhow::Error> { @@ -104,8 +101,8 @@ pub async fn change_lobby(id: ClientId, } } item_state.load_character(&mut entity_gateway, &client.character).await?; - let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, level_table)?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, level_table)?; + let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state)?; + let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state)?; let neighbors = client_location.get_client_neighbors(id).unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() @@ -145,8 +142,7 @@ pub fn get_room_tab_info(id: ClientId, let gc = cs.user.guildcard; let name = &cs.character.name; let cc = cs.character.char_class; - let leveltable = CharacterLevelTable::default(); - let lv = leveltable.get_level_from_exp(cc, cs.character.exp); + let lv = LEVEL_TABLE.get_level_from_exp(cc, cs.character.exp); let floor = cs.area.unwrap_or(MapArea::Pioneer2Ep1); room_info += format!("{} Lv{} {}\n{} {}\n", gc,lv,name,cc,floor).as_str(); diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 456ed7d..b76c46c 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -3,7 +3,7 @@ use libpso::packet::messages::*; use crate::entity::gateway::EntityGateway; use crate::entity::item::Meseta; use crate::common::serverstate::ClientId; -use crate::common::leveltable::CharacterLevelTable; +use crate::common::leveltable::LEVEL_TABLE; use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation}; use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::ClientItemId; @@ -16,8 +16,7 @@ pub async fn request_exp(id: ClientId, mut entity_gateway: EG, client_location: &ClientLocation, clients: &mut Clients, - rooms: &mut Rooms, - level_table: &CharacterLevelTable) + rooms: &mut Rooms) -> Result + Send>, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; @@ -44,13 +43,13 @@ pub async fn request_exp(id: ClientId, (c.client, SendShipPacket::Message(Message::new(GameMessage::GiveCharacterExp(gain_exp_pkt.clone())))) })); - let before_level = level_table.get_level_from_exp(client.character.char_class, client.character.exp); - let after_level = level_table.get_level_from_exp(client.character.char_class, client.character.exp + exp_gain); + let before_level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp); + let after_level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp + exp_gain); let level_up = before_level != after_level; if level_up { - let (_, before_stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); - let (after_level, after_stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp + exp_gain); + let (_, before_stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp); + let (after_level, after_stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp + exp_gain); let level_up_pkt = builder::message::character_leveled_up(area_client, after_level, before_stats, after_stats); exp_pkts = Box::new(exp_pkts.chain(clients_in_area.into_iter() diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 9d5eb57..a88203b 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -1,7 +1,7 @@ use libpso::packet::ship::*; use libpso::packet::messages::*; use crate::common::serverstate::ClientId; -use crate::common::leveltable::CharacterLevelTable; +use crate::common::leveltable::LEVEL_TABLE; use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients}; use crate::ship::location::{ClientLocation, RoomId, RoomLobby, ClientLocationError}; use crate::ship::packet::builder; @@ -14,11 +14,10 @@ pub fn create_room(id: ClientId, client_location: &mut ClientLocation, clients: &mut Clients, item_state: &mut ItemState, - level_table: &CharacterLevelTable, rooms: &mut Rooms) -> Result + Send>, anyhow::Error> { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let level = level_table.get_level_from_exp(client.character.char_class, client.character.exp); + let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp); match room::Difficulty::try_from(create_room.difficulty)? { room::Difficulty::Ultimate => { if level < 80 { @@ -81,11 +80,10 @@ pub fn join_room(id: ClientId, client_location: &mut ClientLocation, clients: &mut Clients, item_state: &mut ItemState, - level_table: &CharacterLevelTable, rooms: &mut Rooms) -> Result + Send>, ShipError> { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let level = level_table.get_level_from_exp(client.character.char_class, client.character.exp); + let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp); // let room = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))?.as_ref().unwrap(); // clippy look what you made me do if let Some(room) = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))? { @@ -124,7 +122,7 @@ pub fn join_room(id: ClientId, let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?; let join_room = builder::room::join_room(id, clients, client_location, room_id, room)?; - let add_to = builder::room::add_to_room(id, client, &area_client, &leader, item_state, level_table, room_id)?; + let add_to = builder::room::add_to_room(id, client, &area_client, &leader, item_state, room_id)?; let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap(); room.bursting = true; diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 42bd0e5..47e4529 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -15,7 +15,6 @@ use libpso::packet::ship::{BLOCK_MENU_ID, ROOM_MENU_ID}; use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; -use crate::common::leveltable::CharacterLevelTable; use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage}; use crate::login::character::SHIP_MENU_ID; @@ -437,7 +436,6 @@ impl ShipServerStateBuilder { ShipServerState { entity_gateway: self.entity_gateway.unwrap(), clients: HashMap::new(), - level_table: CharacterLevelTable::default(), name: self.name.unwrap_or_else(|| "NAMENOTSET".into()), item_state: items::state::ItemState::default(), ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)), @@ -481,7 +479,6 @@ impl Blocks { pub struct ShipServerState { entity_gateway: EG, pub clients: Clients, - level_table: CharacterLevelTable, name: String, item_state: items::state::ItemState, shops: Box, @@ -509,7 +506,7 @@ impl ShipServerState { Ok(match &msg.msg { GameMessage::RequestExp(request_exp) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::request_exp(id, request_exp, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut block.rooms, &self.level_table).await? + handler::message::request_exp(id, request_exp, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut block.rooms).await? }, GameMessage::PlayerDropItem(player_drop_item) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -592,7 +589,7 @@ impl ShipServerState { handler::direct_message::bank_interaction(id, bank_interaction, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::ShopRequest(shop_request) => { - handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await? + handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &mut self.shops).await? }, GameMessage::BuyItem(buy_item) => { handler::direct_message::buy_item(id, buy_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await? @@ -667,10 +664,10 @@ impl ServerState for ShipServerState { } BLOCK_MENU_ID => { let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten(); - let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_state, &self.level_table)?.into_iter(); + let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_state)?.into_iter(); Box::new(leave_lobby.chain(select_block)) } - ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)?, + ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)?, QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &mut block.rooms)?, _ => unreachable!(), } @@ -692,7 +689,7 @@ impl ServerState for ShipServerState { menu: room_password_req.menu, item: room_password_req.item, }; - handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)? + handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)? } else { Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter()) @@ -700,7 +697,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::CharData(chardata) => { let block = self.blocks.with_client(id, &self.clients)?; - Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state, &self.level_table)?.into_iter()) + Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state)?.into_iter()) }, RecvShipPacket::Message(msg) => { self.message(id, msg).await? @@ -714,7 +711,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::CreateRoom(create_room) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)? + handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)? }, RecvShipPacket::RoomNameRequest(_req) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -752,7 +749,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::LobbySelect(pkt) => { let block = self.blocks.with_client(id, &self.clients)?; - Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_state, &self.level_table, &mut block.rooms, self.entity_gateway.clone()).await?.into_iter()) + Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_state, &mut block.rooms, self.entity_gateway.clone()).await?.into_iter()) }, RecvShipPacket::RequestQuestList(rql) => { let block = self.blocks.with_client(id, &self.clients)?;