mod entities;
mod ids;
mod map;
pub(crate) mod state;
mod teams;
mod tuning;
use crate::teams::GameWorld;
use ids::{PlayerUidGenerator, SnapIdGenerator, TeamId};
use log::debug;
use std::cmp;
use std::rc::Rc;
use std::str;
use std::sync::{mpsc, Arc};
use twgame_core::console::Command;
use twgame_core::database::{DatabaseResult, DatabaseWrite};
use twgame_core::net_msg::ClNetMessage;
use twgame_core::replay::{GameValidator, ReplayerConfigure};
use twgame_core::twsnap::time::Instant;
use twgame_core::twsnap::Snap;
use twgame_core::Snapper;
use twgame_core::ThHeader;
use twgame_core::{Game, Input};
use twmap::TwMap;
use vek::Vec2;
pub use twgame_core as core;
pub use twgame_core::twsnap;
pub use crate::map::{coord, Map};
pub use crate::state::Bug;
pub(crate) struct SnapOuter<'a> {
pub id_generator: &'a mut SnapIdGenerator,
}
#[derive(Debug)]
pub struct DdnetWorld {
max_player_id: u32,
players: PlayerUidGenerator,
teams: GameWorld,
snap_id_generator: SnapIdGenerator,
}
impl DdnetWorld {
pub fn new(
map: &mut TwMap,
sender: mpsc::Sender<DatabaseWrite>,
receiver: mpsc::Receiver<DatabaseResult>,
) -> Result<DdnetWorld, String> {
let map: Map = map.try_into()?;
let map = Arc::new(map);
Self::new_with_map(map, sender, receiver)
}
pub fn new_with_map(
map: Arc<Map>,
sender: mpsc::Sender<DatabaseWrite>,
receiver: mpsc::Receiver<DatabaseResult>,
) -> Result<Self, String> {
let snap_id_generator = map.snap_id_generator.clone().finalize();
let map = Rc::new(map);
let teams = teams::GameWorld::new(map, sender, receiver);
Ok(Self {
max_player_id: 0,
players: Default::default(),
teams,
snap_id_generator,
})
}
pub fn enable_ddnet_tele_compat_mode(&mut self, enabled: bool) {
self.teams.enable_ddnet_tele_compat_mode(enabled);
}
pub fn supply_prng_compat_data(&mut self, compat_data: &str) {
self.teams.supply_prng_compat_data(compat_data);
}
pub fn enable_prng_compat_data_collection(&mut self) {
self.teams.enable_prng_compat_data_collection();
}
pub fn retrieve_prng_compat_data(&mut self) -> Option<String> {
self.teams.retrieve_prng_compat_data()
}
pub fn retrieve_bugs(&mut self) -> Vec<Bug> {
self.teams.retrieve_bugs()
}
}
impl Game for DdnetWorld {
fn player_join(&mut self, id: u32) {
let player_uid = self.players.player_join(id);
self.teams.player_join(player_uid);
self.max_player_id = cmp::max(self.max_player_id, id + 1);
}
fn player_ready(&mut self, id: u32) {
let player_uid = self.players.get(id).unwrap();
self.teams.player_ready(player_uid);
}
fn player_input(&mut self, id: u32, input: &Input) {
let player_uid = self.players.get(id).unwrap();
self.teams.player_input(player_uid, input);
}
fn player_leave(&mut self, id: u32) {
let player_uid = self.players.player_leave(id);
self.teams.player_leave(player_uid);
}
fn on_net_msg(&mut self, id: u32, msg: &ClNetMessage) {
let player_uid = self.players.get(id).unwrap();
self.teams.on_net_msg(player_uid, msg);
}
fn on_command(&mut self, id: u32, command: &Command) {
let player_uid = self.players.get(id).unwrap();
self.teams.on_command(player_uid, command);
}
fn swap_tees(&mut self, id1: u32, id2: u32) {
let player_uid1 = self.players.get(id1).unwrap();
let player_uid2 = self.players.get(id2).unwrap();
self.teams.swap_tees(player_uid1, player_uid2);
}
fn tick(&mut self, cur_time: Instant) {
let mut snap_outer = SnapOuter {
id_generator: &mut self.snap_id_generator,
};
self.teams.tick(cur_time, &mut snap_outer);
self.teams.post_tick();
}
fn is_empty(&self) -> bool {
self.teams.is_empty()
}
}
impl Snapper for DdnetWorld {
fn snap(&self, snapshot: &mut Snap) {
self.teams.snap(snapshot);
}
}
impl ReplayerConfigure for DdnetWorld {
fn on_teehistorian_header(&mut self, _raw: &[u8], header: &ThHeader) {
self.teams.configure_with_teehistorian_parameters(header);
}
}
impl GameValidator for DdnetWorld {
fn tee_pos(&self, id: u32) -> Option<Vec2<i32>> {
let player_uid = self.players.get(id)?;
self.teams.tee_pos(player_uid)
}
fn max_tee_id(&self) -> u32 {
self.max_player_id
}
fn set_tee_pos(&mut self, id: u32, pos: Option<Vec2<i32>>) {
if let Some(player_uid) = self.players.get(id) {
self.teams.set_tee_pos(player_uid, pos);
} else {
debug!("set_tee_pos called for non-existing player");
}
}
fn player_team(&self, id: u32) -> i32 {
let Some(player_uid) = self.players.get(id) else {
return 0;
};
self.teams.player_team(player_uid).to_i32()
}
fn set_player_team(&mut self, id: u32, team: i32) {
let player_uid = self
.players
.get(id)
.expect("set_player_team called for non-existing player");
self.teams
.player_team_change(player_uid, TeamId::from_i32(team));
}
}