quakeball-rust/rust/src/game_manager.rs
2026-07-18 03:19:55 +03:00

414 lines
13 KiB
Rust

use std::{collections::HashMap, net::SocketAddr};
use godot::{classes::CharacterBody3D, prelude::*, tools::get_autoload_by_name};
use serde::{Deserialize, Serialize};
use crate::{
map::Map,
map_resource::MapResource,
net::{
network_manager::{
NetworkManager,
Package::{self, SelfSync},
PeerKind,
},
util::NetVector3,
},
player::{IPlayer, NetPlayerTransform},
};
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
#[derive(GodotClass)]
#[class(base=Node, init)]
pub struct GameManager {
pub game: Option<Gd<Game>>,
base: Base<Node>,
#[export]
pub maps: Array<Gd<MapResource>>,
}
#[godot_api]
impl GameManager {
#[signal]
pub fn on_players_updated();
#[signal]
pub fn on_new_player(id: u16);
#[signal]
pub fn on_player_disconnected(id: u16);
#[signal]
pub fn on_map_changed(id: u8);
pub fn singleton() -> Gd<GameManager> {
get_autoload_by_name::<GameManager>(GAME_MANAGER_GLOBAL)
}
pub fn init_game(&mut self, is_host: bool) {
if let Some(mut game) = self.game.take() {
game.queue_free();
}
let mut game = Game::new_alloc();
game.bind_mut().is_host = is_host;
game.bind_mut().maps = self.maps.clone();
self.run_deferred(|s| {
if let Some(game) = &s.game {
game.signals()
.on_new_player()
.connect_other(s, |s, id| s.signals().on_new_player().emit(id));
game.signals()
.on_player_disconnected()
.connect_other(s, |s, id| s.signals().on_player_disconnected().emit(id));
game.signals()
.on_players_updated()
.connect_other(s, |s| s.signals().on_players_updated().emit());
game.signals()
.on_map_changed()
.connect_other(s, |s, id| s.signals().on_map_changed().emit(id));
}
});
self.base_mut().add_child(&game);
self.game = Some(game);
}
}
#[derive(GodotClass)]
#[class(base=Node, init)]
pub struct Game {
pub selected_map_idx: u8,
pub selected_map: Option<Gd<MapResource>>,
pub maps: Array<Gd<MapResource>>,
is_host: bool,
player_counter: u16,
pub self_id: Option<u16>,
pub players: Vec<Player>,
game_started: bool,
pub current_map: Option<Gd<Map>>,
since_last_sync: f64,
base: Base<Node>,
}
#[godot_api]
impl INode for Game {
fn process(&mut self, delta: f64) {
self.since_last_sync += delta;
if self.since_last_sync > (1. / 60.) {
self.since_last_sync = 0.;
if let Some(_) = self.current_map {
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
match peer {
PeerKind::Client(peer, socket_addr) => {
if let Some(player) = self
.players
.iter()
.find(|p| p.id == self.self_id.unwrap_or(0))
{
if let Some((dir, rot)) = &player.get_self_sync() {
peer.send_unreliable(
socket_addr,
SelfSync(dir.clone(), rot.clone()),
);
}
}
}
PeerKind::Server(peer, _) => {
let mut sync_data = HashMap::new();
for player in &self.players {
if let (Some(dir), Some(transform)) =
(player.get_move_dir(), player.get_transform())
{
sync_data.insert(player.id, (dir, transform));
}
}
peer.broadcast_unreliable(Package::Sync(sync_data));
}
}
}
}
}
}
}
#[godot_api]
impl Game {
#[signal]
pub fn on_players_updated();
#[signal]
pub fn on_new_player(id: u16);
#[signal]
pub fn on_player_disconnected(id: u16);
#[signal]
pub fn on_map_changed(id: u8);
pub fn singleton() -> Option<Gd<Game>> {
GameManager::singleton()
.get_child(0)
.map(|g| g.cast::<Game>())
}
pub fn change_map_selection(&mut self, map_idx: u8, emit: bool) {
self.selected_map_idx = map_idx;
if let Some(map) = self.maps.get(map_idx as usize) {
self.selected_map = Some(map);
if emit {
self.run_deferred(move |s| s.signals().on_map_changed().emit(map_idx));
}
}
}
pub fn start_game(&mut self) {
self.game_started = true;
if let Some(map) = &self.selected_map {
if let Some(scene) = &map.bind().scene {
self.base().get_tree().change_scene_to_packed(scene);
}
}
self.run_deferred(|_| {
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
if let PeerKind::Server(peer, _) = peer {
peer.broadcast_reliable(Package::GameStarted);
peer.set_accepting_connections(false);
}
}
});
}
pub fn set_self(&mut self, id: u16) {
self.self_id = Some(id);
}
pub fn new_player(&mut self, addr: SocketAddr, name: String, id: Option<u16>) -> u16 {
let id = if let Some(id) = id {
id
} else {
let id = self.player_counter;
self.player_counter += 1;
id
};
self.players.push(Player {
id,
connection_addr: addr,
name,
ping: 0,
ready: false,
character: None,
});
self.run_deferred(move |s| s.signals().on_new_player().emit(id));
id
}
pub fn set_players(&mut self, players: Vec<Player>) {
self.players = players;
for player in self.players.clone() {
self.run_deferred(move |s| s.signals().on_new_player().emit(player.id));
}
}
pub fn find_player_by_addr(&self, addr: &SocketAddr) -> Option<&Player> {
self.players.iter().find(|p| p.connection_addr == *addr)
}
pub fn find_player_by_addr_mut(&mut self, addr: &SocketAddr) -> Option<&mut Player> {
self.players.iter_mut().find(|p| p.connection_addr == *addr)
}
pub fn find_player(&self, player_id: u16) -> Option<&Player> {
self.players.iter().find(|p| p.id == player_id)
}
pub fn find_player_mut(&mut self, player_id: u16) -> Option<&mut Player> {
self.players.iter_mut().find(|p| p.id == player_id)
}
pub fn remove_player(&mut self, id: u16) {
self.players.retain(|p| p.id != id);
self.run_deferred(move |s| s.signals().on_player_disconnected().emit(id));
}
pub fn update_player_nick(&mut self, player_id: u16, nick: String) {
if let Some(player) = self.players.iter_mut().find(|p| p.id == player_id) {
player.name = nick;
}
self.run_deferred(|s| s.signals().on_players_updated().emit());
}
pub fn update_player_ping(&mut self, player_id: u16, ping: u128) {
if let Some(player) = self.players.iter_mut().find(|p| p.id == player_id) {
player.ping = ping;
}
self.run_deferred(|s| s.signals().on_players_updated().emit());
}
pub fn update_player_ready(&mut self, player_id: u16, ready: bool) {
if let Some(player) = self.players.iter_mut().find(|p| p.id == player_id) {
player.ready = ready;
}
self.run_deferred(|s| s.signals().on_players_updated().emit());
}
pub fn on_map_loaded(&mut self, map: Gd<Map>) {
self.current_map = Some(map);
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
if let PeerKind::Client(peer, to) = peer {
peer.send_reliable(to, Package::MapLoaded);
} else {
self.run_deferred(|s| {
for player in &mut s.players {
if let Some(map) = &mut s.current_map {
let character = map.bind_mut().spawn_player(
player,
player.id == s.self_id.unwrap_or(0),
None,
);
player.character = character;
}
}
});
}
}
}
pub fn spawn_player(&mut self, id: u16, transform: NetPlayerTransform) {
if let Some(map) = &mut self.current_map {
if let Some(player) = self.players.iter_mut().find(|p| p.id == id) {
let character = map.bind_mut().spawn_player(
player,
player.id == self.self_id.unwrap_or(0),
Some(transform),
);
player.character = character;
}
}
}
pub fn handle_sync(&mut self, sync_data: HashMap<u16, (NetVector3, NetPlayerTransform)>) {
for (player_id, (dir, transform)) in sync_data {
if let Some(player) = self.players.iter_mut().find(|p| p.id == player_id) {
if player.id != self.self_id.unwrap_or(0) {
// Sync remote players always
player.set_transform(transform);
player.set_move_dir(dir);
} else {
// Sync local player only if it's far enough
if let Some(local_net_transform) = player.get_transform() {
let local_location: Vector3 = local_net_transform.location.into();
let remote_location: Vector3 = transform.location.into();
if local_location.distance_squared_to(remote_location) > 2. {
player.set_transform(transform);
}
}
}
}
}
}
pub fn handle_jump(&mut self, player_id: u16) {
if player_id != self.self_id.unwrap_or(0) {
if let Some(player) = self.find_player_mut(player_id) {
player.apply_jump();
}
}
}
}
#[derive(Clone, Debug)]
pub struct Player {
pub id: u16,
pub connection_addr: SocketAddr,
pub name: String,
pub ping: u128,
pub ready: bool,
pub character: Option<DynGd<CharacterBody3D, dyn IPlayer + 'static>>,
}
impl Player {
pub fn get_transform(&self) -> Option<NetPlayerTransform> {
if let Some(character) = &self.character {
Some(character.dyn_bind().get_transform())
} else {
None
}
}
pub fn set_transform(&mut self, transform: NetPlayerTransform) {
if let Some(character) = &mut self.character {
character.dyn_bind_mut().apply_transform(transform);
}
}
pub fn get_move_dir(&self) -> Option<NetVector3> {
if let Some(character) = &self.character {
Some(character.dyn_bind().get_movement_dir().into())
} else {
None
}
}
pub fn set_move_dir(&mut self, dir: NetVector3) {
if let Some(character) = &mut self.character {
character.dyn_bind_mut().set_movement_dir(dir.into());
}
}
pub fn get_self_sync(&self) -> Option<(NetVector3, NetVector3)> {
if let Some(character) = &self.character {
Some((
character.dyn_bind().get_movement_dir().into(),
character.dyn_bind().get_transform().rotation,
))
} else {
None
}
}
pub fn apply_self_sync(&mut self, dir: NetVector3, rot: NetVector3) {
if let Some(character) = &mut self.character {
character.dyn_bind_mut().set_movement_dir(dir.into());
character.dyn_bind_mut().apply_rotation(rot.into());
}
}
pub fn apply_jump(&mut self) {
if let Some(character) = &mut self.character {
character.dyn_bind_mut().jump();
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct NetPlayer {
pub id: u16,
pub name: String,
pub ping: u128,
}
impl From<NetPlayer> for Player {
fn from(value: NetPlayer) -> Self {
Player {
id: value.id,
connection_addr: SocketAddr::from(([0, 0, 0, 0], 0)),
name: value.name,
ping: value.ping,
ready: false,
character: None,
}
}
}
impl From<Player> for NetPlayer {
fn from(value: Player) -> Self {
NetPlayer {
id: value.id,
name: value.name,
ping: value.ping,
}
}
}