414 lines
13 KiB
Rust
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,
|
|
}
|
|
}
|
|
}
|