856 lines
28 KiB
Rust
856 lines
28 KiB
Rust
use std::{collections::HashMap, net::SocketAddr};
|
|
|
|
use godot::{classes::CharacterBody3D, prelude::*, tools::get_autoload_by_name};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
ball::Ball,
|
|
map::Map,
|
|
map_resource::MapResource,
|
|
net::{
|
|
network_manager::{
|
|
BallSync, NetworkManager,
|
|
Package::{self, Kill, SelfSync, SetTeam, SpawnPlayer, TakeDamage},
|
|
PeerKind, SyncPackage,
|
|
},
|
|
util::NetVector3,
|
|
},
|
|
player::{DamageSource, IPlayer, LocalPlayer, NetTransform, RemotePlayer},
|
|
team_resource::TeamResource,
|
|
weapon::WeaponType,
|
|
};
|
|
|
|
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
|
|
|
#[derive(GodotClass)]
|
|
#[class(base=Node, init)]
|
|
pub struct GameManager {
|
|
#[export]
|
|
pub respawn_timer: f64,
|
|
|
|
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();
|
|
game.bind_mut().respawn_timer = self.respawn_timer;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
pub struct GameOptions {
|
|
pub values: HashMap<GameOption, GameOptionValue>,
|
|
}
|
|
|
|
impl Default for GameOptions {
|
|
fn default() -> Self {
|
|
let mut opts = HashMap::new();
|
|
opts.insert(GameOption::AllowAnyMap, GameOptionValue::Boolean(false));
|
|
opts.insert(GameOption::AllowAnyTeam, GameOptionValue::Boolean(false));
|
|
Self { values: opts }
|
|
}
|
|
}
|
|
|
|
impl GameOptions {
|
|
pub fn is_true(&self, opt: GameOption) -> bool {
|
|
let opt = self
|
|
.values
|
|
.get(&opt)
|
|
.copied()
|
|
.unwrap_or(GameOptionValue::Boolean(false));
|
|
match opt {
|
|
GameOptionValue::Boolean(value) => value,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Copy, Clone, Serialize, Deserialize)]
|
|
pub enum GameOption {
|
|
/// Allow anyone to select a map, not just the host
|
|
AllowAnyMap,
|
|
/// Allow anyone to select anyone's team, not just themselves and the host
|
|
AllowAnyTeam,
|
|
}
|
|
|
|
impl ToString for GameOption {
|
|
fn to_string(&self) -> String {
|
|
match self {
|
|
GameOption::AllowAnyMap => "Allow anyone to select the map",
|
|
GameOption::AllowAnyTeam => "Allow anyone to select any teams",
|
|
}
|
|
.to_owned()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
|
pub enum GameOptionValue {
|
|
Boolean(bool),
|
|
}
|
|
|
|
#[derive(GodotClass)]
|
|
#[class(base=Node, init)]
|
|
pub struct Game {
|
|
#[export]
|
|
pub respawn_timer: f64,
|
|
|
|
pub selected_map_idx: u8,
|
|
pub selected_map: Option<Gd<MapResource>>,
|
|
pub maps: Array<Gd<MapResource>>,
|
|
|
|
pub opts: GameOptions,
|
|
|
|
is_host: bool,
|
|
player_counter: u16,
|
|
pub self_id: Option<u16>,
|
|
pub players: Vec<Player>,
|
|
game_started: bool,
|
|
|
|
pub current_map: Option<Gd<Map>>,
|
|
pub world_ball: Option<Gd<Ball>>,
|
|
since_last_sync: f64,
|
|
|
|
base: Base<Node>,
|
|
}
|
|
|
|
#[godot_api]
|
|
impl INode for Game {
|
|
fn physics_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(data) = &player.get_self_sync() {
|
|
peer.send_unreliable(socket_addr, SelfSync(data.clone()));
|
|
}
|
|
}
|
|
}
|
|
PeerKind::Server(peer, _) => {
|
|
let mut sync_data = HashMap::new();
|
|
for player in &self.players {
|
|
if let Some(character) = &player.character {
|
|
if character.dyn_bind().is_dead() {
|
|
continue;
|
|
}
|
|
}
|
|
if let (Some(dir), Some(look_up), Some(transform)) = (
|
|
player.get_move_dir(),
|
|
player.get_look_up(),
|
|
player.get_transform(),
|
|
) {
|
|
sync_data.insert(
|
|
player.id(),
|
|
SyncPackage {
|
|
movement_direction: dir,
|
|
look_up,
|
|
transform,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
peer.broadcast_unreliable(Package::Sync(
|
|
sync_data,
|
|
self.world_ball.as_ref().map(|b| b.bind().get_sync()),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
if let PeerKind::Server(peer, _) = peer {
|
|
for player in self.players.clone() {
|
|
let player_id = player.id();
|
|
if let Some(character) = player.character {
|
|
if character.dyn_bind().dead_for() > self.respawn_timer {
|
|
let character = self.spawn_player_server(player_id);
|
|
|
|
if let Some(character) = character {
|
|
peer.broadcast_reliable(SpawnPlayer(
|
|
player_id,
|
|
character.dyn_bind().get_transform(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[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_player_team_changed(id: u16, team: u8);
|
|
#[signal]
|
|
pub fn on_map_changed(id: u8);
|
|
#[signal]
|
|
pub fn options_changed();
|
|
|
|
pub fn singleton() -> Option<Gd<Game>> {
|
|
GameManager::singleton()
|
|
.get_child(0)
|
|
.map(|g| g.cast::<Game>())
|
|
}
|
|
|
|
pub fn change_option(&mut self, opt: GameOption, value: GameOptionValue) {
|
|
self.opts.values.insert(opt, value);
|
|
self.run_deferred(move |s| {
|
|
s.signals().options_changed().emit();
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
if let PeerKind::Server(peer, _) = peer {
|
|
peer.broadcast_reliable(Package::GameOptionChanged(opt, value));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
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);
|
|
|
|
let teams_max = (self.get_teams().len() - 1) as u8;
|
|
self.run_deferred(move |s| {
|
|
if NetworkManager::singleton().bind().is_host() {
|
|
for player in s.players.clone() {
|
|
s.run_deferred(move |s| {
|
|
s.try_set_player_team(
|
|
player.id(),
|
|
player.data.team.clamp(0, teams_max),
|
|
true,
|
|
)
|
|
});
|
|
}
|
|
}
|
|
});
|
|
if emit {
|
|
self.run_deferred(move |s| s.signals().on_map_changed().emit(map_idx));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_teams(&self) -> Array<Gd<TeamResource>> {
|
|
if let Some(map) = &self.selected_map {
|
|
map.bind().teams.clone()
|
|
} else {
|
|
Array::new()
|
|
}
|
|
}
|
|
|
|
pub fn get_team(&self, team: u8) -> Option<Gd<TeamResource>> {
|
|
self.get_teams().get(team as usize)
|
|
}
|
|
|
|
pub fn try_set_player_team(&mut self, player_id: u16, team: u8, emit: bool) {
|
|
if let Some(player) = self.find_player_mut(player_id) {
|
|
player.data.team = team;
|
|
self.run_deferred(move |_| {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
match peer {
|
|
PeerKind::Client(peer, socket_addr) => {
|
|
peer.send_reliable(socket_addr, SetTeam(player_id, team));
|
|
}
|
|
PeerKind::Server(peer, _) => {
|
|
peer.broadcast_reliable(SetTeam(player_id, team));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if emit {
|
|
self.run_deferred(move |s| {
|
|
s.signals().on_player_team_changed().emit(player_id, team)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn handle_player_team_change(&mut self, player_id: u16, team: u8) {
|
|
if let Some(player) = self.find_player_mut(player_id) {
|
|
player.data.team = team;
|
|
self.run_deferred(move |s| s.signals().on_player_team_changed().emit(player_id, team));
|
|
}
|
|
}
|
|
|
|
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: Option<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 {
|
|
connection_addr: addr,
|
|
data: NetPlayer {
|
|
id,
|
|
name: name.unwrap_or(format!("Player {}", id)),
|
|
ping: 0,
|
|
team: 0,
|
|
},
|
|
ready: false,
|
|
character: None,
|
|
});
|
|
self.run_deferred(move |s| s.signals().on_new_player().emit(id));
|
|
|
|
let team_with_least = self
|
|
.get_teams()
|
|
.iter_shared()
|
|
.enumerate()
|
|
.min_by(|(team_idx_a, _), (team_idx_b, _)| {
|
|
let a = self
|
|
.players
|
|
.iter()
|
|
.filter(|p| p.data.team == *team_idx_a as u8)
|
|
.count();
|
|
let b = self
|
|
.players
|
|
.iter()
|
|
.filter(|p| p.data.team == *team_idx_b as u8)
|
|
.count();
|
|
a.cmp(&b)
|
|
})
|
|
.map(|(team_idx, _)| team_idx)
|
|
.unwrap_or(0);
|
|
|
|
self.try_set_player_team(id, team_with_least as u8, true);
|
|
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.data.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.data.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 s.players.clone() {
|
|
s.spawn_player_server(player.id());
|
|
}
|
|
s.spawn_ball(None, None, None);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn spawn_ball(
|
|
&mut self,
|
|
transform: Option<NetTransform>,
|
|
velocity: Option<NetVector3>,
|
|
player_id: Option<u16>,
|
|
) {
|
|
if let Some(map) = &mut self.current_map {
|
|
let ball = map.bind_mut().spawn_ball(transform, velocity, player_id);
|
|
if let Some(ball) = &ball {
|
|
self.world_ball = Some(ball.clone());
|
|
ball.signals()
|
|
.on_ball_pickup()
|
|
.connect_other(self, |s, player_id| s.handle_ball_pickup(player_id));
|
|
}
|
|
self.run_deferred(move |_| {
|
|
if let Some(ball) = ball
|
|
&& let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
|
|
&& let PeerKind::Server(peer, _) = peer
|
|
{
|
|
peer.broadcast_reliable(Package::SpawnBall(
|
|
ball.bind().get_transform(),
|
|
ball.bind().get_velocity(),
|
|
player_id,
|
|
));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn despawn_ball(&mut self) {
|
|
if let Some(mut ball) = self.world_ball.take() {
|
|
ball.queue_free();
|
|
self.run_deferred(|_| {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
|
|
&& let PeerKind::Server(peer, _) = peer
|
|
{
|
|
peer.broadcast_reliable(Package::DespawnBall);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn respawn_ball(&mut self) {
|
|
self.despawn_ball();
|
|
self.spawn_ball(None, None, None);
|
|
}
|
|
|
|
pub fn handle_ball_pickup(&mut self, player_id: u16) {
|
|
if NetworkManager::singleton().bind_mut().is_host() {
|
|
self.despawn_ball();
|
|
self.run_deferred(move |s| s.handle_swap_weapon(player_id, WeaponType::Ball, true));
|
|
}
|
|
}
|
|
|
|
// Handle a swap-weapon command package
|
|
pub fn handle_swap_weapon(
|
|
&mut self,
|
|
player_id: u16,
|
|
weapon_type: WeaponType,
|
|
allow_ball: bool,
|
|
) {
|
|
if weapon_type == WeaponType::Ball && !allow_ball {
|
|
return;
|
|
}
|
|
|
|
if let Some(player) = self.find_player_mut(player_id)
|
|
&& let Some(character) = &mut player.character
|
|
{
|
|
character.dyn_bind_mut().swap_weapon(weapon_type);
|
|
let player_id = player.id();
|
|
self.run_deferred(move |_| {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer
|
|
&& let PeerKind::Server(peer, _) = peer
|
|
{
|
|
peer.broadcast_reliable(Package::SwapWeapon(player_id, weapon_type));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn spawn_player_server(&mut self, id: u16) -> Option<DynGd<CharacterBody3D, dyn IPlayer>> {
|
|
if let Some(player) = self.find_player_mut(id)
|
|
&& let Some(character) = &mut player.character
|
|
{
|
|
// This is a respawn, remove old character
|
|
character.queue_free();
|
|
}
|
|
|
|
if let Some(mut map) = self.current_map.clone() {
|
|
let mut character =
|
|
map.bind_mut()
|
|
.spawn_player(id, id == self.self_id.unwrap_or(0), None);
|
|
|
|
if let Some(character) = &mut character {
|
|
if let Ok(mut local_player) = character.clone().try_cast::<LocalPlayer>() {
|
|
local_player.bind_mut().signals().on_die().connect_other(
|
|
self,
|
|
move |s, signal, spid| {
|
|
s.on_death(DamageSource::from_signal(signal, spid), id)
|
|
},
|
|
);
|
|
local_player
|
|
.bind_mut()
|
|
.signals()
|
|
.on_take_damage()
|
|
.connect_other(self, move |s, signal, spid, damage| {
|
|
s.on_take_damage(DamageSource::from_signal(signal, spid), id, damage)
|
|
});
|
|
} else if let Ok(mut remote_player) = character.clone().try_cast::<RemotePlayer>() {
|
|
remote_player.bind_mut().signals().on_die().connect_other(
|
|
self,
|
|
move |s, signal, spid| {
|
|
s.on_death(DamageSource::from_signal(signal, spid), id)
|
|
},
|
|
);
|
|
remote_player
|
|
.bind_mut()
|
|
.signals()
|
|
.on_take_damage()
|
|
.connect_other(self, move |s, signal, spid, damage| {
|
|
s.on_take_damage(DamageSource::from_signal(signal, spid), id, damage)
|
|
});
|
|
}
|
|
}
|
|
|
|
if let Some(player) = self.find_player_mut(id) {
|
|
player.character = character.clone();
|
|
}
|
|
character
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
// Method called on the client-side whenever a player has spawned
|
|
pub fn spawn_player_client(&mut self, id: u16, transform: NetTransform) {
|
|
if let Some(map) = &mut self.current_map {
|
|
if let Some(player) = self.players.iter_mut().find(|p| p.id() == id) {
|
|
if let Some(character) = &mut player.character {
|
|
// This is a respawn, delete old character
|
|
character.queue_free();
|
|
}
|
|
|
|
let character = map.bind_mut().spawn_player(
|
|
player.id(),
|
|
player.id() == self.self_id.unwrap_or(0),
|
|
Some(transform),
|
|
);
|
|
player.character = character;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle a sync package from the server
|
|
pub fn handle_sync(
|
|
&mut self,
|
|
sync_data: HashMap<u16, SyncPackage>,
|
|
ball_sync: Option<BallSync>,
|
|
) {
|
|
for (
|
|
player_id,
|
|
SyncPackage {
|
|
movement_direction,
|
|
look_up,
|
|
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(movement_direction);
|
|
player.apply_look_up(look_up);
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(ball_sync) = ball_sync
|
|
&& let Some(ball) = &mut self.world_ball
|
|
{
|
|
ball.bind_mut().apply_sync(ball_sync);
|
|
}
|
|
}
|
|
|
|
// Handle a jump, either from the server or from a client
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle shooting, either from the server of from a client
|
|
pub fn handle_shoot(
|
|
&mut self,
|
|
player_id: u16,
|
|
to: Vector3,
|
|
transform: NetTransform,
|
|
look_up: f32,
|
|
deal_damage: bool,
|
|
) -> bool {
|
|
let self_id = self.self_id.clone().unwrap_or(0);
|
|
if let Some(player) = self.find_player_mut(player_id) {
|
|
if player.id() != self_id {
|
|
player.try_shoot(to, transform, look_up, deal_damage)
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
// Handle taking damage, from server to client
|
|
pub fn handle_take_damage(&mut self, source: DamageSource, player_id: u16, damage: i32) {
|
|
if let Some(player) = self.find_player_mut(player_id) {
|
|
if let Some(character) = &mut player.character {
|
|
character.dyn_bind_mut().take_damage(source, damage, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle dying, from server to client
|
|
pub fn handle_kill(&mut self, source: DamageSource, player_id: u16) {
|
|
if let Some(player) = self.find_player_mut(player_id) {
|
|
if let Some(character) = &mut player.character {
|
|
character.dyn_bind_mut().kill(source);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called whenever a player takes damage on the server-side
|
|
pub fn on_take_damage(&mut self, source: DamageSource, target_player_id: u16, damage: i32) {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
if let PeerKind::Server(peer, _) = peer {
|
|
peer.broadcast_reliable(TakeDamage(source, target_player_id, damage));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called whenever a player dies on the server-side
|
|
pub fn on_death(&mut self, source: DamageSource, target_player_id: u16) {
|
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
|
if let PeerKind::Server(peer, _) = peer {
|
|
peer.broadcast_reliable(Kill(source, target_player_id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Player {
|
|
pub connection_addr: SocketAddr,
|
|
pub data: NetPlayer,
|
|
pub ready: bool,
|
|
pub character: Option<DynGd<CharacterBody3D, dyn IPlayer + 'static>>,
|
|
}
|
|
|
|
impl Player {
|
|
pub fn get_transform(&self) -> Option<NetTransform> {
|
|
if let Some(character) = &self.character {
|
|
Some(character.dyn_bind().get_transform())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn set_transform(&mut self, transform: NetTransform) {
|
|
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 get_look_up(&self) -> Option<f32> {
|
|
if let Some(character) = &self.character {
|
|
Some(character.dyn_bind().get_look_up())
|
|
} 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<SyncPackage> {
|
|
if let Some(character) = &self.character {
|
|
Some(SyncPackage {
|
|
movement_direction: character.dyn_bind().get_movement_dir().into(),
|
|
look_up: character.dyn_bind().get_look_up(),
|
|
transform: character.dyn_bind().get_transform(),
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn apply_self_sync(&mut self, data: SyncPackage) {
|
|
if let Some(character) = &mut self.character {
|
|
character
|
|
.dyn_bind_mut()
|
|
.set_movement_dir(data.movement_direction.into());
|
|
character
|
|
.dyn_bind_mut()
|
|
.apply_rotation(data.transform.rotation.into());
|
|
character.dyn_bind_mut().apply_look_up(data.look_up);
|
|
}
|
|
}
|
|
|
|
pub fn apply_jump(&mut self) {
|
|
if let Some(character) = &mut self.character {
|
|
character.dyn_bind_mut().jump();
|
|
}
|
|
}
|
|
|
|
pub fn apply_look_up(&mut self, rot: f32) {
|
|
if let Some(character) = &mut self.character {
|
|
character.dyn_bind_mut().apply_look_up(rot);
|
|
}
|
|
}
|
|
|
|
pub fn try_shoot(
|
|
&mut self,
|
|
to: Vector3,
|
|
transform: NetTransform,
|
|
look_up: f32,
|
|
deal_damage: bool,
|
|
) -> bool {
|
|
if let Some(character) = &mut self.character {
|
|
character
|
|
.dyn_bind_mut()
|
|
.apply_rotation(transform.rotation.into());
|
|
character.dyn_bind_mut().apply_look_up(look_up);
|
|
character.dyn_bind_mut().try_shoot(to, deal_damage)
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn id(&self) -> u16 {
|
|
self.data.id
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct NetPlayer {
|
|
pub id: u16,
|
|
pub name: String,
|
|
pub ping: u128,
|
|
pub team: u8,
|
|
}
|
|
|
|
impl From<NetPlayer> for Player {
|
|
fn from(value: NetPlayer) -> Self {
|
|
Player {
|
|
connection_addr: SocketAddr::from(([0, 0, 0, 0], 0)),
|
|
data: value,
|
|
ready: false,
|
|
character: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Player> for NetPlayer {
|
|
fn from(value: Player) -> Self {
|
|
value.data
|
|
}
|
|
}
|