doukutsu-rs/src/netplay/server.rs

426 lines
18 KiB
Rust

use std::collections::HashMap;
use std::net::SocketAddr;
use std::ops::Deref;
use std::thread;
use std::thread::{sleep, JoinHandle};
use std::time::{Duration, Instant};
use crossbeam_channel::{Receiver, Sender};
use laminar::{Socket, SocketEvent};
use rand::rngs::ThreadRng;
use rand::{RngCore, SeedableRng};
use crate::framework::error::GameResult;
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;
#[repr(u8)]
#[derive(Copy, Clone, PartialEq)]
enum DeliveryType {
Reliable,
Unreliable,
}
enum SyncMessage {
SyncStageToPlayer(TargetPlayer),
SyncNewPlayer(TargetPlayer),
PlayerLeft(TargetPlayer),
}
pub struct Server {
_thread: JoinHandle<()>,
game_packet_queue: Receiver<DRSPacket>,
sync_message_queue: Receiver<SyncMessage>,
broadcast_packet_queue: Sender<(DRSPacket, DeliveryType)>,
send_packet_queue: Sender<(TargetPlayer, DRSPacket, DeliveryType)>,
tick: usize,
}
struct PlayerState {
target: TargetPlayer,
name: String,
}
impl Server {
pub fn start(config: ServerConfiguration) -> GameResult<Server> {
let mut socket = Socket::bind_with_config(config.bind_to, make_socket_config())?;
let (pq_tx, pq_rx) = crossbeam_channel::bounded(2048);
let (bq_tx, bq_rx) = crossbeam_channel::bounded::<(DRSPacket, DeliveryType)>(2048);
let (sq_tx, sq_rx) = crossbeam_channel::bounded::<(TargetPlayer, DRSPacket, DeliveryType)>(2048);
let (mq_tx, mq_rx) = crossbeam_channel::bounded::<SyncMessage>(32);
log::info!("Listening on {:?}.", socket.local_addr()?);
let thread = thread::spawn(move || {
let receiver = socket.get_event_receiver();
let sender = socket.get_packet_sender();
let mut players = HashMap::<SocketAddr, PlayerState>::new();
let mut rng = ThreadRng::default();
loop {
let mut test = [0u8; 4];
if rng.try_fill_bytes(&mut test).is_ok() {
break;
}
log::warn!("The system RNG is not ready, waiting for initialization...");
sleep(Duration::from_millis(1000));
}
loop {
socket.manual_poll(Instant::now());
sleep(Duration::from_millis(1));
while let Ok((packet, delivery)) = bq_rx.try_recv() {
match delivery {
DeliveryType::Reliable => {
sender.broadcast_reliable(players.keys().into_iter().copied(), packet);
}
DeliveryType::Unreliable => {
sender.broadcast_unreliable(players.keys().into_iter().copied(), packet);
}
}
}
while let Ok((target, packet, delivery)) = sq_rx.try_recv() {
let entry = players.iter().find(|(_, state)| state.target == target);
if let Some(entry) = entry {
match delivery {
DeliveryType::Reliable => {
sender.send_reliable(*entry.0, packet);
}
DeliveryType::Unreliable => {
sender.send_unreliable(*entry.0, packet);
}
}
}
}
while let Ok(packet) = receiver.try_recv() {
match packet {
SocketEvent::Packet(p) => {
let player = players.get(&p.addr());
if let Ok(payload) = DRSPacket::decode(p.payload()) {
match payload {
DRSPacket::KeepAlive => {
sender.send_reliable(p.addr(), DRSPacket::KeepAlive);
}
DRSPacket::ServerInfoRequest => {
sender.send_reliable(
p.addr(),
DRSPacket::ServerInfoResponse(ServerInfo {
motd: "A doukutsu-rs server".to_string(),
players: (0, 2),
}),
);
}
DRSPacket::Connect(info) => {
if player.is_some() {
sender.kick(p.addr(), "Invalid state.");
continue;
}
if players.len() == 2 {
sender.kick(p.addr(), "Too many players are connected right now.");
continue;
}
let mut target = TargetPlayer::Player1;
for (_, state) in players.iter() {
if state.target == TargetPlayer::Player1 {
target = TargetPlayer::Player2;
}
}
players.insert(p.addr(), PlayerState { target, name: info.name.clone() });
sender.send_reliable(p.addr(), DRSPacket::ConnectResponse(target));
sender.broadcast_reliable(
players.keys().into_iter().copied(),
DRSPacket::ChatMessage(format!(
"§2(§a+§2) §7Player §f{} §7joined the game.",
info.name
)),
);
let _ = mq_tx.send(SyncMessage::SyncNewPlayer(target));
let _ = mq_tx.send(SyncMessage::SyncStageToPlayer(target));
}
DRSPacket::Move(mut plr_move) => {
if let Some(player) = player {
plr_move.target = player.target;
let _ = pq_tx.send(DRSPacket::Move(plr_move));
}
}
_ => (),
}
}
}
SocketEvent::Connect(addr) => {
log::info!("Client {:?} connected.", addr);
let mut challenge = [0u8; 32];
if rng.try_fill_bytes(&mut challenge).is_err() {
log::error!("Failed to generate challenge, the RNG is not ready?");
continue;
}
sender.send_reliable(addr, DRSPacket::Hello(HelloData { challenge }))
}
SocketEvent::Timeout(addr) => {
log::info!("Client {:?} timed out.", addr);
}
SocketEvent::Disconnect(addr) => {
log::info!("Client {:?} disconnected.", addr);
let info = players.remove(&addr);
if let Some(info) = info {
sender.broadcast_reliable(
players.keys().into_iter().copied(),
DRSPacket::ChatMessage(format!(
"§4(§c-§4) §7Player §f{} §7left the game.",
info.name
)),
);
let _ = mq_tx.send(SyncMessage::PlayerLeft(info.target));
}
}
}
}
}
});
Ok(Server {
_thread: thread,
game_packet_queue: pq_rx,
sync_message_queue: mq_rx,
broadcast_packet_queue: bq_tx,
send_packet_queue: sq_tx,
tick: 0,
})
}
pub fn process(&mut self, state: &mut SharedGameState, game_scene: &mut GameScene) {
self.tick = self.tick.wrapping_add(1);
while let Ok(packet) = self.game_packet_queue.try_recv() {
match packet {
DRSPacket::Move(plr_move) => {
let player = if plr_move.target == TargetPlayer::Player1 {
&mut game_scene.player1
} else {
&mut game_scene.player2
};
player.x = plr_move.x;
player.y = plr_move.y;
player.vel_x = plr_move.vel_x;
player.vel_y = plr_move.vel_y;
player.direction = plr_move.direction;
player.cond = plr_move.cond;
player.controller.set_state((plr_move.state, plr_move.old_state, plr_move.trigger));
let other_player = if plr_move.target == TargetPlayer::Player1 {
TargetPlayer::Player2
} else {
TargetPlayer::Player1
};
let _ = self.send_packet_queue.send((
other_player,
DRSPacket::Move(plr_move),
DeliveryType::Unreliable,
));
}
_ => (),
}
}
while let Ok(msg) = self.sync_message_queue.try_recv() {
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) => {
if target == TargetPlayer::Player2 {
game_scene.add_player2(state);
}
{
let player = if target == TargetPlayer::Player1 {
&mut game_scene.player1
} else {
&mut game_scene.player2
};
player.cond.set_alive(true);
}
self.sync_players(state, game_scene);
}
SyncMessage::PlayerLeft(target) => {
{
let player = if target == TargetPlayer::Player1 {
&mut game_scene.player1
} else {
&mut game_scene.player2
};
player.cond.set_alive(false);
}
self.sync_players(state, game_scene);
}
}
}
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::Unreliable));
}
}
if self.tick % 10 == 0 {
self.sync_tsc(state, game_scene, None);
}
}
pub fn sync_players(&mut self, state: &mut SharedGameState, game_scene: &mut GameScene) {
let players = [TargetPlayer::Player1, TargetPlayer::Player2];
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();
let sync_packet = DRSPacket::SyncPlayer(PlayerData {
target,
life: player.life,
max_life: player.max_life,
control_mode: player.control_mode,
question: player.question,
popup: player.popup,
shock_counter: player.shock_counter,
xp_counter: player.xp_counter,
current_weapon: player.current_weapon,
stars: player.stars,
damage: player.damage,
air_counter: player.air_counter,
air: player.air,
});
let move_packet = DRSPacket::Move(PlayerMove {
target,
x: player.x,
y: player.y,
vel_x: player.vel_x,
vel_y: player.vel_y,
state,
old_state,
trigger,
direction: player.direction,
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));
}
}
pub fn sync_tsc(&mut self, state: &mut SharedGameState, game_scene: &mut GameScene, target: Option<TargetPlayer>) {
let tsc_packet = DRSPacket::SyncTSC(TextScriptData {
state: state.textscript_vm.state,
stack: state.textscript_vm.stack.clone(),
flags: state.textscript_vm.flags,
mode: state.textscript_vm.mode,
executor_player: state.textscript_vm.executor_player,
strict_mode: state.textscript_vm.strict_mode,
suspend: state.textscript_vm.suspend,
reset_invincibility: state.textscript_vm.reset_invincibility,
numbers: state.textscript_vm.numbers,
face: state.textscript_vm.face,
item: state.textscript_vm.item,
current_line: state.textscript_vm.current_line,
line_1: state.textscript_vm.line_1.clone(),
line_2: state.textscript_vm.line_2.clone(),
line_3: state.textscript_vm.line_3.clone(),
current_illustration: state.textscript_vm.current_illustration.clone(),
illustration_state: state.textscript_vm.illustration_state.clone(),
prev_char: state.textscript_vm.prev_char,
fade_state: state.fade_state,
prev_song: state.sound_manager.prev_song() as u32,
current_song: state.sound_manager.current_song() as u32,
});
if let Some(target) = target {
let _ = self.send_packet_queue.send((target, tsc_packet, DeliveryType::Unreliable));
} else {
let _ = self.broadcast_packet_queue.send((tsc_packet, DeliveryType::Unreliable));
}
}
pub fn sync_transfer_stage(
&mut self,
state: &mut SharedGameState,
game_scene: &mut GameScene,
target: Option<TargetPlayer>,
) {
let stage_packet = DRSPacket::SyncStageData(StageData {
stage_id: game_scene.stage_id as u32,
stage: game_scene.stage.clone(),
player_pos: (game_scene.player1.x, game_scene.player1.y),
});
let tsc_packet = DRSPacket::SyncTSCScripts(state.textscript_vm.scripts.deref().borrow().clone());
let ctrlf_packet = DRSPacket::SyncControlFlags(state.control_flags);
if let Some(target) = target {
let _ = self.send_packet_queue.send((target, stage_packet, DeliveryType::Reliable));
let _ = self.send_packet_queue.send((target, tsc_packet, DeliveryType::Reliable));
let _ = self.send_packet_queue.send((target, ctrlf_packet, DeliveryType::Reliable));
for npc in game_scene.npc_list.iter_alive() {
let _ = self.send_packet_queue.send((target, DRSPacket::SyncNPC(npc.clone()), DeliveryType::Reliable));
}
} else {
let _ = self.broadcast_packet_queue.send((stage_packet, DeliveryType::Reliable));
let _ = self.broadcast_packet_queue.send((tsc_packet, DeliveryType::Reliable));
let _ = self.broadcast_packet_queue.send((ctrlf_packet, DeliveryType::Reliable));
for npc in game_scene.npc_list.iter_alive() {
let _ = self.broadcast_packet_queue.send((DRSPacket::SyncNPC(npc.clone()), DeliveryType::Reliable));
}
}
self.sync_tsc(state, game_scene, target);
}
pub fn sync_flags(
&mut self,
state: &mut SharedGameState,
game_scene: &mut GameScene,
target: Option<TargetPlayer>,
) {
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));
}
}
}