This commit is contained in:
Relintai 2023-12-25 20:36:30 +01:00
parent 48a313f79d
commit d34578c442
15 changed files with 674 additions and 0 deletions

View File

@ -0,0 +1,5 @@
# Editor cache
.godot
.import
/logs/*

View File

@ -0,0 +1,70 @@
extends KinematicBody
# ----------------------------------------------------------------------------------------- Settings
const MOVE_SPEED: int = 10
const MOTION_INTERPOLATE_SPEED: int = 20
const VELOCITY_INTERPOLATE_SPEED: int = 2
const GRAVITY: int = 10
const JUMP_IMPULSE: float = 6.0
# --------------------------------------------------------------------------------------------- Vars
onready var avatar_container: Spatial = $AvatarContainer
onready var _mesh: MeshInstance = $AvatarContainer/Mesh
var linear_velocity := Vector3()
onready var camera: Camera = $Camera
var on_floor: bool = false
# ---------------------------------------------------------------------------------------------- API
func set_color(color):
var mat = _mesh.get_surface_material(0)
if mat == null:
mat = SpatialMaterial.new()
else:
mat = mat.duplicate()
mat.set_albedo(Color(color))
_mesh.set_surface_material(0, mat)
func update_safe_body_transform():
$KinematicBody.transform = transform
# ------------------------------------------------------------------------------------ Notifications
func _ready():
## Avoid colliding with parent
$KinematicBody.add_collision_exception_with(self)
if "player_0" == name:
$KinematicBody.collision_layer = 0
$KinematicBody.collision_mask = 0
$AvatarContainer.physics_interpolation_mode = PHYSICS_INTERPOLATION_MODE_ON
# ------------------------------------------------------------------------------ Processing internal
## Computes one motion step.
func step_body(delta: float, input_direction: Vector3, is_jumping: bool):
_set_player_orientation(input_direction)
var motion: Vector3 = input_direction * MOVE_SPEED
var new_velocity: Vector3
if on_floor and linear_velocity.length() < MOVE_SPEED:
new_velocity = linear_velocity.linear_interpolate(motion, MOTION_INTERPOLATE_SPEED * delta)
if is_jumping:
new_velocity.y = new_velocity.y + JUMP_IMPULSE
else:
new_velocity = linear_velocity.linear_interpolate(motion, VELOCITY_INTERPOLATE_SPEED * delta)
new_velocity.y = new_velocity.y - GRAVITY * delta
if new_velocity.length() > 0.01:
linear_velocity = move_and_slide(new_velocity, Vector3(0, 1, 0))
on_floor = is_on_floor()
func _set_player_orientation(input_direction):
if input_direction.length_squared() < 0.01:
return
avatar_container.transform = avatar_container.transform.looking_at(input_direction, Vector3(0, 1, 0))

View File

@ -0,0 +1,52 @@
[gd_scene load_steps=8 format=2]
[ext_resource path="res://NetworkedController.gd" type="Script" id=1]
[ext_resource path="res://PlayerCamera.gd" type="Script" id=2]
[ext_resource path="res://Character.gd" type="Script" id=3]
[sub_resource type="CapsuleShape" id=4]
radius = 0.249783
height = 1.50094
[sub_resource type="CapsuleMesh" id=2]
radius = 0.25
mid_height = 1.5
[sub_resource type="SpatialMaterial" id=3]
[sub_resource type="CapsuleShape" id=5]
radius = 0.343218
height = 1.47735
[node name="Character" type="KinematicBody"]
script = ExtResource( 3 )
[node name="CollisionShape" type="CollisionShape" parent="."]
transform = Transform( 1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0 )
shape = SubResource( 4 )
[node name="AvatarContainer" type="Spatial" parent="."]
[node name="Mesh" type="MeshInstance" parent="AvatarContainer"]
transform = Transform( 1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0 )
mesh = SubResource( 2 )
skeleton = NodePath("../..")
material/0 = SubResource( 3 )
[node name="Camera" type="Camera" parent="."]
transform = Transform( -0.707107, 0.454519, -0.541675, 0, 0.766044, 0.642787, 0.707107, 0.454519, -0.541675, -4.12047, 6.30383, -4.13525 )
projection = 1
size = 15.0
near = 0.01
far = 2000.0
script = ExtResource( 2 )
[node name="NetworkedController" type="NetworkedController" parent="."]
doll_connection_stats_frame_span = 180
script = ExtResource( 1 )
[node name="KinematicBody" type="KinematicBody" parent="."]
[node name="CollisionShape" type="CollisionShape" parent="KinematicBody"]
transform = Transform( 1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0 )
shape = SubResource( 5 )

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 GodotNetworking
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,106 @@
extends Node
# ----------------------------------------------------------------------------------------- Settings
const SERVER_PORT = 2000
const MAX_PLAYERS = 5
const SERVER_IP = "127.0.0.1"
const COLORS_LIST = [
"#0ad609",
"#f0bf58",
"#1120d3",
"#704c28",
"#2af4ce"
]
# --------------------------------------------------------------------------------------------- Vars
onready var _menu: Control = $Menu
onready var _session_type_lbl: Label = $SessionTypeLbl
var _player_id_counter = 0
var _players = {}
# ------------------------------------------------------------------------------------- UI functions
func _start_server():
_menu.hide()
_session_type_lbl.text = "Server"
var peer = NetworkedMultiplayerENet.new()
peer.create_server(SERVER_PORT, MAX_PLAYERS)
get_tree().network_peer = peer
get_tree().connect("network_peer_connected", self, "_on_client_connected")
get_tree().connect("network_peer_disconnected", self, "_on_client_disconnected")
print("Server IP: ", IP.get_local_addresses())
func _start_client():
_menu.hide()
_session_type_lbl.text = "Client"
var peer = NetworkedMultiplayerENet.new()
peer.create_client(SERVER_IP, SERVER_PORT)
get_tree().network_peer = peer
# ------------------------------------------------------------------- Server only internal functions
func _on_client_connected(peer_id):
print("Connected: ", peer_id)
var new_player_id = _player_id_counter
_player_id_counter += 1
_players[new_player_id] = {"peer_id": peer_id, "player_id": new_player_id}
# Spawn player on server, for server any player is puppet (even if it's
# autoritative)
_spawn_new_player(new_player_id, peer_id)
# Spawn the player on the client
rpc_id(peer_id, "_spawn_new_player", new_player_id, peer_id)
# Tell anyone new player appeared
for player_id in _players.keys():
if _players[player_id]["peer_id"] != peer_id:
rpc_id(_players[player_id]["peer_id"], "_spawn_new_player", new_player_id, 1)
# Spawn the actual _players on this client
for player_id in _players.keys():
if player_id != new_player_id:
rpc_id(peer_id, "_spawn_new_player", player_id, 1)
func _on_client_disconnected(peer_id):
print("Disconnected: ", peer_id)
var disconnected_player_id = -1
for player_id in _players.keys():
if _players[player_id]["peer_id"] == peer_id:
disconnected_player_id = player_id
break
if disconnected_player_id == -1:
return
_players.erase(disconnected_player_id)
_remove_player(disconnected_player_id)
# Tell anyone player disappeared
for player_id in _players.keys():
rpc_id(_players[player_id]["peer_id"], "_remove_player", disconnected_player_id)
# --------------------------------------------------------------------------------- Remote functions
remote func _spawn_new_player(player_id, peer_id):
print("Spawn player id: ", player_id, ", Peer_id: ", peer_id)
print("While my peer id is: ", get_tree().multiplayer.network_peer.get_unique_id())
var character = load("res://Character.tscn").instance()
character.set_network_master(peer_id)
character.set_name("player_" + str(player_id))
get_tree().get_current_scene().add_child(character)
character.set_color(COLORS_LIST[player_id])
remote func _remove_player(player_id):
var player_node = get_tree().get_current_scene().get_node("player_" + str(player_id))
if player_node != null:
player_node.queue_free()

View File

@ -0,0 +1,107 @@
[gd_scene load_steps=10 format=2]
[ext_resource path="res://MainScene.gd" type="Script" id=1]
[ext_resource path="res://aerial_rocks_02_diff_4k.jpg" type="Texture" id=2]
[sub_resource type="ProceduralSky" id=1]
[sub_resource type="Environment" id=2]
background_mode = 2
background_sky = SubResource( 1 )
[sub_resource type="BoxShape" id=3]
extents = Vector3( 50, 0.25, 50 )
[sub_resource type="PlaneMesh" id=4]
size = Vector2( 100, 100 )
[sub_resource type="SpatialMaterial" id=5]
albedo_texture = ExtResource( 2 )
[sub_resource type="CapsuleShape" id=6]
radius = 0.25
[sub_resource type="CapsuleMesh" id=7]
radius = 0.25
[node name="Node" type="Node"]
script = ExtResource( 1 )
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource( 2 )
[node name="DirectionalLight" type="DirectionalLight" parent="."]
transform = Transform( 0.942496, -0.163932, -0.291253, 0.331754, 0.35325, 0.874731, -0.0405109, -0.921054, 0.387322, 0, 29.9334, 0 )
shadow_enabled = true
[node name="Floor" type="StaticBody" parent="."]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.03388, 0 )
[node name="CollisionShape" type="CollisionShape" parent="Floor"]
shape = SubResource( 3 )
[node name="MeshInstance" type="MeshInstance" parent="Floor"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.235271, 0 )
mesh = SubResource( 4 )
material/0 = SubResource( 5 )
[node name="SessionTypeLbl" type="Label" parent="."]
anchor_left = 0.5
anchor_right = 0.5
margin_left = -20.0
margin_right = 20.0
margin_bottom = 14.0
custom_colors/font_color = Color( 0, 0, 0, 1 )
text = "None"
align = 1
[node name="Menu" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="HBoxContainer" type="HBoxContainer" parent="Menu"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -103.0
margin_top = -7.0
margin_right = 112.0
margin_bottom = 13.0
rect_pivot_offset = Vector2( 0.5, 0.5 )
[node name="ServerButton" type="Button" parent="Menu/HBoxContainer"]
margin_right = 103.0
margin_bottom = 20.0
custom_colors/font_color = Color( 0, 1, 0.262745, 1 )
text = "Start as Server"
[node name="VSeparator" type="VSeparator" parent="Menu/HBoxContainer"]
margin_left = 107.0
margin_right = 111.0
margin_bottom = 20.0
[node name="ClientButton" type="Button" parent="Menu/HBoxContainer"]
margin_left = 115.0
margin_right = 215.0
margin_bottom = 20.0
custom_colors/font_color = Color( 0, 0.988235, 1, 1 )
text = "Start as Client"
[node name="Camera" type="Camera" parent="."]
transform = Transform( -0.707107, 0.353553, -0.612372, 0, 0.866025, 0.5, 0.707107, 0.353553, -0.612372, -71.053, 66.4778, -71.053 )
projection = 1
size = 50.0
far = 2000.0
[node name="StaticBody" type="StaticBody" parent="."]
transform = Transform( 1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, -0.772334, 2.46257 )
[node name="CollisionShape" type="CollisionShape" parent="StaticBody"]
shape = SubResource( 6 )
[node name="MeshInstance" type="MeshInstance" parent="StaticBody"]
mesh = SubResource( 7 )
[connection signal="pressed" from="Menu/HBoxContainer/ServerButton" to="." method="_start_server"]
[connection signal="pressed" from="Menu/HBoxContainer/ClientButton" to="." method="_start_client"]

View File

@ -0,0 +1 @@
extends SceneSynchronizer

View File

@ -0,0 +1,133 @@
extends NetworkedController
# Take cares to control the player and propagate the motion on the other peers
const MAX_PLAYER_DISTANCE: float = 20.0
var _position_id := -1
var _rotation_id := -1
func _ready():
# Notify the NetworkSync who is controlling parent nodes.
NetworkSync.set_node_as_controlled_by(get_parent(), self)
NetworkSync.register_variable(get_parent(), "translation")
NetworkSync.register_variable(get_parent(), "linear_velocity")
NetworkSync.register_variable(get_parent(), "on_floor")
if not get_tree().multiplayer.is_network_server():
set_physics_process(false)
func _physics_process(_delta):
"""
# Changes the character relevancy to scale the net traffic depending on the character
importance.
for character in get_tree().get_nodes_in_group("characters"):
if character != get_parent():
var delta_distance = character.get_global_transform().origin - get_parent().get_global_transform().origin
var is_far_away = delta_distance.length_squared() > (MAX_PLAYER_DISTANCE * MAX_PLAYER_DISTANCE)
set_doll_peer_active(character.get_network_master(), !is_far_away);
"""
func _collect_inputs(_delta: float, db: DataBuffer):
# Collects the player inputs.
var input_direction := Vector3()
var is_jumping: bool = false
if Input.is_action_pressed("forward"):
input_direction -= get_parent().camera.global_transform.basis.z
if Input.is_action_pressed("backward"):
input_direction += get_parent().camera.global_transform.basis.z
if Input.is_action_pressed("left"):
input_direction -= get_parent().camera.global_transform.basis.x
if Input.is_action_pressed("right"):
input_direction += get_parent().camera.global_transform.basis.x
if Input.is_action_pressed("jump"):
is_jumping = true
input_direction.y = 0
input_direction = input_direction.normalized()
db.add_bool(is_jumping)
var has_input: bool = input_direction.length_squared() > 0.0
db.add_bool(has_input)
if has_input:
db.add_normalized_vector2(Vector2(input_direction.x, input_direction.z), DataBuffer.COMPRESSION_LEVEL_3)
func _controller_process(delta: float, db: DataBuffer):
# Process the controller.
# Take the inputs
var is_jumping = db.read_bool()
var input_direction := Vector2()
var has_input = db.read_bool()
if has_input:
input_direction = db.read_normalized_vector2(DataBuffer.COMPRESSION_LEVEL_3)
var initial_transform: Vector3 = get_parent().transform.origin
# Process the character
get_parent().step_body(delta, Vector3(input_direction.x, 0.0, input_direction.y), is_jumping)
var after_transform: Vector3 = get_parent().transform.origin
var mode = "Client"
if is_server_controller():
mode = "Server"
mode = ""
#if "player_0" == get_parent().name:
# print(get_parent().name, " ", mode, ", Input: ", get_current_input_id(), " Delta: ", delta, " Dir: ", Vector3(input_direction.x, 0.0, input_direction.y), " Jump: ", is_jumping, " - Initial t ", initial_transform, " result t", after_transform)
func _count_input_size(inputs: DataBuffer) -> int:
# Count the input buffer size.
var size: int = 0
size += inputs.get_bool_size()
inputs.skip_bool()
size += inputs.get_bool_size()
if inputs.read_bool():
size += inputs.get_normalized_vector2_size(DataBuffer.COMPRESSION_LEVEL_3)
return size
func _are_inputs_different(inputs_A: DataBuffer, inputs_B: DataBuffer) -> bool:
# Compare two inputs, returns true when those are different or false when are close enough.
if inputs_A.read_bool() != inputs_B.read_bool():
return true
var inp_A_has_i = inputs_A.read_bool()
var inp_B_has_i = inputs_B.read_bool()
if inp_A_has_i != inp_B_has_i:
return true
if inp_A_has_i:
var inp_A_dir = inputs_A.read_normalized_vector2(DataBuffer.COMPRESSION_LEVEL_3)
var inp_B_dir = inputs_B.read_normalized_vector2(DataBuffer.COMPRESSION_LEVEL_3)
if (inp_A_dir - inp_B_dir).length_squared() > 0.0001:
return true
return false
func _collect_epoch_data(buffer: DataBuffer):
buffer.add_vector3(get_parent().global_transform.origin, DataBuffer.COMPRESSION_LEVEL_0)
buffer.add_vector3(get_parent().avatar_container.rotation, DataBuffer.COMPRESSION_LEVEL_2)
func _apply_epoch(_delta: float, alpha: float, past_buffer: DataBuffer, future_buffer: DataBuffer):
var past_position := past_buffer.read_vector3(DataBuffer.COMPRESSION_LEVEL_0)
var past_rotation := past_buffer.read_vector3(DataBuffer.COMPRESSION_LEVEL_2)
var future_position := future_buffer.read_vector3(DataBuffer.COMPRESSION_LEVEL_0)
var future_rotation := future_buffer.read_vector3(DataBuffer.COMPRESSION_LEVEL_2)
get_parent().global_transform.origin = lerp(past_position, future_position, alpha)
get_parent().avatar_container.rotation = lerp(past_rotation, future_rotation, alpha)

View File

@ -0,0 +1,8 @@
extends Camera
func _ready() -> void:
if is_network_master():
current = true
else:
set_process_input(false)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 MiB

View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="StreamTexture"
path.s3tc="res://.import/aerial_rocks_02_diff_4k.jpg-8512df93af913ad07cc794706f989328.s3tc.stex"
path.etc2="res://.import/aerial_rocks_02_diff_4k.jpg-8512df93af913ad07cc794706f989328.etc2.stex"
metadata={
"imported_formats": [ "s3tc", "etc2" ],
"vram_texture": true
}
[deps]
source_file="res://aerial_rocks_02_diff_4k.jpg"
dest_files=[ "res://.import/aerial_rocks_02_diff_4k.jpg-8512df93af913ad07cc794706f989328.s3tc.stex", "res://.import/aerial_rocks_02_diff_4k.jpg-8512df93af913ad07cc794706f989328.etc2.stex" ]
[params]
compress/mode=2
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=true
flags/filter=true
flags/mipmaps=true
flags/anisotropic=false
flags/srgb=1
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=false
svg/scale=1.0

View File

@ -0,0 +1,7 @@
[gd_resource type="Environment" load_steps=2 format=2]
[sub_resource type="ProceduralSky" id=1]
[resource]
background_mode = 2
background_sky = SubResource( 1 )

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.png"
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@ -0,0 +1,92 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=4
_global_script_classes=[ {
"base": "SpringArm3D",
"class": @"PlayerCamera",
"language": @"GDScript",
"path": "res://player_camera.gd"
} ]
_global_script_class_icons={
@"PlayerCamera": ""
}
[NetworkSynchronizer]
debug_doll_speedup=true
[application]
config/name="NetSyncTest3.x"
run/main_scene="res://MainScene.tscn"
config/icon="res://icon.png"
[autoload]
NetworkSync="*res://NetworkSync.gd"
[display]
window/size/width=640
window/size/height=400
window/dpi/allow_hidpi=true
window/stretch/mode="2d"
window/stretch/aspect="expand"
stretch_2d=true
[editor]
main_run_args="--server"
[gdnative]
singletons=[ ]
[input]
forward={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":87,"unicode":0,"echo":false,"script":null)
]
}
backward={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":83,"unicode":0,"echo":false,"script":null)
]
}
left={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":65,"unicode":0,"echo":false,"script":null)
]
}
right={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":68,"unicode":0,"echo":false,"script":null)
]
}
jump={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":32,"unicode":0,"echo":false,"script":null)
]
}
[layer_names]
3d_physics/layer_1="Common"
3d_physics/layer_2="Player"
[physics]
common/physics_jitter_fix=0.0
common/enable_pause_aware_picking=true
[rendering]
environment/default_environment="res://default_env.tres"