Compare commits
10 Commits
b93eded960
...
d251973dd3
| Author | SHA1 | Date | |
|---|---|---|---|
| d251973dd3 | |||
| 11b6691a4e | |||
| 73c7c5b850 | |||
| c594af3bd1 | |||
| b01ecbfe13 | |||
| 936de8fd0b | |||
| 6ffdd201a7 | |||
| 4c58824840 | |||
| 8893a2370d | |||
| eaac940b98 |
7
godot/maps/default_map.tres
Normal file
7
godot/maps/default_map.tres
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[gd_resource type="Map" format=3 uid="uid://b4rw48yjf7q3q"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://lahirbqfppvw" path="res://scenes/main.tscn" id="1_k5804"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
name = "Default Map"
|
||||||
|
scene = ExtResource("1_k5804")
|
||||||
7
godot/maps/default_map_2.tres
Normal file
7
godot/maps/default_map_2.tres
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[gd_resource type="Map" format=3 uid="uid://crti664evgfmj"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://lahirbqfppvw" path="res://scenes/main.tscn" id="1_gr7ev"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
name = "Default Map 2"
|
||||||
|
scene = ExtResource("1_gr7ev")
|
||||||
@ -34,6 +34,15 @@ project/assembly_name="QuakeBall"
|
|||||||
|
|
||||||
naming/node_name_casing=2
|
naming/node_name_casing=2
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
toggle_cli={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":167,"key_label":0,"unicode":167,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[physics]
|
[physics]
|
||||||
|
|
||||||
3d/physics_engine="Jolt Physics"
|
3d/physics_engine="Jolt Physics"
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
[gd_scene format=3 uid="uid://bsb6asp5o35w3"]
|
[gd_scene format=3 uid="uid://bsb6asp5o35w3"]
|
||||||
|
|
||||||
[node name="canvas_layer" type="CanvasLayer" unique_id=1517655401]
|
[node name="canvas_layer" type="CanvasLayer" unique_id=1517655401]
|
||||||
|
layer = 10
|
||||||
|
|
||||||
[node name="command_line_panel" type="CommandLinePanel" parent="." unique_id=2077938969 node_paths=PackedStringArray("input_field", "text_field", "scroll_container")]
|
[node name="command_line_panel" type="CommandLinePanel" parent="." unique_id=2077938969 node_paths=PackedStringArray("input_field", "text_field", "scroll_container")]
|
||||||
input_field = NodePath("input_field")
|
input_field = NodePath("input_field")
|
||||||
@ -8,7 +9,10 @@ text_field = NodePath("scroll_container/text_field")
|
|||||||
scroll_container = NodePath("scroll_container")
|
scroll_container = NodePath("scroll_container")
|
||||||
anchors_preset = 10
|
anchors_preset = 10
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_bottom = 307.0
|
offset_left = 1.0
|
||||||
|
offset_top = -344.0
|
||||||
|
offset_right = 1.0
|
||||||
|
offset_bottom = -37.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
|
|
||||||
[node name="input_field" type="TextEdit" parent="command_line_panel" unique_id=2095748573]
|
[node name="input_field" type="TextEdit" parent="command_line_panel" unique_id=2095748573]
|
||||||
@ -18,6 +22,8 @@ anchor_right = 1.0
|
|||||||
offset_top = 307.0
|
offset_top = 307.0
|
||||||
offset_bottom = 342.0
|
offset_bottom = 342.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
|
editable = false
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
[node name="scroll_container" type="ScrollContainer" parent="command_line_panel" unique_id=1922272461]
|
[node name="scroll_container" type="ScrollContainer" parent="command_line_panel" unique_id=1922272461]
|
||||||
custom_minimum_size = Vector2(-1, 0)
|
custom_minimum_size = Vector2(-1, 0)
|
||||||
@ -28,6 +34,7 @@ offset_left = 1.0
|
|||||||
offset_right = -1.0
|
offset_right = -1.0
|
||||||
offset_bottom = 292.0
|
offset_bottom = 292.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
[node name="text_field" type="RichTextLabel" parent="command_line_panel/scroll_container" unique_id=591332585]
|
[node name="text_field" type="RichTextLabel" parent="command_line_panel/scroll_container" unique_id=591332585]
|
||||||
custom_minimum_size = Vector2(-1, -1)
|
custom_minimum_size = Vector2(-1, -1)
|
||||||
@ -37,3 +44,4 @@ fit_content = true
|
|||||||
scroll_active = false
|
scroll_active = false
|
||||||
autowrap_mode = 0
|
autowrap_mode = 0
|
||||||
shortcut_keys_enabled = false
|
shortcut_keys_enabled = false
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
[gd_scene format=3 uid="uid://bii6018k3ip3t"]
|
[gd_scene format=3 uid="uid://bii6018k3ip3t"]
|
||||||
|
|
||||||
|
[ext_resource type="Map" uid="uid://b4rw48yjf7q3q" path="res://maps/default_map.tres" id="1_glms7"]
|
||||||
|
[ext_resource type="Map" uid="uid://crti664evgfmj" path="res://maps/default_map_2.tres" id="2_udu3i"]
|
||||||
|
|
||||||
[node name="game_manager" type="GameManager" unique_id=1442044769]
|
[node name="game_manager" type="GameManager" unique_id=1442044769]
|
||||||
|
maps = Array[Map]([ExtResource("1_glms7"), ExtResource("2_udu3i")])
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
[gd_scene format=3 uid="uid://bwd6bj21x310s"]
|
[gd_scene format=3 uid="uid://bwd6bj21x310s"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ccd0wj2usx5os" path="res://scenes/player_listing.tscn" id="1_o1atq"]
|
||||||
|
|
||||||
[sub_resource type="LabelSettings" id="LabelSettings_1cgct"]
|
[sub_resource type="LabelSettings" id="LabelSettings_1cgct"]
|
||||||
font_size = 32
|
font_size = 32
|
||||||
|
|
||||||
[node name="lobby" type="LobbyPanel" unique_id=915192272 node_paths=PackedStringArray("players_list", "name_field")]
|
[node name="lobby" type="LobbyPanel" unique_id=915192272 node_paths=PackedStringArray("players_list", "name_field", "ready_button", "map_selection")]
|
||||||
players_list = NodePath("v_box_container/players_list")
|
players_list = NodePath("v_box_container/players_list")
|
||||||
name_field = NodePath("h_box_container/text_edit")
|
name_field = NodePath("v_box_container/h_box_container/text_edit")
|
||||||
|
player_listing_prefab = ExtResource("1_o1atq")
|
||||||
|
ready_button = NodePath("ready_button")
|
||||||
|
map_selection = NodePath("v_box_container2/option_button")
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
@ -25,29 +30,57 @@ horizontal_alignment = 1
|
|||||||
|
|
||||||
[node name="v_box_container" type="VBoxContainer" parent="." unique_id=336946640]
|
[node name="v_box_container" type="VBoxContainer" parent="." unique_id=336946640]
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_left = 178.0
|
offset_left = 129.0
|
||||||
offset_top = 189.0
|
offset_top = 146.0
|
||||||
offset_right = 239.0
|
offset_right = 334.0
|
||||||
offset_bottom = 229.0
|
offset_bottom = 200.0
|
||||||
|
|
||||||
|
[node name="h_box_container" type="HBoxContainer" parent="v_box_container" unique_id=1789302658]
|
||||||
|
layout_mode = 2
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
|
[node name="label" type="Label" parent="v_box_container/h_box_container" unique_id=626633696]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Name:"
|
||||||
|
|
||||||
|
[node name="text_edit" type="TextEdit" parent="v_box_container/h_box_container" unique_id=683714912]
|
||||||
|
custom_minimum_size = Vector2(150, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="players_label" type="Label" parent="v_box_container" unique_id=1537542117]
|
[node name="players_label" type="Label" parent="v_box_container" unique_id=1537542117]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Players:"
|
text = "Players:"
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
[node name="players_list" type="VBoxContainer" parent="v_box_container" unique_id=1834737379]
|
[node name="players_list" type="VBoxContainer" parent="v_box_container" unique_id=1834737379]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
[node name="h_box_container" type="HBoxContainer" parent="." unique_id=1789302658]
|
[node name="netstats" type="NetStatsLabel" parent="." unique_id=1335814511]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 12
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = -144.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
vertical_alignment = 2
|
||||||
|
|
||||||
|
[node name="ready_button" type="Button" parent="." unique_id=311553419]
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_left = 221.0
|
offset_left = 129.0
|
||||||
offset_top = 512.0
|
offset_top = 504.0
|
||||||
offset_right = 261.0
|
offset_right = 278.0
|
||||||
offset_bottom = 552.0
|
offset_bottom = 555.0
|
||||||
|
text = "Not Ready"
|
||||||
|
|
||||||
[node name="label" type="Label" parent="h_box_container" unique_id=626633696]
|
[node name="v_box_container2" type="VBoxContainer" parent="." unique_id=653902328]
|
||||||
layout_mode = 2
|
layout_mode = 0
|
||||||
text = "Name:"
|
offset_left = 716.0
|
||||||
|
offset_top = 146.0
|
||||||
|
offset_right = 824.0
|
||||||
|
offset_bottom = 204.0
|
||||||
|
|
||||||
[node name="text_edit" type="TextEdit" parent="h_box_container" unique_id=683714912]
|
[node name="option_button" type="OptionButton" parent="v_box_container2" unique_id=426342653]
|
||||||
custom_minimum_size = Vector2(150, 0)
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|||||||
26
godot/scenes/player_listing.tscn
Normal file
26
godot/scenes/player_listing.tscn
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[gd_scene format=3 uid="uid://ccd0wj2usx5os"]
|
||||||
|
|
||||||
|
[node name="player_listing" type="PlayerListing" unique_id=772938280 node_paths=PackedStringArray("name_label", "ping_label", "ready_label")]
|
||||||
|
name_label = NodePath("name")
|
||||||
|
ping_label = NodePath("ping")
|
||||||
|
ready_label = NodePath("ready")
|
||||||
|
offset_right = 319.0
|
||||||
|
offset_bottom = 23.0
|
||||||
|
columns = 3
|
||||||
|
|
||||||
|
[node name="name" type="Label" parent="." unique_id=635700663]
|
||||||
|
custom_minimum_size = Vector2(200, 0)
|
||||||
|
custom_maximum_size = Vector2(200, -1)
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Name"
|
||||||
|
|
||||||
|
[node name="ready" type="Label" parent="." unique_id=1064828735]
|
||||||
|
custom_minimum_size = Vector2(100, 0)
|
||||||
|
custom_maximum_size = Vector2(100, -1)
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Ready"
|
||||||
|
|
||||||
|
[node name="ping" type="Label" parent="." unique_id=1935412601]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "(200ms)"
|
||||||
|
horizontal_alignment = 2
|
||||||
@ -3,6 +3,11 @@ use std::net::SocketAddr;
|
|||||||
use godot::{prelude::*, tools::get_autoload_by_name};
|
use godot::{prelude::*, tools::get_autoload_by_name};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
map_resource::Map,
|
||||||
|
net::network_manager::{NetworkManager, Package, PeerKind},
|
||||||
|
};
|
||||||
|
|
||||||
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
@ -10,6 +15,9 @@ pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
|||||||
pub struct GameManager {
|
pub struct GameManager {
|
||||||
pub game: Option<Gd<Game>>,
|
pub game: Option<Gd<Game>>,
|
||||||
base: Base<Node>,
|
base: Base<Node>,
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
pub maps: Array<Gd<Map>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[godot_api]
|
#[godot_api]
|
||||||
@ -20,6 +28,8 @@ impl GameManager {
|
|||||||
pub fn on_new_player(id: u16);
|
pub fn on_new_player(id: u16);
|
||||||
#[signal]
|
#[signal]
|
||||||
pub fn on_player_disconnected(id: u16);
|
pub fn on_player_disconnected(id: u16);
|
||||||
|
#[signal]
|
||||||
|
pub fn on_map_changed(id: u8);
|
||||||
|
|
||||||
pub fn singleton() -> Gd<GameManager> {
|
pub fn singleton() -> Gd<GameManager> {
|
||||||
get_autoload_by_name::<GameManager>(GAME_MANAGER_GLOBAL)
|
get_autoload_by_name::<GameManager>(GAME_MANAGER_GLOBAL)
|
||||||
@ -31,6 +41,7 @@ impl GameManager {
|
|||||||
}
|
}
|
||||||
let mut game = Game::new_alloc();
|
let mut game = Game::new_alloc();
|
||||||
game.bind_mut().is_host = is_host;
|
game.bind_mut().is_host = is_host;
|
||||||
|
game.bind_mut().maps = self.maps.clone();
|
||||||
|
|
||||||
self.run_deferred(|s| {
|
self.run_deferred(|s| {
|
||||||
if let Some(game) = &s.game {
|
if let Some(game) = &s.game {
|
||||||
@ -43,6 +54,9 @@ impl GameManager {
|
|||||||
game.signals()
|
game.signals()
|
||||||
.on_players_updated()
|
.on_players_updated()
|
||||||
.connect_other(s, |s| s.signals().on_players_updated().emit());
|
.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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,10 +69,15 @@ impl GameManager {
|
|||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(base=Node, init)]
|
#[class(base=Node, init)]
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
|
pub selected_map_idx: u8,
|
||||||
|
pub selected_map: Option<Gd<Map>>,
|
||||||
|
pub maps: Array<Gd<Map>>,
|
||||||
|
|
||||||
is_host: bool,
|
is_host: bool,
|
||||||
player_counter: u16,
|
player_counter: u16,
|
||||||
pub self_id: Option<u16>,
|
pub self_id: Option<u16>,
|
||||||
pub players: Vec<Player>,
|
pub players: Vec<Player>,
|
||||||
|
game_started: bool,
|
||||||
|
|
||||||
base: Base<Node>,
|
base: Base<Node>,
|
||||||
}
|
}
|
||||||
@ -71,6 +90,8 @@ impl Game {
|
|||||||
pub fn on_new_player(id: u16);
|
pub fn on_new_player(id: u16);
|
||||||
#[signal]
|
#[signal]
|
||||||
pub fn on_player_disconnected(id: u16);
|
pub fn on_player_disconnected(id: u16);
|
||||||
|
#[signal]
|
||||||
|
pub fn on_map_changed(id: u8);
|
||||||
|
|
||||||
pub fn singleton() -> Option<Gd<Game>> {
|
pub fn singleton() -> Option<Gd<Game>> {
|
||||||
GameManager::singleton()
|
GameManager::singleton()
|
||||||
@ -78,34 +99,75 @@ impl Game {
|
|||||||
.map(|g| g.cast::<Game>())
|
.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) {
|
pub fn set_self(&mut self, id: u16) {
|
||||||
self.self_id = Some(id);
|
self.self_id = Some(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_player(&mut self, addr: SocketAddr, name: String) -> u16 {
|
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;
|
let id = self.player_counter;
|
||||||
self.player_counter += 1;
|
self.player_counter += 1;
|
||||||
|
id
|
||||||
|
};
|
||||||
self.players.push(Player {
|
self.players.push(Player {
|
||||||
id,
|
id,
|
||||||
connection_addr: addr,
|
connection_addr: addr,
|
||||||
name,
|
name,
|
||||||
|
ping: 0,
|
||||||
|
ready: false,
|
||||||
});
|
});
|
||||||
self.signals().on_new_player().emit(id);
|
self.run_deferred(move |s| s.signals().on_new_player().emit(id));
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_players(&mut self, players: Vec<Player>) {
|
pub fn set_players(&mut self, players: Vec<Player>) {
|
||||||
self.players = players;
|
self.players = players;
|
||||||
self.signals().on_players_updated().emit();
|
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> {
|
pub fn find_player_by_addr(&self, addr: &SocketAddr) -> Option<&Player> {
|
||||||
self.players.iter().find(|p| p.connection_addr == *addr)
|
self.players.iter().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 remove_player(&mut self, id: u16) {
|
pub fn remove_player(&mut self, id: u16) {
|
||||||
self.signals().on_player_disconnected().emit(id);
|
|
||||||
self.players.retain(|p| p.id != id);
|
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) {
|
pub fn update_player_nick(&mut self, player_id: u16, nick: String) {
|
||||||
@ -114,6 +176,20 @@ impl Game {
|
|||||||
}
|
}
|
||||||
self.run_deferred(|s| s.signals().on_players_updated().emit());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||||
@ -121,4 +197,6 @@ pub struct Player {
|
|||||||
pub id: u16,
|
pub id: u16,
|
||||||
pub connection_addr: SocketAddr,
|
pub connection_addr: SocketAddr,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub ping: u128,
|
||||||
|
pub ready: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use std::net::SocketAddr;
|
|||||||
use godot::prelude::*;
|
use godot::prelude::*;
|
||||||
|
|
||||||
pub mod game_manager;
|
pub mod game_manager;
|
||||||
|
pub mod map_resource;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
|
|||||||
27
rust/src/map_resource.rs
Normal file
27
rust/src/map_resource.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use godot::prelude::*;
|
||||||
|
|
||||||
|
#[derive(GodotClass)]
|
||||||
|
#[class(base=Resource, init, tool)]
|
||||||
|
pub struct Map {
|
||||||
|
#[export]
|
||||||
|
pub name: GString,
|
||||||
|
#[export]
|
||||||
|
pub scene: Option<Gd<PackedScene>>,
|
||||||
|
|
||||||
|
base: Base<Resource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl IResource for Map {
|
||||||
|
fn on_notification(&mut self, what: godot::classes::notify::ObjectNotification) {}
|
||||||
|
|
||||||
|
fn to_string(&self) -> GString {
|
||||||
|
"Hello there".to_gstring()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_local_to_scene(&mut self) {}
|
||||||
|
|
||||||
|
fn reset_state(&mut self) {}
|
||||||
|
|
||||||
|
fn set_path_cache(&self, path: GString) {}
|
||||||
|
}
|
||||||
@ -1,11 +1,12 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use godot::{prelude::*, tools::get_autoload_by_name};
|
use godot::{prelude::*, tools::get_autoload_by_name};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use teanet::{Peer, PeerConfig, PeerMessage};
|
use teanet::{Peer, PeerConfig, PeerMessage, connections::Connection};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
game_manager::{Game, GameManager, Player},
|
game_manager::{Game, GameManager, Player},
|
||||||
@ -23,6 +24,7 @@ pub struct NetworkManager {
|
|||||||
|
|
||||||
last_hello: Instant,
|
last_hello: Instant,
|
||||||
last_netstat_update: Instant,
|
last_netstat_update: Instant,
|
||||||
|
last_player_update: Instant,
|
||||||
|
|
||||||
base: Base<Node>,
|
base: Base<Node>,
|
||||||
}
|
}
|
||||||
@ -39,6 +41,7 @@ impl INode for NetworkManager {
|
|||||||
|
|
||||||
last_hello: Instant::now(),
|
last_hello: Instant::now(),
|
||||||
last_netstat_update: Instant::now(),
|
last_netstat_update: Instant::now(),
|
||||||
|
last_player_update: Instant::now(),
|
||||||
|
|
||||||
base,
|
base,
|
||||||
}
|
}
|
||||||
@ -74,6 +77,29 @@ impl INode for NetworkManager {
|
|||||||
self.last_netstat_update = now;
|
self.last_netstat_update = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if now - self.last_player_update > Duration::from_millis(500) {
|
||||||
|
self.last_player_update = now;
|
||||||
|
if let PeerKind::Server(peer, _) = peer {
|
||||||
|
let mut pings = HashMap::new();
|
||||||
|
|
||||||
|
for connection in peer.connections() {
|
||||||
|
if let Some(mut game) = Game::singleton() {
|
||||||
|
let player = game
|
||||||
|
.bind()
|
||||||
|
.find_player_by_addr(&connection.address)
|
||||||
|
.cloned();
|
||||||
|
if let Some(player) = player {
|
||||||
|
game.bind_mut()
|
||||||
|
.update_player_ping(player.id, connection.ping.as_millis());
|
||||||
|
pings.insert(player.id, connection.ping.as_millis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.broadcast_reliable(Package::UpdatePings(pings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match peer {
|
match peer {
|
||||||
PeerKind::Client(..) => {}
|
PeerKind::Client(..) => {}
|
||||||
PeerKind::Server(peer, _) => {
|
PeerKind::Server(peer, _) => {
|
||||||
@ -137,6 +163,49 @@ impl INode for NetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Package::UpdatePings(pings) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
for (player_id, ping) in pings {
|
||||||
|
game.bind_mut().update_player_ping(player_id, ping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::NewPlayer(player) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
if player.id != game.bind().self_id.unwrap_or(u16::MAX)
|
||||||
|
{
|
||||||
|
game.bind_mut().new_player(
|
||||||
|
SocketAddr::from(([0, 0, 0, 0], 0)),
|
||||||
|
player.name,
|
||||||
|
Some(player.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::PlayerLeft(player) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
if player.id != game.bind().self_id.unwrap_or(u16::MAX)
|
||||||
|
{
|
||||||
|
game.bind_mut().remove_player(player.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::SetReady(player_id, is_ready) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut()
|
||||||
|
.update_player_ready(player_id, is_ready);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::SelectMap(map_id) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().change_map_selection(map_id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::GameStarted => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().start_game();
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -158,12 +227,29 @@ impl INode for NetworkManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some(game) = &mut Game::singleton() {
|
if let Some(game) = &mut Game::singleton() {
|
||||||
let self_id = game
|
let self_id = game.bind_mut().new_player(
|
||||||
.bind_mut()
|
connection.address,
|
||||||
.new_player(connection.address, String::new());
|
String::new(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let mut sent_players = game.bind().players.clone();
|
||||||
|
for player in &mut sent_players {
|
||||||
|
player.connection_addr =
|
||||||
|
SocketAddr::from(([0, 0, 0, 0], 0))
|
||||||
|
}
|
||||||
peer.send_reliable(
|
peer.send_reliable(
|
||||||
&connection.address,
|
&connection.address,
|
||||||
Package::Players(game.bind().players.clone(), self_id),
|
Package::Players(sent_players, self_id),
|
||||||
|
);
|
||||||
|
let mut sent_player =
|
||||||
|
game.bind().find_player(self_id).unwrap().clone();
|
||||||
|
sent_player.connection_addr =
|
||||||
|
SocketAddr::from(([0, 0, 0, 0], 0));
|
||||||
|
peer.broadcast_reliable(Package::NewPlayer(sent_player));
|
||||||
|
|
||||||
|
peer.send_reliable(
|
||||||
|
&connection.address,
|
||||||
|
Package::SelectMap(game.bind().selected_map_idx),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,6 +270,9 @@ impl INode for NetworkManager {
|
|||||||
.find_player_by_addr(&connection.address)
|
.find_player_by_addr(&connection.address)
|
||||||
.map(|p| p.id);
|
.map(|p| p.id);
|
||||||
if let Some(player_id) = player_id {
|
if let Some(player_id) = player_id {
|
||||||
|
peer.broadcast_reliable(Package::PlayerLeft(
|
||||||
|
game.bind().find_player(player_id).unwrap().clone(),
|
||||||
|
));
|
||||||
game.bind_mut().remove_player(player_id);
|
game.bind_mut().remove_player(player_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,6 +299,21 @@ impl INode for NetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Package::SetReady(_, is_ready) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
let player = game
|
||||||
|
.bind()
|
||||||
|
.find_player_by_addr(&conn.address)
|
||||||
|
.cloned();
|
||||||
|
if let Some(player) = player {
|
||||||
|
game.bind_mut()
|
||||||
|
.update_player_ready(player.id, is_ready);
|
||||||
|
peer.broadcast_reliable(Package::SetReady(
|
||||||
|
player.id, is_ready,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -217,7 +321,7 @@ impl INode for NetworkManager {
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
cli.bind_mut()
|
cli.bind_mut()
|
||||||
.publish_message(format!("Error: {}", err), CliColor::Info);
|
.publish_message(format!("Error: {}", err), CliColor::Error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -246,9 +350,12 @@ impl NetworkManager {
|
|||||||
self.swap_to_lobby(true);
|
self.swap_to_lobby(true);
|
||||||
|
|
||||||
if let Some(game) = &mut Game::singleton() {
|
if let Some(game) = &mut Game::singleton() {
|
||||||
let id = game
|
let id = game.bind_mut().new_player(
|
||||||
.bind_mut()
|
SocketAddr::from(([0, 0, 0, 0], 0)),
|
||||||
.new_player(SocketAddr::from(([0, 0, 0, 0], 0)), "Host".to_owned());
|
"Host".to_owned(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
game.bind_mut().update_player_ready(id, true);
|
||||||
game.bind_mut().set_self(id);
|
game.bind_mut().set_self(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,9 +427,24 @@ pub enum Package {
|
|||||||
Players(Vec<Player>, u16),
|
Players(Vec<Player>, u16),
|
||||||
SetNick(u16, String),
|
SetNick(u16, String),
|
||||||
SetSelfNick(String),
|
SetSelfNick(String),
|
||||||
|
UpdatePings(HashMap<u16, u128>),
|
||||||
|
NewPlayer(Player),
|
||||||
|
PlayerLeft(Player),
|
||||||
|
SetReady(u16, bool),
|
||||||
|
SelectMap(u8),
|
||||||
|
GameStarted,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PeerKind {
|
pub enum PeerKind {
|
||||||
Client(Peer<Package>, SocketAddr),
|
Client(Peer<Package>, SocketAddr),
|
||||||
Server(Peer<Package>, u16),
|
Server(Peer<Package>, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PeerKind {
|
||||||
|
pub fn is_host(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
PeerKind::Client(_, _) => false,
|
||||||
|
PeerKind::Server(_, _) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use std::ops::DerefMut;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use godot::classes::{
|
use godot::classes::{
|
||||||
CanvasLayer, Engine, IPanel, Panel, RichTextLabel, ScrollContainer, TextEdit,
|
CanvasLayer, Engine, IPanel, InputEvent, Panel, RichTextLabel, ScrollContainer, TextEdit,
|
||||||
};
|
};
|
||||||
use godot::prelude::*;
|
use godot::prelude::*;
|
||||||
use godot::tools::get_autoload_by_name;
|
use godot::tools::get_autoload_by_name;
|
||||||
@ -39,9 +39,14 @@ pub struct CommandLinePanel {
|
|||||||
#[export]
|
#[export]
|
||||||
pub scroll_container: Option<Gd<ScrollContainer>>,
|
pub scroll_container: Option<Gd<ScrollContainer>>,
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
pub opened: bool,
|
||||||
|
|
||||||
scroll_next_update: bool,
|
scroll_next_update: bool,
|
||||||
commands: HashMap<String, Rc<Box<dyn Command>>>,
|
commands: HashMap<String, Rc<Box<dyn Command>>>,
|
||||||
|
|
||||||
|
idle_y: f32,
|
||||||
|
|
||||||
base: Base<Panel>,
|
base: Base<Panel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,12 +66,18 @@ impl IPanel for CommandLinePanel {
|
|||||||
input_field: None,
|
input_field: None,
|
||||||
text_field: None,
|
text_field: None,
|
||||||
scroll_container: None,
|
scroll_container: None,
|
||||||
|
opened: false,
|
||||||
scroll_next_update: false,
|
scroll_next_update: false,
|
||||||
|
idle_y: 0.,
|
||||||
commands,
|
commands,
|
||||||
base,
|
base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ready(&mut self) {
|
||||||
|
self.idle_y = self.base().get_position().y;
|
||||||
|
}
|
||||||
|
|
||||||
fn process(&mut self, _delta: f64) {
|
fn process(&mut self, _delta: f64) {
|
||||||
if let Some(scroll) = &mut self.scroll_container
|
if let Some(scroll) = &mut self.scroll_container
|
||||||
&& self.scroll_next_update
|
&& self.scroll_next_update
|
||||||
@ -83,6 +94,32 @@ impl IPanel for CommandLinePanel {
|
|||||||
self.input_command(first.to_string());
|
self.input_command(first.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let old_pos = self.base().get_position();
|
||||||
|
let mut target_pos = self.base().get_position();
|
||||||
|
if self.opened {
|
||||||
|
target_pos.y = 0.;
|
||||||
|
} else {
|
||||||
|
target_pos.y = self.idle_y;
|
||||||
|
}
|
||||||
|
let new_pos = old_pos.lerp(target_pos, 0.3);
|
||||||
|
self.base_mut().set_position(new_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input(&mut self, event: Gd<InputEvent>) {
|
||||||
|
if event.is_action_pressed("toggle_cli") {
|
||||||
|
self.opened = !self.opened;
|
||||||
|
if let Some(input_field) = &mut self.input_field {
|
||||||
|
if self.opened {
|
||||||
|
input_field.grab_focus();
|
||||||
|
input_field.set_editable(true);
|
||||||
|
self.run_deferred(|s| s.input_field.as_mut().unwrap().clear());
|
||||||
|
} else {
|
||||||
|
input_field.release_focus();
|
||||||
|
input_field.set_editable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
use godot::{
|
use godot::{
|
||||||
classes::{IPanel, Label, Panel, TextEdit, VBoxContainer},
|
classes::{Button, IPanel, Label, OptionButton, Panel, TextEdit, VBoxContainer},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
game_manager::{Game, GameManager},
|
game_manager::{Game, GameManager},
|
||||||
net::network_manager::NetworkManager,
|
map_resource::Map,
|
||||||
|
net::network_manager::{NetworkManager, Package, PeerKind},
|
||||||
|
ui::player_listing::PlayerListing,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
@ -15,6 +17,14 @@ pub struct LobbyPanel {
|
|||||||
players_list: Option<Gd<VBoxContainer>>,
|
players_list: Option<Gd<VBoxContainer>>,
|
||||||
#[export]
|
#[export]
|
||||||
name_field: Option<Gd<TextEdit>>,
|
name_field: Option<Gd<TextEdit>>,
|
||||||
|
#[export]
|
||||||
|
player_listing_prefab: Option<Gd<PackedScene>>,
|
||||||
|
#[export]
|
||||||
|
ready_button: Option<Gd<Button>>,
|
||||||
|
#[export]
|
||||||
|
map_selection: Option<Gd<OptionButton>>,
|
||||||
|
|
||||||
|
is_ready: bool,
|
||||||
|
|
||||||
base: Base<Panel>,
|
base: Base<Panel>,
|
||||||
}
|
}
|
||||||
@ -25,15 +35,50 @@ impl IPanel for LobbyPanel {
|
|||||||
GameManager::singleton()
|
GameManager::singleton()
|
||||||
.signals()
|
.signals()
|
||||||
.on_new_player()
|
.on_new_player()
|
||||||
.connect_other(self, |s, _| s.update_players_list());
|
.connect_other(self, |s, _| s.update_players(true));
|
||||||
GameManager::singleton()
|
GameManager::singleton()
|
||||||
.signals()
|
.signals()
|
||||||
.on_player_disconnected()
|
.on_player_disconnected()
|
||||||
.connect_other(self, |s, _| s.update_players_list());
|
.connect_other(self, |s, _| s.update_players(true));
|
||||||
GameManager::singleton()
|
GameManager::singleton()
|
||||||
.signals()
|
.signals()
|
||||||
.on_players_updated()
|
.on_players_updated()
|
||||||
.connect_other(self, |s| s.update_players_list());
|
.connect_other(self, |s| s.update_players(false));
|
||||||
|
GameManager::singleton()
|
||||||
|
.signals()
|
||||||
|
.on_map_changed()
|
||||||
|
.connect_other(self, |s, id| s.update_map_selection(id));
|
||||||
|
|
||||||
|
if let Some(ready_button) = &self.ready_button {
|
||||||
|
ready_button
|
||||||
|
.signals()
|
||||||
|
.pressed()
|
||||||
|
.connect_other(self, |s| s.on_ready());
|
||||||
|
}
|
||||||
|
if let Some(map_selection) = &self.map_selection {
|
||||||
|
map_selection
|
||||||
|
.signals()
|
||||||
|
.item_selected()
|
||||||
|
.connect_other(self, |s, id| {
|
||||||
|
s.update_map_selection(id as u8);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(game) = Game::singleton() {
|
||||||
|
if let Some(map_selection) = &mut self.map_selection {
|
||||||
|
for map in game.bind().maps.iter_shared() {
|
||||||
|
map_selection.add_item(&map.bind().name);
|
||||||
|
}
|
||||||
|
let is_host = if let Some(peer) = &NetworkManager::singleton().bind().peer {
|
||||||
|
peer.is_host()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
map_selection.set_disabled(!is_host);
|
||||||
|
map_selection.select(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.update_map_selection(0);
|
||||||
|
|
||||||
if let Some(name_field) = &self.name_field {
|
if let Some(name_field) = &self.name_field {
|
||||||
name_field
|
name_field
|
||||||
@ -58,21 +103,103 @@ impl IPanel for LobbyPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_players_list();
|
self.update_players(true);
|
||||||
|
|
||||||
|
if let Some(peer) = &NetworkManager::singleton().bind().peer
|
||||||
|
&& peer.is_host()
|
||||||
|
{
|
||||||
|
if let Some(button) = &mut self.ready_button {
|
||||||
|
button.set_text("Start Game");
|
||||||
|
button.set_disabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.update_ready_button();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LobbyPanel {
|
impl LobbyPanel {
|
||||||
pub fn update_players_list(&mut self) {
|
fn update_ready_button(&mut self) {
|
||||||
|
if let Some(game) = Game::singleton()
|
||||||
|
&& let Some(peer) = &NetworkManager::singleton().bind().peer
|
||||||
|
{
|
||||||
|
if let PeerKind::Server(_, _) = peer
|
||||||
|
&& let Some(button) = &mut self.ready_button
|
||||||
|
{
|
||||||
|
let all_ready = game.bind().players.iter().all(|p| p.ready);
|
||||||
|
button.set_disabled(!all_ready);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_players(&mut self, replace: bool) {
|
||||||
|
self.update_ready_button();
|
||||||
|
|
||||||
if let Some(list) = &mut self.players_list {
|
if let Some(list) = &mut self.players_list {
|
||||||
|
if replace {
|
||||||
for child in list.get_children().iter_shared() {
|
for child in list.get_children().iter_shared() {
|
||||||
list.remove_child(&child);
|
list.remove_child(&child);
|
||||||
}
|
}
|
||||||
if let Some(game) = &GameManager::singleton().bind().game {
|
if let Some(game) = &GameManager::singleton().bind().game {
|
||||||
|
if let Some(prefab) = &self.player_listing_prefab {
|
||||||
for player in &game.bind().players {
|
for player in &game.bind().players {
|
||||||
let mut label = Label::new_alloc();
|
if let Some(node) = prefab.instantiate() {
|
||||||
label.set_text(&format!("{} ({})", player.name, player.connection_addr));
|
let mut listing = node.cast::<PlayerListing>();
|
||||||
list.add_child(&label);
|
listing.bind_mut().player_id = Some(player.id);
|
||||||
|
listing.bind_mut().update();
|
||||||
|
list.add_child(&listing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for child in list.get_children().iter_shared() {
|
||||||
|
child.cast::<PlayerListing>().bind_mut().update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_ready(&mut self) {
|
||||||
|
let is_host = if let Some(peer) = &NetworkManager::singleton().bind().peer {
|
||||||
|
peer.is_host()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if !is_host {
|
||||||
|
self.is_ready = !self.is_ready;
|
||||||
|
if let Some(button) = &mut self.ready_button {
|
||||||
|
if self.is_ready {
|
||||||
|
button.set_text("Ready");
|
||||||
|
} else {
|
||||||
|
button.set_text("Not Ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
||||||
|
if let PeerKind::Client(peer, socket_addr) = peer {
|
||||||
|
peer.send_reliable(&socket_addr, Package::SetReady(0, self.is_ready));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().start_game();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_map_selection(&mut self, idx: u8) {
|
||||||
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
||||||
|
match peer {
|
||||||
|
PeerKind::Client(..) => {
|
||||||
|
if let Some(map_selection) = &mut self.map_selection {
|
||||||
|
map_selection.select(idx as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PeerKind::Server(peer, _) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().change_map_selection(idx, false);
|
||||||
|
}
|
||||||
|
peer.broadcast_reliable(Package::SelectMap(idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod lobby;
|
pub mod lobby;
|
||||||
pub mod net_stats;
|
pub mod net_stats;
|
||||||
|
pub mod player_listing;
|
||||||
|
|||||||
49
rust/src/ui/player_listing.rs
Normal file
49
rust/src/ui/player_listing.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use godot::{
|
||||||
|
classes::{GridContainer, IGridContainer, Label, Panel, TextEdit, VBoxContainer},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
game_manager::{Game, Player},
|
||||||
|
net::network_manager::NetworkManager,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(GodotClass)]
|
||||||
|
#[class(base=GridContainer, init)]
|
||||||
|
pub struct PlayerListing {
|
||||||
|
#[export]
|
||||||
|
pub name_label: Option<Gd<Label>>,
|
||||||
|
#[export]
|
||||||
|
pub ping_label: Option<Gd<Label>>,
|
||||||
|
#[export]
|
||||||
|
pub ready_label: Option<Gd<Label>>,
|
||||||
|
|
||||||
|
pub player_id: Option<u16>,
|
||||||
|
|
||||||
|
base: Base<GridContainer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerListing {
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
if let Some(player_id) = &self.player_id
|
||||||
|
&& let Some(game) = Game::singleton()
|
||||||
|
&& let Some(player) = game.bind().find_player(*player_id)
|
||||||
|
{
|
||||||
|
if let Some(player_label) = &mut self.name_label {
|
||||||
|
player_label.set_text(&format!("{} ({})", player.name, player.connection_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ready_label) = &mut self.ready_label {
|
||||||
|
if player.ready {
|
||||||
|
ready_label.set_text("Ready");
|
||||||
|
} else {
|
||||||
|
ready_label.set_text("Not Ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ping_label) = &mut self.ping_label {
|
||||||
|
ping_label.set_text(&format!("({}ms)", player.ping));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 2721021f7b20831388d9ea7b3ea0797e3efdfdac
|
Subproject commit a947f3a8cbad03b939dedfc1486de75226d90c0a
|
||||||
Loading…
Reference in New Issue
Block a user