Compare commits
12 Commits
51d0093807
...
25867a0909
| Author | SHA1 | Date | |
|---|---|---|---|
| 25867a0909 | |||
| 704ea006c9 | |||
| 66082a7f04 | |||
| e1b7572098 | |||
| 1402b10180 | |||
| 4b0a90be0e | |||
| 67110b8216 | |||
| 90a39d712d | |||
| 1761d730fc | |||
| cb0fa40d1a | |||
| bbb6856185 | |||
| 93809e7fd6 |
@ -1,6 +1,6 @@
|
|||||||
[gd_resource type="Map" format=3 uid="uid://b4rw48yjf7q3q"]
|
[gd_resource type="MapResource" format=3 uid="uid://c7n1tacc7a73t"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://lahirbqfppvw" path="res://scenes/main.tscn" id="1_k5804"]
|
[ext_resource type="PackedScene" uid="uid://7iooamjot1tg" path="res://scenes/map0.tscn" id="1_k5804"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
name = "Default Map"
|
name = "Default Map"
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
[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")
|
|
||||||
@ -23,6 +23,7 @@ GameManagerGlobal="*uid://bii6018k3ip3t"
|
|||||||
|
|
||||||
[display]
|
[display]
|
||||||
|
|
||||||
|
window/size/mode=2
|
||||||
window/stretch/mode="canvas_items"
|
window/stretch/mode="canvas_items"
|
||||||
window/stretch/aspect="expand"
|
window/stretch/aspect="expand"
|
||||||
|
|
||||||
@ -40,6 +41,32 @@ toggle_cli={
|
|||||||
"deadzone": 0.2,
|
"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)
|
"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)
|
, 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)
|
||||||
|
, 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":4194343,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
left={
|
||||||
|
"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":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
right={
|
||||||
|
"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":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
forward={
|
||||||
|
"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":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
backward={
|
||||||
|
"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":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
jump={
|
||||||
|
"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":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
[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="MapResource" uid="uid://c7n1tacc7a73t" 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")])
|
maps = Array[MapResource]([ExtResource("1_glms7")])
|
||||||
|
|||||||
@ -68,19 +68,28 @@ grow_vertical = 0
|
|||||||
vertical_alignment = 2
|
vertical_alignment = 2
|
||||||
|
|
||||||
[node name="ready_button" type="Button" parent="." unique_id=311553419]
|
[node name="ready_button" type="Button" parent="." unique_id=311553419]
|
||||||
layout_mode = 0
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
offset_left = 129.0
|
offset_left = 129.0
|
||||||
offset_top = 504.0
|
offset_top = -177.0
|
||||||
offset_right = 278.0
|
offset_right = 278.0
|
||||||
offset_bottom = 555.0
|
offset_bottom = -126.0
|
||||||
|
grow_vertical = 0
|
||||||
text = "Not Ready"
|
text = "Not Ready"
|
||||||
|
|
||||||
[node name="v_box_container2" type="VBoxContainer" parent="." unique_id=653902328]
|
[node name="v_box_container2" type="VBoxContainer" parent="." unique_id=653902328]
|
||||||
layout_mode = 0
|
layout_mode = 1
|
||||||
offset_left = 716.0
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -329.0
|
||||||
offset_top = 146.0
|
offset_top = 146.0
|
||||||
offset_right = 824.0
|
offset_right = -221.0
|
||||||
offset_bottom = 204.0
|
offset_bottom = 204.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
|
||||||
[node name="option_button" type="OptionButton" parent="v_box_container2" unique_id=426342653]
|
[node name="option_button" type="OptionButton" parent="v_box_container2" unique_id=426342653]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|||||||
21
godot/scenes/local_player.tscn
Normal file
21
godot/scenes/local_player.tscn
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[gd_scene format=3 uid="uid://ct70bpbca8one"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_1cgct"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1cgct"]
|
||||||
|
|
||||||
|
[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera")]
|
||||||
|
look_sensitivity = 1.5
|
||||||
|
camera = NodePath("camera_3d")
|
||||||
|
move_speed = 5.0
|
||||||
|
collision_mask = 2
|
||||||
|
|
||||||
|
[node name="mesh_instance_3d" type="MeshInstance3D" parent="." unique_id=700336478]
|
||||||
|
mesh = SubResource("CapsuleMesh_1cgct")
|
||||||
|
|
||||||
|
[node name="collision_shape_3d" type="CollisionShape3D" parent="." unique_id=1110995513]
|
||||||
|
shape = SubResource("CapsuleShape3D_1cgct")
|
||||||
|
|
||||||
|
[node name="camera_3d" type="Camera3D" parent="." unique_id=1773137418]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5391109, 0)
|
||||||
|
current = true
|
||||||
46
godot/scenes/map0.tscn
Normal file
46
godot/scenes/map0.tscn
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
[gd_scene format=3 uid="uid://7iooamjot1tg"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ct70bpbca8one" path="res://scenes/local_player.tscn" id="1_yjvui"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://kfxc0migw80o" path="res://scenes/remote_player.tscn" id="2_kcncr"]
|
||||||
|
|
||||||
|
[sub_resource type="ImageTexture" id="ImageTexture_3vyb7"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_u8vuu"]
|
||||||
|
albedo_texture = SubResource("ImageTexture_3vyb7")
|
||||||
|
|
||||||
|
[sub_resource type="PlaneMesh" id="PlaneMesh_1cgct"]
|
||||||
|
size = Vector2(100, 100)
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_1cgct"]
|
||||||
|
size = Vector3(100, 0.01, 100)
|
||||||
|
|
||||||
|
[node name="map_0" type="Map" unique_id=914577955 node_paths=PackedStringArray("spawner")]
|
||||||
|
spawner = NodePath("spawner")
|
||||||
|
local_player = ExtResource("1_yjvui")
|
||||||
|
remote_player = ExtResource("2_kcncr")
|
||||||
|
|
||||||
|
[node name="net_stats_label" type="NetStatsLabel" parent="." unique_id=1821474986]
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = -152.0
|
||||||
|
offset_right = 369.0
|
||||||
|
grow_vertical = 0
|
||||||
|
vertical_alignment = 2
|
||||||
|
|
||||||
|
[node name="terrain" type="MeshInstance3D" parent="." unique_id=1474127252]
|
||||||
|
material_override = SubResource("StandardMaterial3D_u8vuu")
|
||||||
|
mesh = SubResource("PlaneMesh_1cgct")
|
||||||
|
|
||||||
|
[node name="static_body_3d" type="StaticBody3D" parent="terrain" unique_id=203785193]
|
||||||
|
collision_layer = 3
|
||||||
|
collision_mask = 3
|
||||||
|
|
||||||
|
[node name="collision_shape_3d" type="CollisionShape3D" parent="terrain/static_body_3d" unique_id=1466526153]
|
||||||
|
shape = SubResource("BoxShape3D_1cgct")
|
||||||
|
|
||||||
|
[node name="directional_light_3d" type="DirectionalLight3D" parent="." unique_id=793524416]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.7274598, 0.6861503, 0, -0.6861503, 0.7274598, -5.1267624, 3.3997262, 3.715582)
|
||||||
|
|
||||||
|
[node name="spawner" type="Node3D" parent="." unique_id=1364438299]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.028572083, 3.9666176, 0.031027317)
|
||||||
15
godot/scenes/remote_player.tscn
Normal file
15
godot/scenes/remote_player.tscn
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[gd_scene format=3 uid="uid://kfxc0migw80o"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_1cgct"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1cgct"]
|
||||||
|
|
||||||
|
[node name="player" type="RemotePlayer" unique_id=845968872]
|
||||||
|
move_speed = 5.0
|
||||||
|
collision_mask = 2
|
||||||
|
|
||||||
|
[node name="mesh_instance_3d" type="MeshInstance3D" parent="." unique_id=700336478]
|
||||||
|
mesh = SubResource("CapsuleMesh_1cgct")
|
||||||
|
|
||||||
|
[node name="collision_shape_3d" type="CollisionShape3D" parent="." unique_id=1110995513]
|
||||||
|
shape = SubResource("CapsuleShape3D_1cgct")
|
||||||
@ -1,11 +1,20 @@
|
|||||||
use std::net::SocketAddr;
|
use std::{collections::HashMap, net::SocketAddr};
|
||||||
|
|
||||||
use godot::{prelude::*, tools::get_autoload_by_name};
|
use godot::{classes::CharacterBody3D, prelude::*, tools::get_autoload_by_name};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
map_resource::Map,
|
map::Map,
|
||||||
net::network_manager::{NetworkManager, Package, PeerKind},
|
map_resource::MapResource,
|
||||||
|
net::{
|
||||||
|
network_manager::{
|
||||||
|
NetworkManager,
|
||||||
|
Package::{self, SelfSync},
|
||||||
|
PeerKind,
|
||||||
|
},
|
||||||
|
util::NetVector3,
|
||||||
|
},
|
||||||
|
player::{IPlayer, NetPlayerTransform},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
|
||||||
@ -17,7 +26,7 @@ pub struct GameManager {
|
|||||||
base: Base<Node>,
|
base: Base<Node>,
|
||||||
|
|
||||||
#[export]
|
#[export]
|
||||||
pub maps: Array<Gd<Map>>,
|
pub maps: Array<Gd<MapResource>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[godot_api]
|
#[godot_api]
|
||||||
@ -70,8 +79,8 @@ impl GameManager {
|
|||||||
#[class(base=Node, init)]
|
#[class(base=Node, init)]
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
pub selected_map_idx: u8,
|
pub selected_map_idx: u8,
|
||||||
pub selected_map: Option<Gd<Map>>,
|
pub selected_map: Option<Gd<MapResource>>,
|
||||||
pub maps: Array<Gd<Map>>,
|
pub maps: Array<Gd<MapResource>>,
|
||||||
|
|
||||||
is_host: bool,
|
is_host: bool,
|
||||||
player_counter: u16,
|
player_counter: u16,
|
||||||
@ -79,9 +88,53 @@ pub struct Game {
|
|||||||
pub players: Vec<Player>,
|
pub players: Vec<Player>,
|
||||||
game_started: bool,
|
game_started: bool,
|
||||||
|
|
||||||
|
pub current_map: Option<Gd<Map>>,
|
||||||
|
since_last_sync: f64,
|
||||||
|
|
||||||
base: Base<Node>,
|
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]
|
#[godot_api]
|
||||||
impl Game {
|
impl Game {
|
||||||
#[signal]
|
#[signal]
|
||||||
@ -145,6 +198,7 @@ impl Game {
|
|||||||
name,
|
name,
|
||||||
ping: 0,
|
ping: 0,
|
||||||
ready: false,
|
ready: false,
|
||||||
|
character: None,
|
||||||
});
|
});
|
||||||
self.run_deferred(move |s| s.signals().on_new_player().emit(id));
|
self.run_deferred(move |s| s.signals().on_new_player().emit(id));
|
||||||
id
|
id
|
||||||
@ -161,10 +215,18 @@ impl Game {
|
|||||||
self.players.iter().find(|p| p.connection_addr == *addr)
|
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> {
|
pub fn find_player(&self, player_id: u16) -> Option<&Player> {
|
||||||
self.players.iter().find(|p| p.id == player_id)
|
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) {
|
pub fn remove_player(&mut self, id: u16) {
|
||||||
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));
|
self.run_deferred(move |s| s.signals().on_player_disconnected().emit(id));
|
||||||
@ -190,13 +252,162 @@ impl Game {
|
|||||||
}
|
}
|
||||||
self.run_deferred(|s| s.signals().on_players_updated().emit());
|
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, Deserialize, Serialize, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Player {
|
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 ping: u128,
|
||||||
pub ready: bool,
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,10 @@ use std::net::SocketAddr;
|
|||||||
use godot::prelude::*;
|
use godot::prelude::*;
|
||||||
|
|
||||||
pub mod game_manager;
|
pub mod game_manager;
|
||||||
|
pub mod map;
|
||||||
pub mod map_resource;
|
pub mod map_resource;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
pub mod player;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
struct MyExtension;
|
struct MyExtension;
|
||||||
|
|||||||
87
rust/src/map.rs
Normal file
87
rust/src/map.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use godot::{classes::CharacterBody3D, prelude::*};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
game_manager::{Game, Player},
|
||||||
|
player::{IPlayer, LocalPlayer, NetPlayerTransform, RemotePlayer},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(GodotClass)]
|
||||||
|
#[class(base=Node3D, init)]
|
||||||
|
pub struct Map {
|
||||||
|
#[export]
|
||||||
|
pub spawner: Option<Gd<Node3D>>,
|
||||||
|
#[export]
|
||||||
|
pub local_player: Option<Gd<PackedScene>>,
|
||||||
|
#[export]
|
||||||
|
pub remote_player: Option<Gd<PackedScene>>,
|
||||||
|
|
||||||
|
base: Base<Node3D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl INode3D for Map {
|
||||||
|
fn ready(&mut self) {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().on_map_loaded(self.to_gd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl Map {
|
||||||
|
#[signal]
|
||||||
|
pub fn on_loaded();
|
||||||
|
|
||||||
|
pub fn spawn_player(
|
||||||
|
&mut self,
|
||||||
|
player: &Player,
|
||||||
|
is_local: bool,
|
||||||
|
transform: Option<NetPlayerTransform>,
|
||||||
|
) -> Option<DynGd<CharacterBody3D, dyn IPlayer + 'static>> {
|
||||||
|
if is_local {
|
||||||
|
if let Some(local_player) = &self.local_player {
|
||||||
|
let mut node = local_player.instantiate_as::<LocalPlayer>();
|
||||||
|
node.bind_mut().player_id = Some(player.id);
|
||||||
|
self.base_mut().add_child(&node);
|
||||||
|
if let Some(transform) = transform {
|
||||||
|
node.bind_mut()
|
||||||
|
.base_mut()
|
||||||
|
.set_global_position(transform.location.into());
|
||||||
|
node.bind_mut()
|
||||||
|
.base_mut()
|
||||||
|
.set_global_rotation(transform.rotation.into());
|
||||||
|
} else {
|
||||||
|
if let Some(spawner) = &self.spawner {
|
||||||
|
node.bind_mut()
|
||||||
|
.base_mut()
|
||||||
|
.set_global_position(spawner.get_global_position());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(node.into_dyn::<dyn IPlayer>().upcast())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else if let Some(remote_player) = &self.remote_player {
|
||||||
|
let mut node = remote_player.instantiate_as::<RemotePlayer>();
|
||||||
|
node.bind_mut().player_id = Some(player.id);
|
||||||
|
self.base_mut().add_child(&node);
|
||||||
|
if let Some(transform) = transform {
|
||||||
|
node.bind_mut()
|
||||||
|
.base_mut()
|
||||||
|
.set_global_position(transform.location.into());
|
||||||
|
node.bind_mut()
|
||||||
|
.base_mut()
|
||||||
|
.set_global_rotation(transform.rotation.into());
|
||||||
|
} else {
|
||||||
|
if let Some(spawner) = &self.spawner {
|
||||||
|
node.bind_mut()
|
||||||
|
.base_mut()
|
||||||
|
.set_global_position(spawner.get_global_position());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(node.into_dyn::<dyn IPlayer>().upcast())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ use godot::prelude::*;
|
|||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(base=Resource, init, tool)]
|
#[class(base=Resource, init, tool)]
|
||||||
pub struct Map {
|
pub struct MapResource {
|
||||||
#[export]
|
#[export]
|
||||||
pub name: GString,
|
pub name: GString,
|
||||||
#[export]
|
#[export]
|
||||||
@ -12,7 +12,7 @@ pub struct Map {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[godot_api]
|
#[godot_api]
|
||||||
impl IResource for Map {
|
impl IResource for MapResource {
|
||||||
fn on_notification(&mut self, what: godot::classes::notify::ObjectNotification) {}
|
fn on_notification(&mut self, what: godot::classes::notify::ObjectNotification) {}
|
||||||
|
|
||||||
fn to_string(&self) -> GString {
|
fn to_string(&self) -> GString {
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
pub mod net_stats;
|
pub mod net_stats;
|
||||||
pub mod network_manager;
|
pub mod network_manager;
|
||||||
|
pub mod util;
|
||||||
|
|||||||
@ -9,8 +9,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
use teanet::{Peer, PeerConfig, PeerMessage, connections::Connection};
|
use teanet::{Peer, PeerConfig, PeerMessage, connections::Connection};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
game_manager::{Game, GameManager, Player},
|
game_manager::{Game, GameManager, NetPlayer, Player},
|
||||||
net::net_stats::Stats,
|
net::{net_stats::Stats, util::NetVector3},
|
||||||
|
player::NetPlayerTransform,
|
||||||
ui::cli::{CliColor, CommandLinePanel},
|
ui::cli::{CliColor, CommandLinePanel},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,7 +153,12 @@ impl INode for NetworkManager {
|
|||||||
CliColor::Info,
|
CliColor::Info,
|
||||||
);
|
);
|
||||||
if let Some(game) = &mut Game::singleton() {
|
if let Some(game) = &mut Game::singleton() {
|
||||||
game.bind_mut().set_players(players);
|
game.bind_mut().set_players(
|
||||||
|
players
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.clone().into())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
game.bind_mut().set_self(self_id);
|
game.bind_mut().set_self(self_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,6 +212,21 @@ impl INode for NetworkManager {
|
|||||||
game.bind_mut().start_game();
|
game.bind_mut().start_game();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Package::SpawnPlayer(id, transform) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().spawn_player(id, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::Sync(sync_data) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().handle_sync(sync_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::Jump(player_id) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
game.bind_mut().handle_jump(player_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -232,20 +253,25 @@ impl INode for NetworkManager {
|
|||||||
String::new(),
|
String::new(),
|
||||||
None,
|
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(sent_players, self_id),
|
Package::Players(
|
||||||
|
game.bind()
|
||||||
|
.players
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.into())
|
||||||
|
.collect(),
|
||||||
|
self_id,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
let mut sent_player =
|
peer.broadcast_reliable(Package::NewPlayer(
|
||||||
game.bind().find_player(self_id).unwrap().clone();
|
game.bind()
|
||||||
sent_player.connection_addr =
|
.find_player(self_id)
|
||||||
SocketAddr::from(([0, 0, 0, 0], 0));
|
.unwrap()
|
||||||
peer.broadcast_reliable(Package::NewPlayer(sent_player));
|
.clone()
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
|
||||||
peer.send_reliable(
|
peer.send_reliable(
|
||||||
&connection.address,
|
&connection.address,
|
||||||
@ -271,7 +297,11 @@ impl INode for NetworkManager {
|
|||||||
.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(
|
peer.broadcast_reliable(Package::PlayerLeft(
|
||||||
game.bind().find_player(player_id).unwrap().clone(),
|
game.bind()
|
||||||
|
.find_player(player_id)
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
game.bind_mut().remove_player(player_id);
|
game.bind_mut().remove_player(player_id);
|
||||||
}
|
}
|
||||||
@ -314,6 +344,40 @@ impl INode for NetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Package::MapLoaded => {
|
||||||
|
if let Some(game) = Game::singleton() {
|
||||||
|
for player in &game.bind().players {
|
||||||
|
if let Some(transform) = player.get_transform() {
|
||||||
|
peer.send_reliable(
|
||||||
|
&conn.address,
|
||||||
|
Package::SpawnPlayer(player.id, transform),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::SelfSync(dir, rot) => {
|
||||||
|
if let Some(game) = &mut Game::singleton() {
|
||||||
|
if let Some(player) = &mut game
|
||||||
|
.bind_mut()
|
||||||
|
.find_player_by_addr_mut(&conn.address)
|
||||||
|
{
|
||||||
|
player.apply_self_sync(dir, rot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Package::Jump(_) => {
|
||||||
|
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().handle_jump(player.id);
|
||||||
|
peer.broadcast_reliable(Package::Jump(player.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -424,15 +488,26 @@ impl NetworkManager {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum Package {
|
pub enum Package {
|
||||||
Hello,
|
Hello,
|
||||||
Players(Vec<Player>, u16),
|
|
||||||
|
// Meta values
|
||||||
|
Players(Vec<NetPlayer>, u16),
|
||||||
|
UpdatePings(HashMap<u16, u128>),
|
||||||
SetNick(u16, String),
|
SetNick(u16, String),
|
||||||
SetSelfNick(String),
|
SetSelfNick(String),
|
||||||
UpdatePings(HashMap<u16, u128>),
|
NewPlayer(NetPlayer),
|
||||||
NewPlayer(Player),
|
PlayerLeft(NetPlayer),
|
||||||
PlayerLeft(Player),
|
|
||||||
|
// Lobby packages
|
||||||
SetReady(u16, bool),
|
SetReady(u16, bool),
|
||||||
SelectMap(u8),
|
SelectMap(u8),
|
||||||
GameStarted,
|
GameStarted,
|
||||||
|
MapLoaded,
|
||||||
|
|
||||||
|
// Game packages
|
||||||
|
SpawnPlayer(u16, NetPlayerTransform),
|
||||||
|
Sync(HashMap<u16, (NetVector3, NetPlayerTransform)>),
|
||||||
|
SelfSync(NetVector3, NetVector3),
|
||||||
|
Jump(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PeerKind {
|
pub enum PeerKind {
|
||||||
|
|||||||
25
rust/src/net/util.rs
Normal file
25
rust/src/net/util.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use godot::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct NetVector3 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vector3> for NetVector3 {
|
||||||
|
fn from(value: Vector3) -> Self {
|
||||||
|
NetVector3 {
|
||||||
|
x: value.x,
|
||||||
|
y: value.y,
|
||||||
|
z: value.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NetVector3> for Vector3 {
|
||||||
|
fn from(value: NetVector3) -> Self {
|
||||||
|
Vector3::new(value.x, value.y, value.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
275
rust/src/player.rs
Normal file
275
rust/src/player.rs
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
use godot::{
|
||||||
|
classes::{
|
||||||
|
Camera3D, CharacterBody3D, ICharacterBody3D, Input, InputEvent, InputEventMouseMotion,
|
||||||
|
input::MouseMode,
|
||||||
|
},
|
||||||
|
obj::WithBaseField,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
net::{
|
||||||
|
network_manager::{NetworkManager, Package::Jump, PeerKind},
|
||||||
|
util::NetVector3,
|
||||||
|
},
|
||||||
|
ui::cli::CommandLinePanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct NetPlayerTransform {
|
||||||
|
pub location: NetVector3,
|
||||||
|
pub rotation: NetVector3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IPlayer {
|
||||||
|
fn get_player_id(&self) -> Option<u16>;
|
||||||
|
fn get_y_speed(&self) -> f32;
|
||||||
|
fn set_y_speed(&mut self, speed: f32);
|
||||||
|
fn get_movement_dir(&self) -> Vector3;
|
||||||
|
fn set_movement_dir(&mut self, dir: Vector3);
|
||||||
|
fn get_move_speed(&self) -> f32;
|
||||||
|
fn apply_transform(&mut self, transform: NetPlayerTransform);
|
||||||
|
fn apply_rotation(&mut self, rot: Vector3);
|
||||||
|
fn get_transform(&self) -> NetPlayerTransform;
|
||||||
|
fn jump(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PlayerMovement {
|
||||||
|
fn player_update(&mut self, delta: f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerMovement for T {
|
||||||
|
fn player_update(&mut self, delta: f64) {
|
||||||
|
self.set_y_speed(self.get_y_speed() - 9.8 * delta as f32);
|
||||||
|
if self.base().is_on_floor() && self.get_y_speed() < 0. {
|
||||||
|
self.set_y_speed(0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
let speed = Vector3::UP * self.get_y_speed()
|
||||||
|
+ self
|
||||||
|
.get_movement_dir()
|
||||||
|
.try_normalized()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.rotated(Vector3::UP, self.base().get_rotation().y)
|
||||||
|
* self.get_move_speed();
|
||||||
|
|
||||||
|
self.base_mut().set_velocity(speed);
|
||||||
|
self.base_mut().move_and_slide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GodotClass)]
|
||||||
|
#[class(base=CharacterBody3D, init)]
|
||||||
|
pub struct LocalPlayer {
|
||||||
|
#[export]
|
||||||
|
look_sensitivity: f32,
|
||||||
|
#[export]
|
||||||
|
camera: Option<Gd<Camera3D>>,
|
||||||
|
#[export]
|
||||||
|
move_speed: f32,
|
||||||
|
|
||||||
|
pub player_id: Option<u16>,
|
||||||
|
|
||||||
|
movement_dir: Vector3,
|
||||||
|
y_speed: f32,
|
||||||
|
|
||||||
|
locked: bool,
|
||||||
|
|
||||||
|
base: Base<CharacterBody3D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_dyn]
|
||||||
|
impl IPlayer for LocalPlayer {
|
||||||
|
fn get_player_id(&self) -> Option<u16> {
|
||||||
|
self.player_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_y_speed(&self) -> f32 {
|
||||||
|
self.y_speed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_y_speed(&mut self, speed: f32) {
|
||||||
|
self.y_speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_movement_dir(&self) -> Vector3 {
|
||||||
|
self.movement_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_movement_dir(&mut self, _dir: Vector3) {}
|
||||||
|
|
||||||
|
fn get_move_speed(&self) -> f32 {
|
||||||
|
self.move_speed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_transform(&mut self, transform: NetPlayerTransform) {
|
||||||
|
self.base_mut()
|
||||||
|
.set_global_position(transform.location.into());
|
||||||
|
self.base_mut()
|
||||||
|
.set_global_rotation(transform.rotation.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_rotation(&mut self, _rot: Vector3) {}
|
||||||
|
|
||||||
|
fn get_transform(&self) -> NetPlayerTransform {
|
||||||
|
NetPlayerTransform {
|
||||||
|
location: self.base().get_global_position().into(),
|
||||||
|
rotation: self.base().get_global_rotation().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump(&mut self) {
|
||||||
|
self.set_y_speed(5.);
|
||||||
|
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
|
||||||
|
match peer {
|
||||||
|
PeerKind::Client(peer, addr) => peer.send_reliable(&addr, Jump(0)),
|
||||||
|
PeerKind::Server(peer, _) => {
|
||||||
|
peer.broadcast_reliable(Jump(self.player_id.unwrap_or(0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl ICharacterBody3D for LocalPlayer {
|
||||||
|
fn ready(&mut self) {
|
||||||
|
if !CommandLinePanel::singleton().bind().opened {
|
||||||
|
self.set_lock(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLinePanel::singleton()
|
||||||
|
.signals()
|
||||||
|
.on_open()
|
||||||
|
.connect_other(self, |s| s.set_lock(false));
|
||||||
|
CommandLinePanel::singleton()
|
||||||
|
.signals()
|
||||||
|
.on_close()
|
||||||
|
.connect_other(self, |s| {
|
||||||
|
s.set_lock(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(&mut self, delta: f64) {
|
||||||
|
let input_direction = if self.locked {
|
||||||
|
let input = Input::singleton();
|
||||||
|
|
||||||
|
if self.base().is_on_floor() && input.is_action_just_pressed("jump") {
|
||||||
|
self.jump();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3::RIGHT * input.is_action_pressed("right") as u32 as f32
|
||||||
|
- Vector3::RIGHT * input.is_action_pressed("left") as u32 as f32
|
||||||
|
+ Vector3::FORWARD * input.is_action_pressed("forward") as u32 as f32
|
||||||
|
- Vector3::FORWARD * input.is_action_pressed("backward") as u32 as f32
|
||||||
|
} else {
|
||||||
|
Vector3::ZERO
|
||||||
|
};
|
||||||
|
self.movement_dir = input_direction;
|
||||||
|
|
||||||
|
self.player_update(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input(&mut self, event: Gd<InputEvent>) {
|
||||||
|
if self.locked {
|
||||||
|
if let Ok(event) = event.try_cast::<InputEventMouseMotion>() {
|
||||||
|
let x = event.get_relative().x * 0.001 * self.look_sensitivity;
|
||||||
|
let y = event.get_relative().y * 0.001 * self.look_sensitivity;
|
||||||
|
|
||||||
|
if let Some(camera) = self.camera.as_mut() {
|
||||||
|
let curr_rotation = camera.get_rotation();
|
||||||
|
let x_rot =
|
||||||
|
(curr_rotation.x - y * self.look_sensitivity).clamp(-PI / 2., PI / 2.);
|
||||||
|
camera.set_rotation(Vector3::RIGHT * x_rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
let y_rot = -x * self.look_sensitivity;
|
||||||
|
self.base_mut().rotate_y(y_rot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalPlayer {
|
||||||
|
fn set_lock(&mut self, locked: bool) {
|
||||||
|
self.locked = locked;
|
||||||
|
if self.locked {
|
||||||
|
Input::singleton().set_mouse_mode(MouseMode::CAPTURED);
|
||||||
|
} else {
|
||||||
|
Input::singleton().set_mouse_mode(MouseMode::VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GodotClass)]
|
||||||
|
#[class(base=CharacterBody3D, init)]
|
||||||
|
pub struct RemotePlayer {
|
||||||
|
#[export]
|
||||||
|
move_speed: f32,
|
||||||
|
|
||||||
|
pub player_id: Option<u16>,
|
||||||
|
|
||||||
|
movement_dir: Vector3,
|
||||||
|
y_speed: f32,
|
||||||
|
|
||||||
|
base: Base<CharacterBody3D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_dyn]
|
||||||
|
impl IPlayer for RemotePlayer {
|
||||||
|
fn get_y_speed(&self) -> f32 {
|
||||||
|
self.y_speed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_y_speed(&mut self, speed: f32) {
|
||||||
|
self.y_speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_movement_dir(&self) -> Vector3 {
|
||||||
|
self.movement_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_movement_dir(&mut self, dir: Vector3) {
|
||||||
|
self.movement_dir = dir.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_move_speed(&self) -> f32 {
|
||||||
|
self.move_speed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_player_id(&self) -> Option<u16> {
|
||||||
|
self.player_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_transform(&mut self, transform: NetPlayerTransform) {
|
||||||
|
self.base_mut()
|
||||||
|
.set_global_position(transform.location.into());
|
||||||
|
self.base_mut()
|
||||||
|
.set_global_rotation(transform.rotation.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_rotation(&mut self, rot: Vector3) {
|
||||||
|
self.base_mut().set_global_rotation(rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_transform(&self) -> NetPlayerTransform {
|
||||||
|
NetPlayerTransform {
|
||||||
|
location: self.base().get_global_position().into(),
|
||||||
|
rotation: self.base().get_global_rotation().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump(&mut self) {
|
||||||
|
self.set_y_speed(5.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl ICharacterBody3D for RemotePlayer {
|
||||||
|
fn process(&mut self, delta: f64) {
|
||||||
|
self.player_update(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -109,6 +109,11 @@ impl IPanel for CommandLinePanel {
|
|||||||
fn input(&mut self, event: Gd<InputEvent>) {
|
fn input(&mut self, event: Gd<InputEvent>) {
|
||||||
if event.is_action_pressed("toggle_cli") {
|
if event.is_action_pressed("toggle_cli") {
|
||||||
self.opened = !self.opened;
|
self.opened = !self.opened;
|
||||||
|
if self.opened {
|
||||||
|
self.signals().on_open().emit();
|
||||||
|
} else {
|
||||||
|
self.signals().on_close().emit();
|
||||||
|
}
|
||||||
if let Some(input_field) = &mut self.input_field {
|
if let Some(input_field) = &mut self.input_field {
|
||||||
if self.opened {
|
if self.opened {
|
||||||
input_field.grab_focus();
|
input_field.grab_focus();
|
||||||
@ -131,6 +136,10 @@ impl CommandLinePanel {
|
|||||||
pub fn on_join(port: String);
|
pub fn on_join(port: String);
|
||||||
#[signal]
|
#[signal]
|
||||||
pub fn on_disconnect();
|
pub fn on_disconnect();
|
||||||
|
#[signal]
|
||||||
|
pub fn on_open();
|
||||||
|
#[signal]
|
||||||
|
pub fn on_close();
|
||||||
|
|
||||||
pub fn singleton() -> Gd<CommandLinePanel> {
|
pub fn singleton() -> Gd<CommandLinePanel> {
|
||||||
get_autoload_by_name::<CanvasLayer>(CLI_GLOBAL_NAME)
|
get_autoload_by_name::<CanvasLayer>(CLI_GLOBAL_NAME)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use godot::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
game_manager::{Game, GameManager},
|
game_manager::{Game, GameManager},
|
||||||
map_resource::Map,
|
map_resource::MapResource,
|
||||||
net::network_manager::{NetworkManager, Package, PeerKind},
|
net::network_manager::{NetworkManager, Package, PeerKind},
|
||||||
ui::player_listing::PlayerListing,
|
ui::player_listing::PlayerListing,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user