diff --git a/src/components/chat.rs b/src/components/chat.rs index 9819b75..b9af351 100644 --- a/src/components/chat.rs +++ b/src/components/chat.rs @@ -8,7 +8,7 @@ use crate::shared_game_state::SharedGameState; struct MessageData { content: String, - fade: u8, + fade: u16, } pub struct Chat { @@ -22,7 +22,7 @@ impl Chat { } pub fn push_message(&mut self, content: String) { - self.messages.push_front(MessageData { content, fade: 150 }); + self.messages.push_front(MessageData { content, fade: 300 }); while self.messages.len() > self.max_messages { self.messages.pop_back(); @@ -47,7 +47,7 @@ impl GameEntity<()> for Chat { for message in self.messages.iter() { if message.fade > 0 { - let fade = message.fade.min(50); + let fade = message.fade.min(50) as u8; state.font.draw_colored_text_with_shadow_scaled( message.content.chars(), diff --git a/src/input/dummy_player_controller.rs b/src/input/dummy_player_controller.rs index 1cdc7ee..12d75c4 100644 --- a/src/input/dummy_player_controller.rs +++ b/src/input/dummy_player_controller.rs @@ -184,7 +184,7 @@ impl PlayerController for DummyPlayerController { fn set_state(&mut self, state: (u16, u16, u16)) { self.state = KeyState(state.0); - self.old_state = KeyState(state.1); - self.trigger = KeyState(state.2); + //self.old_state = KeyState(state.1); + //self.trigger = KeyState(state.2); } } diff --git a/src/inventory.rs b/src/inventory.rs index 6ba390d..4d656e8 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -6,11 +6,11 @@ use crate::weapon::{Weapon, WeaponLevel, WeaponType}; use crate::player::{Player, TargetPlayer}; use crate::weapon::bullet::BulletManager; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, bincode::Encode, bincode::Decode)] /// (id, amount) pub struct Item(pub u16, pub u16); -#[derive(Clone)] +#[derive(Clone, bincode::Encode, bincode::Decode)] pub struct Inventory { pub current_item: u16, pub current_weapon: u16, diff --git a/src/netplay/client.rs b/src/netplay/client.rs index aae8b20..575e295 100644 --- a/src/netplay/client.rs +++ b/src/netplay/client.rs @@ -347,10 +347,23 @@ impl Client { if let Some(npc_ref) = game_scene.npc_list.get_npc(npc.id as usize) { *npc_ref = npc; } + game_scene.npc_list.recheck(); } DRSPacket::SyncControlFlags(flags) => { state.control_flags = flags; } + DRSPacket::SyncFlags(flags) => { + for (idx, &flags) in flags.iter().enumerate() { + state.set_flag(idx * 8, flags & 0b00000001 != 0); + state.set_flag(idx * 8 + 1, flags & 0b00000010 != 0); + state.set_flag(idx * 8 + 2, flags & 0b00000100 != 0); + state.set_flag(idx * 8 + 3, flags & 0b00001000 != 0); + state.set_flag(idx * 8 + 4, flags & 0b00010000 != 0); + state.set_flag(idx * 8 + 5, flags & 0b00100000 != 0); + state.set_flag(idx * 8 + 6, flags & 0b01000000 != 0); + state.set_flag(idx * 8 + 7, flags & 0b10000000 != 0); + } + } DRSPacket::SyncPlayer(data) => { let player = if data.target == TargetPlayer::Player1 { &mut game_scene.player1 @@ -371,6 +384,13 @@ impl Client { player.air_counter = data.air_counter; player.air = data.air; } + DRSPacket::SyncInventory(target, inventory) => { + if target == TargetPlayer::Player1 { + game_scene.inventory_player1 = inventory; + } else { + game_scene.inventory_player2 = inventory; + } + } DRSPacket::Move(data) => { let player = if data.target == TargetPlayer::Player1 { &mut game_scene.player1 diff --git a/src/netplay/common.rs b/src/netplay/common.rs index 76c97af..7df827d 100644 --- a/src/netplay/common.rs +++ b/src/netplay/common.rs @@ -9,6 +9,8 @@ use crate::netplay::protocol::DRSPacket; pub fn make_socket_config() -> Config { let mut config = Config::default(); config.idle_connection_timeout = Duration::new(30, 0); + config.max_fragments = 128; + config.max_packet_size = 128 * 1024; config } diff --git a/src/netplay/protocol.rs b/src/netplay/protocol.rs index c7dd31f..5e1a62e 100644 --- a/src/netplay/protocol.rs +++ b/src/netplay/protocol.rs @@ -6,6 +6,7 @@ use crate::scripting::tsc::text_script::{ use crate::stage::Stage; use crate::{GameResult, ScriptMode}; use crate::components::number_popup::NumberPopup; +use crate::inventory::Inventory; use crate::npc::NPC; #[derive(Clone, bincode::Decode, bincode::Encode)] @@ -103,10 +104,12 @@ pub enum DRSPacket { ConnectResponse(TargetPlayer), Move(PlayerMove), SyncControlFlags(ControlFlags), + SyncFlags([u8; 1000]), SyncStageData(StageData), SyncTSCScripts(Scripts), SyncTSC(TextScriptData), SyncPlayer(PlayerData), + SyncInventory(TargetPlayer, Inventory), SyncNPC(NPC), } diff --git a/src/netplay/server.rs b/src/netplay/server.rs index 3972be3..530ac05 100644 --- a/src/netplay/server.rs +++ b/src/netplay/server.rs @@ -15,6 +15,7 @@ use crate::netplay::common::{make_socket_config, SenderExt}; use crate::netplay::protocol::{DRSPacket, HelloData, PlayerData, PlayerMove, ServerInfo, StageData, TextScriptData}; use crate::netplay::server_config::ServerConfiguration; use crate::player::TargetPlayer; +use crate::profile::GameProfile; use crate::scene::game_scene::GameScene; use crate::SharedGameState; @@ -131,10 +132,10 @@ impl Server { continue; } - let mut target = TargetPlayer::Player2; + let mut target = TargetPlayer::Player1; for (_, state) in players.iter() { - if state.target == TargetPlayer::Player2 { - target = TargetPlayer::Player1; + if state.target == TargetPlayer::Player1 { + target = TargetPlayer::Player2; } } @@ -245,6 +246,7 @@ impl Server { match msg { SyncMessage::SyncStageToPlayer(target) => { self.sync_transfer_stage(state, game_scene, Some(target)); + self.sync_flags(state, game_scene, Some(target)); self.sync_players(state, game_scene); } SyncMessage::SyncNewPlayer(target) => { @@ -281,7 +283,7 @@ impl Server { if self.tick % 300 == 50 { for npc in game_scene.npc_list.iter_alive() { - let _ = self.broadcast_packet_queue.send((DRSPacket::SyncNPC(npc.clone()), DeliveryType::Reliable)); + let _ = self.broadcast_packet_queue.send((DRSPacket::SyncNPC(npc.clone()), DeliveryType::Unreliable)); } } @@ -295,6 +297,8 @@ impl Server { for target in players { let player = if target == TargetPlayer::Player1 { &mut game_scene.player1 } else { &mut game_scene.player2 }; + let inventory = + if target == TargetPlayer::Player1 { &mut game_scene.inventory_player1 } else { &mut game_scene.inventory_player2 }; let (state, old_state, trigger) = player.controller.dump_state(); @@ -327,8 +331,11 @@ impl Server { cond: player.cond, }); + let inventory_packet = DRSPacket::SyncInventory(target, inventory.clone()); + let _ = self.broadcast_packet_queue.send((sync_packet, DeliveryType::Reliable)); let _ = self.broadcast_packet_queue.send((move_packet, DeliveryType::Reliable)); + let _ = self.broadcast_packet_queue.send((inventory_packet, DeliveryType::Reliable)); } } @@ -400,4 +407,19 @@ impl Server { self.sync_tsc(state, game_scene, target); } + + pub fn sync_flags( + &mut self, + state: &mut SharedGameState, + game_scene: &mut GameScene, + target: Option, + ) { + let flags = GameProfile::dump(state, game_scene).flags; + + if let Some(target) = target { + let _ = self.send_packet_queue.send((target, DRSPacket::SyncFlags(flags), DeliveryType::Reliable)); + } else { + let _ = self.broadcast_packet_queue.send((DRSPacket::SyncFlags(flags), DeliveryType::Reliable)); + } + } } diff --git a/src/npc/list.rs b/src/npc/list.rs index 91618e6..fd8be2c 100644 --- a/src/npc/list.rs +++ b/src/npc/list.rs @@ -145,6 +145,22 @@ impl NPCList { NPC_LIST_MAX_CAP as u16 } + pub fn recheck(&self) { + let mut max_alive = 0; + + unsafe { + for (id, npc) in (&mut *self.npcs.get()).iter_mut().enumerate() { + npc.id = id as u16; + + if npc.cond.alive() { + max_alive = npc.id; + } + } + } + + self.max_npc.replace(max_alive); + } + unsafe fn npcs<'a: 'b, 'b>(&'a self) -> &'b [NPC; NPC_LIST_MAX_CAP] { &*self.npcs.get() } diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index e7e9295..d5dbe44 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -7,7 +7,7 @@ use crossbeam_channel::tick; use log::info; use crate::caret::CaretType; -use crate::common::{Color, Direction, interpolate_fix9_scale, Rect}; +use crate::common::{interpolate_fix9_scale, Color, Direction, Rect}; use crate::components::background::Background; use crate::components::boss_life_bar::BossLifeBar; use crate::components::credits::Credits; @@ -27,32 +27,32 @@ use crate::components::water_renderer::{WaterLayer, WaterRenderer}; use crate::components::whimsical_star::WhimsicalStar; use crate::entity::GameEntity; use crate::frame::{Frame, UpdateTarget}; -use crate::framework::{filesystem, graphics}; use crate::framework::backend::SpriteBatchCommand; use crate::framework::context::Context; use crate::framework::error::GameResult; -use crate::framework::graphics::{BlendMode, draw_rect, FilterMode}; +use crate::framework::graphics::{draw_rect, BlendMode, FilterMode}; use crate::framework::ui::Components; +use crate::framework::{filesystem, graphics}; use crate::input::touch_controls::TouchControlType; use crate::inventory::{Inventory, TakeExperienceResult}; use crate::map::WaterParams; use crate::menu::pause_menu::PauseMenu; -use crate::npc::{NPC, NPCLayer}; use crate::npc::boss::BossNPC; use crate::npc::list::NPCList; -use crate::physics::{OFFSETS, PhysicalEntity}; +use crate::npc::{NPCLayer, NPC}; +use crate::physics::{PhysicalEntity, OFFSETS}; use crate::player::{ControlMode, Player, TargetPlayer}; use crate::rng::RNG; -use crate::scene::Scene; use crate::scene::title_scene::TitleScene; +use crate::scene::Scene; use crate::scripting::tsc::credit_script::CreditScriptVM; use crate::scripting::tsc::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM}; use crate::settings::ControllerType; use crate::shared_game_state::{Language, PlayerCount, ReplayState, SharedGameState, TileSize}; use crate::stage::{BackgroundType, Stage, StageTexturePaths}; use crate::texture_set::SpriteBatch; -use crate::weapon::{Weapon, WeaponType}; use crate::weapon::bullet::BulletManager; +use crate::weapon::{Weapon, WeaponType}; pub struct GameScene { pub tick: u32, @@ -1427,11 +1427,11 @@ impl GameScene { match self.frame.update_target { UpdateTarget::Player => { - if self.player2.cond.alive() + if !state.is_netplay() && (self.player2.cond.alive() && !self.player2.cond.hidden() && (self.player1.x - self.player2.x).abs() < 240 * 0x200 && (self.player1.y - self.player2.y).abs() < 200 * 0x200 - && self.player1.control_mode != ControlMode::IronHead + && self.player1.control_mode != ControlMode::IronHead) { self.frame.target_x = (self.player1.target_x * 2 + self.player2.target_x) / 3; self.frame.target_y = (self.player1.target_y * 2 + self.player2.target_y) / 3; @@ -1439,11 +1439,11 @@ impl GameScene { self.frame.target_x = self.frame.target_x.clamp(self.player1.x - 0x8000, self.player1.x + 0x8000); self.frame.target_y = self.frame.target_y.clamp(self.player1.y, self.player1.y); } else { - self.frame.target_x = self.player1.target_x; - self.frame.target_y = self.player1.target_y; + self.frame.target_x = self.local_player().target_x; + self.frame.target_y = self.local_player().target_y; } - if self.player2.cond.alive() && !self.player2.cond.hidden() { + if !state.is_netplay() && self.player2.cond.alive() && !self.player2.cond.hidden() { if self.player2.x + 0x1000 < self.frame.x || self.player2.x - 0x1000 > self.frame.x + state.canvas_size.0 as i32 * 0x200 || self.player2.y + 0x1000 < self.frame.y @@ -1633,9 +1633,11 @@ impl GameScene { impl Scene for GameScene { fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { let mut is_server = false; + let mut is_client = false; #[cfg(feature = "netplay")] { is_server = state.server.is_some(); + is_client = state.client.is_some(); } if state.mod_path.is_some() && state.replay_state == ReplayState::Recording { @@ -1656,11 +1658,13 @@ impl Scene for GameScene { self.npc_list.set_rng_seed(state.game_rng.next()); self.boss.init_rng(state.game_rng.next()); - state.textscript_vm.set_scene_script(self.stage.load_text_script( - &state.constants.base_paths, - &state.constants, - ctx, - )?); + if !is_client { + state.textscript_vm.set_scene_script(self.stage.load_text_script( + &state.constants.base_paths, + &state.constants, + ctx, + )?); + } state.textscript_vm.suspend = false; state.tile_size = self.stage.map.tile_size; #[cfg(feature = "scripting-lua")] @@ -1671,24 +1675,26 @@ impl Scene for GameScene { self.player2.controller = state.settings.create_player2_controller(); } - let npcs = self.stage.load_npcs(&state.constants.base_paths, ctx)?; - for npc_data in npcs.iter() { - log::info!("creating npc: {:?}", npc_data); + if !is_client { + let npcs = self.stage.load_npcs(&state.constants.base_paths, ctx)?; + for npc_data in npcs.iter() { + log::info!("creating npc: {:?}", npc_data); - let mut npc = NPC::create_from_data(npc_data, &state.npc_table, state.tile_size); - if npc.npc_flags.appear_when_flag_set() { - if state.get_flag(npc_data.flag_num as _) { + let mut npc = NPC::create_from_data(npc_data, &state.npc_table, state.tile_size); + if npc.npc_flags.appear_when_flag_set() { + if state.get_flag(npc_data.flag_num as _) { + npc.cond.set_alive(true); + } + } else if npc.npc_flags.hide_unless_flag_set() { + if !state.get_flag(npc_data.flag_num as _) { + npc.cond.set_alive(true); + } + } else { npc.cond.set_alive(true); } - } else if npc.npc_flags.hide_unless_flag_set() { - if !state.get_flag(npc_data.flag_num as _) { - npc.cond.set_alive(true); - } - } else { - npc.cond.set_alive(true); + + self.npc_list.spawn_at_slot(npc_data.id, npc)?; } - - self.npc_list.spawn_at_slot(npc_data.id, npc)?; } state.npc_table.stage_textures = self.stage_textures.clone(); @@ -1754,6 +1760,15 @@ impl Scene for GameScene { let chat = state.chat.clone(); chat.borrow_mut().tick(state, ())?; + if !self.pause_menu.is_paused() && state.replay_state == ReplayState::Playback { + self.replay.tick(state, (ctx, &mut self.player1))?; + } + + self.player1.controller.update(state, ctx)?; + self.player1.controller.update_trigger(); + self.player2.controller.update(state, ctx)?; + self.player2.controller.update_trigger(); + #[cfg(feature = "netplay")] { let state_ref = unsafe { &mut *(state as *mut SharedGameState) }; @@ -1764,15 +1779,6 @@ impl Scene for GameScene { } } - if !self.pause_menu.is_paused() && state.replay_state == ReplayState::Playback { - self.replay.tick(state, (ctx, &mut self.player1))?; - } - - self.player1.controller.update(state, ctx)?; - self.player1.controller.update_trigger(); - self.player2.controller.update(state, ctx)?; - self.player2.controller.update_trigger(); - state.touch_controls.control_type = if state.control_flags.control_enabled() && !self.pause_menu.is_paused() { TouchControlType::Controls } else { diff --git a/src/weapon/mod.rs b/src/weapon/mod.rs index fdc5afb..c13324d 100644 --- a/src/weapon/mod.rs +++ b/src/weapon/mod.rs @@ -19,7 +19,7 @@ mod snake; mod spur; mod super_missile_launcher; -#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive, bincode::Encode, bincode::Decode)] #[repr(u8)] pub enum WeaponType { None = 0, @@ -35,7 +35,7 @@ pub enum WeaponType { Spur = 13, } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, bincode::Encode, bincode::Decode)] #[repr(u8)] pub enum WeaponLevel { None = 0, @@ -64,7 +64,7 @@ impl WeaponLevel { } } -#[derive(Clone)] +#[derive(Clone, bincode::Encode, bincode::Decode)] pub struct Weapon { pub wtype: WeaponType, pub level: WeaponLevel,