Init commit
This commit is contained in:
commit
8f61ab6c6b
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
godot/assets/** filter=lfs diff=lfs merge=lfs -text
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
rust/target
|
||||
venv
|
5
Makefile
Normal file
5
Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
rust_build:
|
||||
cd rust && cargo build
|
||||
|
||||
rust_fmt:
|
||||
cd rust && cargo fmt
|
0
godot/.gdextension
Normal file
0
godot/.gdextension
Normal file
BIN
godot/assets/models/characters/model.glb
(Stored with Git LFS)
Normal file
BIN
godot/assets/models/characters/model.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
godot/assets/models/characters/model.glb.import
(Stored with Git LFS)
Normal file
BIN
godot/assets/models/characters/model.glb.import
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
godot/assets/models/maps/lowpoly_tdm_1/map.glb
(Stored with Git LFS)
Normal file
BIN
godot/assets/models/maps/lowpoly_tdm_1/map.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
godot/assets/models/maps/lowpoly_tdm_1/map.glb.import
(Stored with Git LFS)
Normal file
BIN
godot/assets/models/maps/lowpoly_tdm_1/map.glb.import
(Stored with Git LFS)
Normal file
Binary file not shown.
1
godot/icon.svg
Normal file
1
godot/icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
After Width: | Height: | Size: 994 B |
37
godot/icon.svg.import
Normal file
37
godot/icon.svg.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ds2iifncj650s"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
72
godot/project.godot
Normal file
72
godot/project.godot
Normal file
@ -0,0 +1,72 @@
|
||||
; 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=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="Killbox"
|
||||
config/version="0.1.0"
|
||||
run/main_scene="res://scenes/game_root/game_root.tscn"
|
||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[autoload]
|
||||
|
||||
logger="*res://scenes/utils/logger.gd"
|
||||
consts="*res://scenes/utils/consts.gd"
|
||||
GameServerAutoload="*res://scenes/server/server.gd"
|
||||
|
||||
[display]
|
||||
|
||||
window/size/mode=4
|
||||
|
||||
[input]
|
||||
|
||||
move_forward={
|
||||
"deadzone": 0.5,
|
||||
"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)
|
||||
]
|
||||
}
|
||||
move_backward={
|
||||
"deadzone": 0.5,
|
||||
"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)
|
||||
]
|
||||
}
|
||||
move_left={
|
||||
"deadzone": 0.5,
|
||||
"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)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.5,
|
||||
"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)
|
||||
]
|
||||
}
|
||||
shoot={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
jump={
|
||||
"deadzone": 0.5,
|
||||
"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)
|
||||
]
|
||||
}
|
||||
|
||||
[layer_names]
|
||||
|
||||
3d_render/layer_1="Static world"
|
||||
3d_render/layer_2="Player node"
|
||||
3d_render/layer_3="Characters on the server"
|
||||
3d_physics/layer_1="Static world"
|
||||
3d_navigation/layer_1="Static world"
|
||||
3d_physics/layer_2="Player node"
|
||||
3d_navigation/layer_2="Player Node"
|
||||
3d_physics/layer_3="Characters on the server"
|
||||
3d_navigation/layer_3="Characters on the server"
|
14
godot/rust_bindings.gdextension
Normal file
14
godot/rust_bindings.gdextension
Normal file
@ -0,0 +1,14 @@
|
||||
[configuration]
|
||||
entry_symbol = "gdext_rust_init"
|
||||
compatibility_minimum = 4.1
|
||||
reloadable = true
|
||||
|
||||
[libraries]
|
||||
linux.debug.x86_64 = "res://../rust/target/debug/libopen_strike_2.so"
|
||||
linux.release.x86_64 = "res://../rust/target/release/libopen_strike_2.so"
|
||||
windows.debug.x86_64 = "res://../rust/target/debug/open_strike_2.dll"
|
||||
windows.release.x86_64 = "res://../rust/target/release/open_strike_2.dll"
|
||||
macos.debug = "res://../rust/target/debug/libopen_strike_2.dylib"
|
||||
macos.release = "res://../rust/target/release/libopen_strike_2.dylib"
|
||||
macos.debug.arm64 = "res://../rust/target/debug/libopen_strike_2.dylib"
|
||||
macos.release.arm64 = "res://../rust/target/release/libopen_strike_2.dylib"
|
33
godot/scenes/characters/character_wrapper.gd
Normal file
33
godot/scenes/characters/character_wrapper.gd
Normal file
@ -0,0 +1,33 @@
|
||||
extends Node
|
||||
class_name CharacterWrapper
|
||||
|
||||
@export var die_script: GDScript = null
|
||||
var owner_placeholder: Node3D = null
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
# Characters should be always controlled by the server and synced accross client
|
||||
# The owner should be responsible for the syncronization, since this node is
|
||||
# just a dummy that is following the controller
|
||||
set_multiplayer_authority(1)
|
||||
|
||||
pass # Replace with function body.
|
||||
|
||||
# Set the owner placeholder, so the characters can send the requests to a node
|
||||
# it depends on
|
||||
func set_owner_placeholder(owner: Node3D):
|
||||
owner_placeholder = owner
|
||||
|
||||
func die():
|
||||
push_warning("TODO: Implement ragdoll kind of dying and respawn character as an object")
|
||||
queue_free()
|
||||
|
||||
|
||||
func _on_area_body_part_hit(damage: int) -> void:
|
||||
# The owner node should be aware of how to take damage, so we need to
|
||||
# pass the value.
|
||||
if owner_placeholder:
|
||||
if owner_placeholder.has_method("take_damage"):
|
||||
owner_placeholder.take_damage(damage)
|
||||
else:
|
||||
push_warning("Node doesn't know how to take the damage")
|
13
godot/scenes/characters/hit_detected.gd
Normal file
13
godot/scenes/characters/hit_detected.gd
Normal file
@ -0,0 +1,13 @@
|
||||
extends Area3D
|
||||
|
||||
@export var damage_multiplexer: float = 1.0
|
||||
@export var body_part: String = ""
|
||||
|
||||
signal body_part_hit(damage: int)
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("target")
|
||||
|
||||
func hit(base_damage: int):
|
||||
var final_damage = round(base_damage * damage_multiplexer)
|
||||
body_part_hit.emit(final_damage)
|
3031
godot/scenes/characters/y-bot/character.tscn
Normal file
3031
godot/scenes/characters/y-bot/character.tscn
Normal file
File diff suppressed because one or more lines are too long
78
godot/scenes/game_root/game_root.gd
Normal file
78
godot/scenes/game_root/game_root.gd
Normal file
@ -0,0 +1,78 @@
|
||||
extends Node
|
||||
class_name GameRoot
|
||||
|
||||
signal load_map(name: String)
|
||||
|
||||
# -- config from args
|
||||
var config_file: String = ""
|
||||
|
||||
const MAIN_MENU_SCENE := "res://scenes/menus/main/main_menu.tscn"
|
||||
|
||||
@onready var level_root: Node3D = $LevelLoader/Level
|
||||
@onready var level_spawner: MultiplayerSpawner = $LevelLoader/MultiplayerSpawner
|
||||
|
||||
func _parse_args() -> void:
|
||||
var args := Array(OS.get_cmdline_args())
|
||||
|
||||
var config_arg: String = "--config"
|
||||
if args.has(config_arg):
|
||||
var index := args.find(config_arg)
|
||||
config_file = args[index + 1]
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
_parse_args()
|
||||
|
||||
if OS.has_feature("dedicated_server") or DisplayServer.get_name() == "headless":
|
||||
var err := _start_dedicated_server()
|
||||
if err != OK:
|
||||
logger.error("couldn't start the server, err is " + str(err))
|
||||
else:
|
||||
var err := _start_game()
|
||||
if err != OK:
|
||||
logger.error("couldn't start the game, err is " + str(err))
|
||||
|
||||
func _start_dedicated_server() -> Error:
|
||||
if config_file != "":
|
||||
logger.info("reading config from the file: " + config_file)
|
||||
logger.info("starting in the dedicated server mode")
|
||||
return OK
|
||||
|
||||
|
||||
func _start_game() -> Error:
|
||||
logger.info("starting in the game mode")
|
||||
|
||||
if not ResourceLoader.exists(MAIN_MENU_SCENE):
|
||||
return ERR_DOES_NOT_EXIST
|
||||
var scene: PackedScene = ResourceLoader.load(MAIN_MENU_SCENE)
|
||||
if not scene.can_instantiate():
|
||||
return ERR_CANT_OPEN
|
||||
|
||||
var node: MainMenu = scene.instantiate()
|
||||
add_child(node)
|
||||
return OK
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _on_load_map(map_name: String) -> void:
|
||||
for c in level_root.get_children():
|
||||
level_root.remove_child(c)
|
||||
c.queue_free()
|
||||
|
||||
var path_tmpl := "res://scenes/maps/maps/%s"
|
||||
var path := path_tmpl % map_name
|
||||
|
||||
if not ResourceLoader.exists(path):
|
||||
logger.error("map " + map_name + " doesn't exist")
|
||||
|
||||
var scene: PackedScene = ResourceLoader.load(path)
|
||||
if scene.can_instantiate():
|
||||
var node: Node3D = scene.instantiate()
|
||||
logger.info("loading map: " + map_name)
|
||||
level_root.add_child(node)
|
||||
else:
|
||||
logger.error("Can't initialize")
|
||||
|
18
godot/scenes/game_root/game_root.tscn
Normal file
18
godot/scenes/game_root/game_root.tscn
Normal file
@ -0,0 +1,18 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://d3qjxw4rk4yn4"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/game_root/game_root.gd" id="1_eb14f"]
|
||||
|
||||
[node name="GameRoot" type="Node"]
|
||||
editor_description = "This node serves a starting point for the game. When the game is running as a dedicated server it's supposed to read the config file and start the server, when the game is running in a default mode, it should render the main menu"
|
||||
script = ExtResource("1_eb14f")
|
||||
|
||||
[node name="LevelLoader" type="Node" parent="."]
|
||||
editor_description = "This node is supposed to make it possible to sync the server data across all the possible players"
|
||||
|
||||
[node name="Level" type="Node3D" parent="LevelLoader"]
|
||||
|
||||
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="LevelLoader"]
|
||||
_spawnable_scenes = PackedStringArray("res://scenes/maps/maps/lowpoly_tdm_1.tscn")
|
||||
spawn_path = NodePath("../Level")
|
||||
|
||||
[connection signal="load_map" from="." to="." method="_on_load_map"]
|
21
godot/scenes/maps/base/bullet_spawner/bullet_controller.gd
Normal file
21
godot/scenes/maps/base/bullet_spawner/bullet_controller.gd
Normal file
@ -0,0 +1,21 @@
|
||||
extends Node
|
||||
class_name BulletSpawnerController
|
||||
# This script shoud be able to find the player
|
||||
|
||||
func _get_spawner() -> MultiplayerSpawner:
|
||||
return $MultiplayerSpawner
|
||||
|
||||
func _get_root() -> Node3D:
|
||||
return $Bullets
|
||||
|
||||
# -- TODO: Better bullet naming handler
|
||||
var bullet_amount: int = -2147483647
|
||||
func spawn_bullet(starting_point: Node3D, speed: int, damage: int):
|
||||
var node: Node3D = ResourceLoader.load("res://scenes/weapon/bullet.tscn").instantiate()
|
||||
node.position = starting_point.global_position
|
||||
node.transform.basis = starting_point.global_transform.basis
|
||||
node.name = str(bullet_amount)
|
||||
node.speed = speed
|
||||
node.damage = damage
|
||||
bullet_amount += 1
|
||||
_get_root().add_child(node)
|
11
godot/scenes/maps/base/bullet_spawner/bullet_spawner.tscn
Normal file
11
godot/scenes/maps/base/bullet_spawner/bullet_spawner.tscn
Normal file
@ -0,0 +1,11 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://dp1lcbwr7vaq1"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/maps/base/bullet_spawner/bullet_controller.gd" id="1_ohxtg"]
|
||||
|
||||
[node name="BulletSpawner" type="Node3D"]
|
||||
script = ExtResource("1_ohxtg")
|
||||
|
||||
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."]
|
||||
spawn_path = NodePath("../Bullets")
|
||||
|
||||
[node name="Bullets" type="Node3D" parent="."]
|
92
godot/scenes/maps/base/map_controller.gd
Normal file
92
godot/scenes/maps/base/map_controller.gd
Normal file
@ -0,0 +1,92 @@
|
||||
extends Node
|
||||
class_name MapController
|
||||
|
||||
const PLAYER_SPAWNER: String = "res://scenes/maps/base/player_spawner/player_spawner.tscn"
|
||||
const BULLET_SPAWNER: String = "res://scenes/maps/base/bullet_spawner/bullet_spawner.tscn"
|
||||
|
||||
var player_spawner: PlayerSpawnerController
|
||||
var bullet_spawner: BulletSpawnerController
|
||||
var client_node: Node3D
|
||||
|
||||
@onready var spawn_locations: SpawnController = $SpawnLocations
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
# add player spawner
|
||||
var err: Error = OK
|
||||
|
||||
err = _add_player_spawner()
|
||||
if err != OK:
|
||||
push_error("Couldn't load player spawner")
|
||||
|
||||
err = _add_bullet_spawner()
|
||||
if err != OK:
|
||||
push_error("Couldn't load bullet spawner")
|
||||
|
||||
# Adding a node that should be used by the clients
|
||||
client_node = Node3D.new()
|
||||
client_node.name = "ClientNode"
|
||||
add_child(client_node)
|
||||
# add objects spawner
|
||||
if not OS.has_feature("dedicated_server"):
|
||||
_request_spawn.rpc_id(1)
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
if multiplayer.is_server():
|
||||
pass
|
||||
#var active_players = player_spawner._get_root().get_children()
|
||||
#for n in active_players:
|
||||
#if not Server.players.has(n.owner_id):
|
||||
#_remove_player(n.owner_id)
|
||||
|
||||
@rpc("call_local", "reliable", "any_peer")
|
||||
func _request_spawn():
|
||||
var id: int = multiplayer.get_remote_sender_id()
|
||||
_spawn_player(id)
|
||||
_spawn_player_controller_node.rpc_id(id)
|
||||
|
||||
func _spawn_player(id: int):
|
||||
player_spawner.spawn_players(spawn_locations, id)
|
||||
|
||||
@rpc("call_local", "reliable", "any_peer")
|
||||
func _spawn_player_controller_node():
|
||||
var path := "res://scenes/player/player_input.tscn"
|
||||
var scene: PackedScene = ResourceLoader.load(path)
|
||||
var controller_scene: PlayerPlaceholder = player_spawner.get_player_node(multiplayer.get_unique_id())
|
||||
var player_node: PlayerInputController = scene.instantiate()
|
||||
player_node.controlled_node = controller_scene
|
||||
client_node.add_child(player_node)
|
||||
|
||||
|
||||
func _remove_player(id: int):
|
||||
player_spawner.remove_player(id)
|
||||
|
||||
func _add_player_spawner() -> Error :
|
||||
if not ResourceLoader.exists(PLAYER_SPAWNER):
|
||||
return ERR_DOES_NOT_EXIST
|
||||
var scene: PackedScene = ResourceLoader.load(PLAYER_SPAWNER)
|
||||
if not scene.can_instantiate():
|
||||
return ERR_CANT_OPEN
|
||||
|
||||
var node: Node3D = scene.instantiate()
|
||||
add_child(node)
|
||||
player_spawner = node
|
||||
return OK
|
||||
|
||||
func _add_bullet_spawner() -> Error :
|
||||
if not ResourceLoader.exists(BULLET_SPAWNER):
|
||||
return ERR_DOES_NOT_EXIST
|
||||
var scene: PackedScene = ResourceLoader.load(BULLET_SPAWNER)
|
||||
if not scene.can_instantiate():
|
||||
return ERR_CANT_OPEN
|
||||
var node: BulletSpawnerController = scene.instantiate()
|
||||
add_child(node)
|
||||
bullet_spawner = node
|
||||
return OK
|
||||
|
||||
# -- TODO: Better bullet naming handler
|
||||
var bullet_amount: int = -2147483647
|
||||
func spawn_bullet(starting_point: Node3D, speed: int, damage: int):
|
||||
bullet_spawner.spawn_bullet(starting_point, speed, damage)
|
38
godot/scenes/maps/base/player_spawner/player_spawner.gd
Normal file
38
godot/scenes/maps/base/player_spawner/player_spawner.gd
Normal file
@ -0,0 +1,38 @@
|
||||
class_name PlayerSpawnerController extends Node3D
|
||||
|
||||
|
||||
func _get_spawner() -> MultiplayerSpawner:
|
||||
return $MultiplayerSpawner
|
||||
|
||||
func _get_root() -> Node3D:
|
||||
return $Players
|
||||
|
||||
# -- Spawn a player node and sync it across all peers
|
||||
func spawn_players(spawn_location: SpawnController, id: int) -> Error:
|
||||
if multiplayer.is_server():
|
||||
var char : PlayerPlaceholder = null
|
||||
var player_data: PlayerData = GameServerAutoload.players[id]
|
||||
char = ResourceLoader.load("res://scenes/player/placeholder.tscn").instantiate()
|
||||
char.name = "PlayerPlaceholder_" + str(player_data.id)
|
||||
var new_position: Vector3 = spawn_location.get_spawner(SpawnController.Sides.BLUE)
|
||||
char.global_position = new_position
|
||||
char.owner_id = id
|
||||
_get_root().add_child(char)
|
||||
return OK
|
||||
return ERR_UNAUTHORIZED
|
||||
|
||||
func remove_player(id: int) -> Error:
|
||||
if multiplayer.is_server():
|
||||
var found_childen: Array[Node] =_get_root().get_children()
|
||||
for found_child in found_childen:
|
||||
if found_child.owner_id:
|
||||
if found_child.owner_id == id:
|
||||
found_child.queue_free()
|
||||
return OK
|
||||
return ERR_UNAUTHORIZED
|
||||
|
||||
func get_player_node(id: int) -> PlayerPlaceholder:
|
||||
var nodes: Array[Node] = _get_root().get_children().filter(func(x: PlayerPlaceholder): return x.owner_id == id)
|
||||
if nodes.size() > 0:
|
||||
return nodes[0]
|
||||
return null
|
13
godot/scenes/maps/base/player_spawner/player_spawner.tscn
Normal file
13
godot/scenes/maps/base/player_spawner/player_spawner.tscn
Normal file
@ -0,0 +1,13 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://xh710fr73bid"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/maps/base/player_spawner/player_spawner.gd" id="1_2hsyd"]
|
||||
|
||||
[node name="PlayerSpawner" type="Node3D"]
|
||||
script = ExtResource("1_2hsyd")
|
||||
|
||||
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."]
|
||||
_spawnable_scenes = PackedStringArray("res://scenes/player/placeholder.tscn")
|
||||
spawn_path = NodePath("../Players")
|
||||
spawn_limit = 100
|
||||
|
||||
[node name="Players" type="Node3D" parent="."]
|
7
godot/scenes/maps/base/single_spawn_controller.gd
Normal file
7
godot/scenes/maps/base/single_spawn_controller.gd
Normal file
@ -0,0 +1,7 @@
|
||||
extends Node3D
|
||||
|
||||
var busy: bool = false
|
||||
|
||||
func choose_spawn_location() -> Vector3:
|
||||
busy = true
|
||||
return global_position
|
49
godot/scenes/maps/base/spawn_controller.gd
Normal file
49
godot/scenes/maps/base/spawn_controller.gd
Normal file
@ -0,0 +1,49 @@
|
||||
class_name SpawnController extends Node3D
|
||||
|
||||
@export_category("SpawnController")
|
||||
|
||||
@onready var blue_spawners: Node3D = $Blue
|
||||
@onready var red_spawners: Node3D = $Red
|
||||
|
||||
const SINGLE_SPAWN_CONTROLLER_PATH = "res://scenes/maps/base/single_spawn_controller.gd"
|
||||
enum Sides {BLUE, RED, UNDEFINED}
|
||||
|
||||
@export var side: Sides = Sides.UNDEFINED
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
var single_spawn_controller: GDScript = ResourceLoader.load(SINGLE_SPAWN_CONTROLLER_PATH)
|
||||
for spawn: Node3D in blue_spawners.get_children():
|
||||
spawn.set_script(single_spawn_controller)
|
||||
for spawn: Node3D in red_spawners.get_children():
|
||||
spawn.set_script(single_spawn_controller)
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _get_available_spawn(spawn_set: Node3D) -> Node3D:
|
||||
if multiplayer.is_server():
|
||||
var spawns: Array[Node3D] = []
|
||||
for spawn: Node3D in spawn_set.get_children():
|
||||
if not spawn.busy:
|
||||
spawns.push_back(spawn)
|
||||
var random_index: int = randi_range(0, spawns.size() - 1)
|
||||
return spawns[random_index]
|
||||
return null
|
||||
|
||||
|
||||
func get_spawner(team: Sides) -> Vector3:
|
||||
match team:
|
||||
Sides.BLUE:
|
||||
var spawn := _get_available_spawn(blue_spawners)
|
||||
return spawn.choose_spawn_location()
|
||||
Sides.RED:
|
||||
print("red")
|
||||
return Vector3(0,0,0)
|
||||
_:
|
||||
return Vector3(0,0,0)
|
||||
# Get all spawners for each team
|
954
godot/scenes/maps/maps/lowpoly_tdm_1.tscn
Normal file
954
godot/scenes/maps/maps/lowpoly_tdm_1.tscn
Normal file
File diff suppressed because one or more lines are too long
65
godot/scenes/menus/main/main_menu.gd
Normal file
65
godot/scenes/menus/main/main_menu.gd
Normal file
@ -0,0 +1,65 @@
|
||||
extends Control
|
||||
class_name MainMenu
|
||||
|
||||
const MAP_DIR := "res://scenes/maps/maps/"
|
||||
|
||||
@onready var map_list: ItemList = $CreateServerMenu/MapList
|
||||
@onready var create_server_panel: VBoxContainer = $CreateServerMenu
|
||||
@onready var join_server_panel: VBoxContainer = $JoinServerMenu
|
||||
|
||||
func _get_game_root() -> GameRoot:
|
||||
var game_root: GameRoot = find_parent("GameRoot")
|
||||
return game_root
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
var map_dir := DirAccess.open(MAP_DIR)
|
||||
if map_dir:
|
||||
map_dir.list_dir_begin()
|
||||
var file_name = map_dir.get_next()
|
||||
while file_name != "":
|
||||
map_list.add_item(file_name)
|
||||
file_name = map_dir.get_next()
|
||||
GameServerAutoload.current_map = map_list.get_item_text(0)
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _on_join_pressed() -> void:
|
||||
if create_server_panel.visible:
|
||||
create_server_panel.visible = false
|
||||
join_server_panel.visible = true
|
||||
|
||||
|
||||
func _on_create_server_pressed() -> void:
|
||||
if join_server_panel.visible:
|
||||
join_server_panel.visible = false
|
||||
create_server_panel.visible = true
|
||||
|
||||
|
||||
func _on_close_join_pressed() -> void:
|
||||
join_server_panel.visible = false
|
||||
|
||||
|
||||
func _on_close_create_pressed() -> void:
|
||||
create_server_panel.visible = false
|
||||
|
||||
func _on_map_list_item_selected(index: int) -> void:
|
||||
GameServerAutoload.current_map = map_list.get_item_text(index)
|
||||
|
||||
|
||||
func _on_create_pressed() -> void:
|
||||
GameServerAutoload.create_server(false)
|
||||
_get_game_root().load_map.emit(GameServerAutoload.current_map)
|
||||
visible = false
|
||||
|
||||
|
||||
func _on_join_server_pressed() -> void:
|
||||
var ip: String = $JoinServerMenu/Host/TextEdit.text
|
||||
var port: int = $JoinServerMenu/Port/TextEdit.text.to_int()
|
||||
GameServerAutoload.join_server(ip, port)
|
||||
visible = false
|
||||
|
126
godot/scenes/menus/main/main_menu.tscn
Normal file
126
godot/scenes/menus/main/main_menu.tscn
Normal file
@ -0,0 +1,126 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://dnqi0ih2hcpym"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/menus/main/main_menu.gd" id="1_mns85"]
|
||||
|
||||
[node name="MainMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 4
|
||||
script = ExtResource("1_mns85")
|
||||
|
||||
[node name="Buttons" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 4
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_top = -21.5
|
||||
offset_right = 38.0
|
||||
offset_bottom = 21.5
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="CreateServer" type="Button" parent="Buttons"]
|
||||
layout_mode = 2
|
||||
text = "Create server"
|
||||
|
||||
[node name="Join" type="Button" parent="Buttons"]
|
||||
layout_mode = 2
|
||||
text = "Join"
|
||||
|
||||
[node name="CreateServerMenu" type="VBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 11
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Port" type="HBoxContainer" parent="CreateServerMenu"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="CreateServerMenu/Port"]
|
||||
layout_mode = 2
|
||||
text = "Port"
|
||||
|
||||
[node name="TextEdit" type="TextEdit" parent="CreateServerMenu/Port"]
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
tooltip_text = "On which port to run the server
|
||||
"
|
||||
text = "27015"
|
||||
|
||||
[node name="MapList" type="ItemList" parent="CreateServerMenu"]
|
||||
custom_minimum_size = Vector2(200, 200)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Create" type="Button" parent="CreateServerMenu"]
|
||||
layout_mode = 2
|
||||
text = "Create"
|
||||
|
||||
[node name="Close" type="Button" parent="CreateServerMenu"]
|
||||
layout_mode = 2
|
||||
text = "Close
|
||||
"
|
||||
|
||||
[node name="JoinServerMenu" type="VBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -200.0
|
||||
offset_bottom = 120.0
|
||||
grow_horizontal = 0
|
||||
|
||||
[node name="Host" type="HBoxContainer" parent="JoinServerMenu"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="JoinServerMenu/Host"]
|
||||
layout_mode = 2
|
||||
text = "Host"
|
||||
|
||||
[node name="TextEdit" type="TextEdit" parent="JoinServerMenu/Host"]
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
tooltip_text = "On which port to run the server
|
||||
"
|
||||
text = "127.0.0.1"
|
||||
|
||||
[node name="Port" type="HBoxContainer" parent="JoinServerMenu"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="JoinServerMenu/Port"]
|
||||
layout_mode = 2
|
||||
text = "Port"
|
||||
|
||||
[node name="TextEdit" type="TextEdit" parent="JoinServerMenu/Port"]
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
tooltip_text = "On which port to run the server
|
||||
"
|
||||
text = "27015"
|
||||
|
||||
[node name="Join" type="Button" parent="JoinServerMenu"]
|
||||
layout_mode = 2
|
||||
text = "Join"
|
||||
|
||||
[node name="Close" type="Button" parent="JoinServerMenu"]
|
||||
layout_mode = 2
|
||||
text = "Close
|
||||
"
|
||||
|
||||
[connection signal="pressed" from="Buttons/CreateServer" to="." method="_on_create_server_pressed"]
|
||||
[connection signal="pressed" from="Buttons/Join" to="." method="_on_join_pressed"]
|
||||
[connection signal="item_selected" from="CreateServerMenu/MapList" to="." method="_on_map_list_item_selected"]
|
||||
[connection signal="pressed" from="CreateServerMenu/Create" to="." method="_on_create_pressed"]
|
||||
[connection signal="pressed" from="CreateServerMenu/Close" to="." method="_on_close_create_pressed"]
|
||||
[connection signal="pressed" from="JoinServerMenu/Join" to="." method="_on_join_server_pressed"]
|
||||
[connection signal="pressed" from="JoinServerMenu/Close" to="." method="_on_close_join_pressed"]
|
28
godot/scenes/player/placeholder.gd
Normal file
28
godot/scenes/player/placeholder.gd
Normal file
@ -0,0 +1,28 @@
|
||||
extends CharacterBody3D
|
||||
class_name ServerNode
|
||||
|
||||
var jumping := false
|
||||
var input_direction: Vector2 = null
|
||||
var owner_id: int = 0
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not is_on_floor():
|
||||
velocity += get_gravity() * delta
|
||||
if is_on_floor() && jumping:
|
||||
velocity.y = const .
|
||||
|
||||
#if shooting:
|
||||
jumping = false
|
||||
|
||||
var direction := (transform.basis * Vector3(input_direction.x, 0, input_direction.y)).normalized()
|
||||
if is_on_floor():
|
||||
if direction:
|
||||
#first_view_legs_anim.play("Run Forward")
|
||||
velocity.x = direction.x * character_speed
|
||||
velocity.z = direction.z * character_speed
|
||||
else:
|
||||
velocity.x = move_toward(velocity.x, 0, character_speed)
|
||||
velocity.z = move_toward(velocity.z, 0, character_speed)
|
32
godot/scenes/player/placeholder.tscn
Normal file
32
godot/scenes/player/placeholder.tscn
Normal file
@ -0,0 +1,32 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://3m22sdln5238"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/player/placeholder.gd" id="1_djt0m"]
|
||||
[ext_resource type="PackedScene" uid="uid://ddwrs0so7swxn" path="res://scenes/characters/y-bot/character.tscn" id="2_a22jl"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1v0rt"]
|
||||
height = 1.8
|
||||
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_43f22"]
|
||||
properties/0/path = NodePath(".:owner_id")
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 1
|
||||
properties/1/path = NodePath(".:position")
|
||||
properties/1/spawn = true
|
||||
properties/1/replication_mode = 1
|
||||
|
||||
[node name="PlayerPlaceholder" type="PlayerPlaceholder"]
|
||||
collision_layer = 4
|
||||
collision_mask = 5
|
||||
script = ExtResource("1_djt0m")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0)
|
||||
shape = SubResource("CapsuleShape3D_1v0rt")
|
||||
|
||||
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
|
||||
replication_config = SubResource("SceneReplicationConfig_43f22")
|
||||
|
||||
[node name="BulletStartingPoint" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.76042, -0.28729)
|
||||
|
||||
[node name="Character" parent="." instance=ExtResource("2_a22jl")]
|
22
godot/scenes/player/player_input.tscn
Normal file
22
godot/scenes/player/player_input.tscn
Normal file
@ -0,0 +1,22 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://dqp551myngaey"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/player/player_input_controller.gd" id="1_5vm0e"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_uacs5"]
|
||||
|
||||
[node name="PlayerInput" type="CharacterBody3D"]
|
||||
collision_layer = 2
|
||||
collision_mask = 3
|
||||
script = ExtResource("1_5vm0e")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
shape = SubResource("CapsuleShape3D_uacs5")
|
||||
|
||||
[node name="CameraMount" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.70665, -0.197926)
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="CameraMount"]
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.70817, 0)
|
72
godot/scenes/player/player_input_controller.gd
Normal file
72
godot/scenes/player/player_input_controller.gd
Normal file
@ -0,0 +1,72 @@
|
||||
# ---------------------------------------------------------------------
|
||||
# This script is supposed to control the node that is rendered on the
|
||||
# client side, and send the changes to the server node
|
||||
# ---------------------------------------------------------------------
|
||||
extends CharacterBody3D
|
||||
class_name PlayerInputController
|
||||
|
||||
var jumping: bool = false
|
||||
var shooting: bool = false
|
||||
var moving: bool = false
|
||||
var look_dir: Vector2
|
||||
var input_direction := Vector2()
|
||||
var camera_sens: float = 0.002
|
||||
|
||||
const JUMP_VELOCITY = 4.5
|
||||
var character_speed: int = 5
|
||||
|
||||
var controlled_node: PlayerPlaceholder = null
|
||||
@export var owner_id: int = 0
|
||||
|
||||
@onready var camera_mount = $CameraMount
|
||||
func _ready() -> void:
|
||||
set_multiplayer_authority(multiplayer.get_unique_id())
|
||||
global_position = controlled_node.global_position
|
||||
rotation = controlled_node.rotation
|
||||
|
||||
func _input(event):
|
||||
if multiplayer.get_unique_id() == get_multiplayer_authority():
|
||||
if Input.is_action_just_pressed("jump"): jump()
|
||||
if Input.is_action_just_released("shoot"): shooting = false
|
||||
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
look_dir = event.relative * 1
|
||||
rotation.y -= look_dir.x * camera_sens * 1.0
|
||||
camera_mount.rotation.x = clamp(camera_mount.rotation.x - look_dir.y * camera_sens * 1.0, -1.5, 1.5)
|
||||
controlled_node.set_rotation_x.rpc_id(1, rotation.x)
|
||||
controlled_node.set_rotation_y.rpc_id(1, rotation.y)
|
||||
if Input.is_action_pressed("move_left") or Input.is_action_pressed("move_right") or Input.is_action_pressed("move_forward") or Input.is_action_pressed("move_backwards"):
|
||||
moving = true
|
||||
else:
|
||||
moving = false
|
||||
|
||||
func jump():
|
||||
jumping = true
|
||||
controlled_node.jump.rpc_id(1)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if multiplayer.get_unique_id() == get_multiplayer_authority():
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
input_direction = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
|
||||
controlled_node.set_input_direction.rpc_id(1, input_direction)
|
||||
if not is_on_floor():
|
||||
velocity += get_gravity() * delta
|
||||
if is_on_floor() && jumping:
|
||||
velocity.y = JUMP_VELOCITY
|
||||
|
||||
#if shooting:
|
||||
jumping = false
|
||||
|
||||
var direction := (transform.basis * Vector3(input_direction.x, 0, input_direction.y)).normalized()
|
||||
if is_on_floor():
|
||||
if direction:
|
||||
#first_view_legs_anim.play("Run Forward")
|
||||
velocity.x = direction.x * character_speed
|
||||
velocity.z = direction.z * character_speed
|
||||
else:
|
||||
velocity.x = move_toward(velocity.x, 0, character_speed)
|
||||
velocity.z = move_toward(velocity.z, 0, character_speed)
|
||||
#controlled_node.sync_velocity.rpc_id(1, velocity.x, velocity.y, velocity.z)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
move_and_slide()
|
12
godot/scenes/player/server_node.tscn
Normal file
12
godot/scenes/player/server_node.tscn
Normal file
@ -0,0 +1,12 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cse87uxvcvhee"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/player/placeholder.gd" id="1_cs2mj"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_82tpl"]
|
||||
|
||||
[node name="ServerNode" type="CharacterBody3D"]
|
||||
script = ExtResource("1_cs2mj")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
shape = SubResource("CapsuleShape3D_82tpl")
|
5
godot/scenes/server/player_data.gd
Normal file
5
godot/scenes/server/player_data.gd
Normal file
@ -0,0 +1,5 @@
|
||||
extends Resource
|
||||
class_name PlayerData
|
||||
|
||||
@export var id: int = 0
|
||||
@export var username: String = ""
|
69
godot/scenes/server/server.gd
Normal file
69
godot/scenes/server/server.gd
Normal file
@ -0,0 +1,69 @@
|
||||
extends Node
|
||||
class_name GameServer
|
||||
|
||||
@export var port: int = 27015
|
||||
@export var player_limit: int = 30
|
||||
@export var current_map: String = "lowpoly_tdm_2"
|
||||
|
||||
@export var current_player_data := PlayerData.new()
|
||||
|
||||
# -- This variable should store all the active players
|
||||
var players: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
multiplayer.peer_connected.connect(_on_player_connected)
|
||||
multiplayer.peer_disconnected.connect(_on_player_disconnected)
|
||||
multiplayer.connected_to_server.connect(_on_connected_to_server)
|
||||
|
||||
|
||||
func _on_player_disconnected(id) -> void:
|
||||
logger.info("player is disconnected with id: " + str(id))
|
||||
|
||||
|
||||
func _on_player_connected(id) -> void:
|
||||
if multiplayer.is_server():
|
||||
logger.info("player is connected with id: " + str(id))
|
||||
|
||||
func _on_connected_to_server() -> void:
|
||||
logger.info("connection is successful, sending info")
|
||||
register_player.rpc_id(1, multiplayer.get_unique_id(), current_player_data.username)
|
||||
|
||||
func create_server(server_only: bool = false) -> Error:
|
||||
var peer := ENetMultiplayerPeer.new()
|
||||
logger.info("starting a server the port: " + str(port))
|
||||
var err := peer.create_server(port, player_limit)
|
||||
multiplayer.multiplayer_peer = peer
|
||||
if err:
|
||||
return err
|
||||
if !server_only:
|
||||
var player_data := PlayerData.new()
|
||||
player_data.id = 1
|
||||
logger.warning("TODO: player name is not yet implemented")
|
||||
player_data.username = "dummy"
|
||||
players[1] = player_data
|
||||
return OK
|
||||
|
||||
func join_server(ip: String, port: int) -> Error:
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
logger.info("trying to connect to: " + ip + ":" + str(port))
|
||||
var err = peer.create_client(ip, port)
|
||||
if err != OK:
|
||||
return err
|
||||
multiplayer.multiplayer_peer = peer
|
||||
return OK
|
||||
|
||||
@rpc("any_peer", "reliable", "call_remote")
|
||||
func register_player(id: int, name: String):
|
||||
if multiplayer.is_server():
|
||||
logger.info("registering player: " + str(id))
|
||||
var player_data := PlayerData.new()
|
||||
player_data.id = id
|
||||
player_data.username = name
|
||||
players[multiplayer.get_remote_sender_id()] = player_data
|
||||
logger.info("player " + str(id) + " is registered")
|
||||
else:
|
||||
logger.debug("register player is supposed to be executed on the server")
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
4
godot/scenes/utils/consts.gd
Normal file
4
godot/scenes/utils/consts.gd
Normal file
@ -0,0 +1,4 @@
|
||||
extends Resource
|
||||
|
||||
const DEFAULT_JUMP_VELOCITY: float = 4.5
|
||||
const DEFAULT_CHARACTER_SPEED: float = 5.0
|
50
godot/scenes/utils/logger.gd
Normal file
50
godot/scenes/utils/logger.gd
Normal file
@ -0,0 +1,50 @@
|
||||
extends Node
|
||||
class_name Logger
|
||||
|
||||
|
||||
var thread: Thread = Thread.new()
|
||||
var mutex: Mutex = Mutex.new()
|
||||
var log_queue: Array[String] = []
|
||||
var running: bool = true
|
||||
|
||||
|
||||
func _ready():
|
||||
thread.start(log_writer)
|
||||
|
||||
func info(msg: Variant):
|
||||
mutex.lock()
|
||||
log_queue.append("[color=white][b]INFO:[/b] [/color]" + msg)
|
||||
mutex.unlock()
|
||||
|
||||
func debug(msg: Variant):
|
||||
mutex.lock()
|
||||
log_queue.append("[color=cyan][b]DEBUG:[/b] [/color]" + msg)
|
||||
mutex.unlock()
|
||||
|
||||
func warning(msg: Variant):
|
||||
mutex.lock()
|
||||
log_queue.append("[color=yellow][b]WARN:[/b] [/color]" + msg)
|
||||
push_warning(msg)
|
||||
mutex.unlock()
|
||||
|
||||
|
||||
func error(msg: Variant):
|
||||
mutex.lock()
|
||||
log_queue.append("[color=red][b]ERROR:[/b] [/color]" + msg)
|
||||
push_error(msg)
|
||||
mutex.unlock()
|
||||
|
||||
func log_writer():
|
||||
while running:
|
||||
mutex.lock()
|
||||
while log_queue.size() > 0:
|
||||
var log_message = log_queue.pop_front()
|
||||
print_rich(log_message)
|
||||
mutex.unlock()
|
||||
|
||||
# Prevent high CPU usage
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
|
||||
func _exit_tree():
|
||||
running = false
|
||||
thread.wait_to_finish()
|
6
renovate.json
Normal file
6
renovate.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
]
|
||||
}
|
265
rust/Cargo.lock
generated
Normal file
265
rust/Cargo.lock
generated
Normal file
@ -0,0 +1,265 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "gdextension-api"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8707eca996b28193b772a4a9a28a97364bb93c97e3c313542e812f2963fb93"
|
||||
|
||||
[[package]]
|
||||
name = "gensym"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
|
||||
|
||||
[[package]]
|
||||
name = "godot"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6985eb6c22a0370c40d67db1f225f62222a2e04966f76d0beb53245e745744fa"
|
||||
dependencies = [
|
||||
"godot-core",
|
||||
"godot-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-bindings"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77d85815de344b419513c9854b82c49aad45c7dd9fa4fcc10302aaf6ce6e07c7"
|
||||
dependencies = [
|
||||
"gdextension-api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-cell"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4750f0da2c2286f8a0f1e2b0aeb5adb2178251086119e1a96186c34cd8857ba1"
|
||||
|
||||
[[package]]
|
||||
name = "godot-codegen"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd9382da869c3e5f4ba9614f43157bb96f5acf3d1f8341bc25147b7aeebd49a"
|
||||
dependencies = [
|
||||
"godot-bindings",
|
||||
"heck",
|
||||
"nanoserde",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-core"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7d014ea57227d84955d9db7bba53654e5e9040f3b5a41d6aad370707cad5b7a"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"godot-bindings",
|
||||
"godot-cell",
|
||||
"godot-codegen",
|
||||
"godot-ffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-ffi"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a93bc8ea912fc629cc7c6ad889f919e99b4e7f32cd912cc65b9be2a986dc6eb"
|
||||
dependencies = [
|
||||
"gensym",
|
||||
"godot-bindings",
|
||||
"godot-codegen",
|
||||
"libc",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-macros"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66b16a49485735010c107030dc2e9fa8701fb3bf45194e34b14d1305ad5a8b4f"
|
||||
dependencies = [
|
||||
"godot-bindings",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"venial",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "nanoserde"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de9cf844ab1e25a0353525bd74cb889843a6215fa4a0d156fd446f4857a1b99"
|
||||
dependencies = [
|
||||
"nanoserde-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanoserde-derive"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e943b2c21337b7e3ec6678500687cdc741b7639ad457f234693352075c082204"
|
||||
|
||||
[[package]]
|
||||
name = "open-strike-2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"godot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "venial"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6816bc32f30bf8dd1b3adb04de8406c7bf187d2f923bd9e4c0b99365d012613f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
10
rust/Cargo.toml
Normal file
10
rust/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "open-strike-2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
godot = "0.2.3"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
1
rust/src/game_root/mod.rs
Normal file
1
rust/src/game_root/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
|
2
rust/src/globals.rs
Normal file
2
rust/src/globals.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub(crate) const DEFAULT_JUMP_VELOCITY: f32 = 4.5;
|
||||
pub(crate) const DEFAULT_CHARACTER_SPEED: f32 = 5.0;
|
10
rust/src/lib.rs
Normal file
10
rust/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use godot::prelude::*;
|
||||
struct MyExtension;
|
||||
|
||||
//mod game_root;
|
||||
mod player;
|
||||
//mod server;
|
||||
mod globals;
|
||||
|
||||
#[gdextension]
|
||||
unsafe impl ExtensionLibrary for MyExtension {}
|
1
rust/src/player/client_node.rs
Normal file
1
rust/src/player/client_node.rs
Normal file
@ -0,0 +1 @@
|
||||
|
2
rust/src/player/mod.rs
Normal file
2
rust/src/player/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod client_node;
|
||||
mod server_node;
|
119
rust/src/player/server_node.rs
Normal file
119
rust/src/player/server_node.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use godot::classes::{CharacterBody3D, ICharacterBody3D};
|
||||
use godot::obj::WithBaseField;
|
||||
use godot::prelude::*;
|
||||
|
||||
use crate::globals;
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=CharacterBody3D)]
|
||||
struct PlayerPlaceholder {
|
||||
base: Base<CharacterBody3D>,
|
||||
#[export]
|
||||
owner_id: i32,
|
||||
jumping: bool,
|
||||
input_direction: Vector2,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl ICharacterBody3D for PlayerPlaceholder {
|
||||
fn init(base: Base<CharacterBody3D>) -> Self {
|
||||
Self {
|
||||
owner_id: 0,
|
||||
base,
|
||||
jumping: false,
|
||||
input_direction: Vector2::new(0.0, 0.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn ready(&mut self) {
|
||||
if let Some(mut multiplayer) = self.base().get_multiplayer() {
|
||||
self.owner_id = multiplayer.get_unique_id();
|
||||
}
|
||||
}
|
||||
|
||||
fn physics_process(&mut self, delta: f64) {
|
||||
if !self.base().is_on_floor() {
|
||||
let new_gravity = self.base().get_gravity() * delta as f32;
|
||||
let new_velocity = self.base().get_velocity() + new_gravity;
|
||||
self.base_mut().set_velocity(new_velocity);
|
||||
}
|
||||
|
||||
if self.base().is_on_floor() && self.jumping {
|
||||
let mut new_velocity = self.base().get_velocity();
|
||||
new_velocity.y = globals::DEFAULT_JUMP_VELOCITY;
|
||||
self.base_mut().set_velocity(new_velocity);
|
||||
}
|
||||
self.jumping = false;
|
||||
|
||||
if self.base().is_on_floor() {
|
||||
let direction = self.base().get_transform().basis
|
||||
* Vector3::new(self.input_direction.x, 0.0, self.input_direction.y);
|
||||
if direction.length() > 0.0 {
|
||||
let direction = direction.normalized();
|
||||
let new_velocity = Vector3::new(
|
||||
direction.x * globals::DEFAULT_CHARACTER_SPEED,
|
||||
self.base().get_velocity().y,
|
||||
direction.z * globals::DEFAULT_CHARACTER_SPEED,
|
||||
);
|
||||
self.base_mut().set_velocity(new_velocity);
|
||||
} else {
|
||||
let moved = self
|
||||
.base()
|
||||
.get_velocity()
|
||||
.clone()
|
||||
.move_toward(Vector3::new(0.0, 0.0, 0.0), globals::DEFAULT_CHARACTER_SPEED);
|
||||
let new_velocity = Vector3::new(moved.x, self.base().get_velocity().y, moved.z);
|
||||
self.base_mut().set_velocity(new_velocity);
|
||||
}
|
||||
}
|
||||
|
||||
self.base_mut().move_and_slide();
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl PlayerPlaceholder {
|
||||
#[rpc(any_peer, call_local, unreliable)]
|
||||
fn jump(&mut self) {
|
||||
self.jumping = true
|
||||
}
|
||||
|
||||
#[rpc(any_peer, call_local, unreliable)]
|
||||
fn set_input_direction(&mut self, new_input_direction: Vector2) {
|
||||
self.input_direction = new_input_direction;
|
||||
}
|
||||
|
||||
#[rpc(any_peer, call_local, unreliable)]
|
||||
fn set_rotation_y(&mut self, y: f32) {
|
||||
let mut new_rotation = self.base().get_rotation();
|
||||
new_rotation.y = y;
|
||||
self.base_mut().set_rotation(new_rotation);
|
||||
}
|
||||
|
||||
#[rpc(any_peer, call_local, unreliable)]
|
||||
fn set_rotation_x(&mut self, x: f32) {
|
||||
let mut camera_mount = self.base().get_node_as::<Node3D>("BulletStartingPoint");
|
||||
let mut new_rotation = camera_mount.get_rotation();
|
||||
new_rotation.x = x;
|
||||
camera_mount.set_rotation(new_rotation);
|
||||
}
|
||||
|
||||
#[rpc(any_peer, call_local, unreliable_ordered)]
|
||||
fn shoot(&mut self) {
|
||||
let bullet_starting_poing = match self.base().find_child("BulletStartingPoint") {
|
||||
Some(node) => node,
|
||||
None => return,
|
||||
};
|
||||
let casted_bullet_node = bullet_starting_poing.cast::<Node3D>();
|
||||
let mut map = match self.base().find_parent("Map") {
|
||||
Some(map) => map,
|
||||
None => return,
|
||||
};
|
||||
let args = &[
|
||||
casted_bullet_node.to_variant(),
|
||||
200.to_variant(),
|
||||
50.to_variant(),
|
||||
];
|
||||
map.call("spawn_bullet", args);
|
||||
}
|
||||
}
|
80
rust/src/server/game_server.rs
Normal file
80
rust/src/server/game_server.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use godot::classes::{ENetMultiplayerPeer, ICharacterBody3D, INode, Node};
|
||||
use godot::meta::ParamType;
|
||||
use godot::obj::{NewGd, WithBaseField};
|
||||
use godot::prelude::*;
|
||||
|
||||
use crate::globals;
|
||||
|
||||
use super::player_data::PlayerData;
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Node)]
|
||||
struct GameServer {
|
||||
base: Base<Node>,
|
||||
#[export]
|
||||
port: i32,
|
||||
#[export]
|
||||
player_limit: i32,
|
||||
#[export]
|
||||
current_map: GString,
|
||||
#[export]
|
||||
current_player_data: Gd<PlayerData>,
|
||||
#[export]
|
||||
pub(crate) players: Dictionary
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl INode for GameServer {
|
||||
fn init(base: Base<Node>) -> Self {
|
||||
let players = Dictionary::new();
|
||||
Self {
|
||||
base,
|
||||
players,
|
||||
port: 27015,
|
||||
player_limit: 10,
|
||||
current_map: "lowpoly_tdm_2".into(),
|
||||
current_player_data: PlayerData::new_gd(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ready(&mut self) {
|
||||
if let Some(multiplayer) = self.base().get_multiplayer() {
|
||||
let on_connected = multiplayer.callable("peer_connected");
|
||||
self.base_mut().connect("on_player_connected", &on_connected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl GameServer {
|
||||
// Signals
|
||||
#[func]
|
||||
fn on_player_connected(&mut self, id: i32) {
|
||||
godot_print!("test");
|
||||
}
|
||||
// Main methods
|
||||
#[func]
|
||||
fn create_server(&mut self, server_only: bool) {
|
||||
let mut peer = ENetMultiplayerPeer::new_gd();
|
||||
let server = peer.create_server_ex(self.port)
|
||||
.max_clients(self.player_limit)
|
||||
.done();
|
||||
|
||||
if let Some(mut multiplayer) = self.base().get_multiplayer() {
|
||||
let id = multiplayer.get_unique_id();
|
||||
multiplayer.set_multiplayer_peer(&peer);
|
||||
let player = PlayerData::new_gd();
|
||||
self.players.set(id, player);
|
||||
}
|
||||
}
|
||||
#[func]
|
||||
fn join_server(&mut self, ip: GString, port: i32) {
|
||||
let mut peer = ENetMultiplayerPeer::new_gd();
|
||||
peer.create_client(&ip, port);
|
||||
if let Some(mut multiplayer) = self.base().get_multiplayer() {
|
||||
multiplayer.set_multiplayer_peer(&peer);
|
||||
}
|
||||
}
|
||||
}
|
2
rust/src/server/mod.rs
Normal file
2
rust/src/server/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod game_server;
|
||||
mod player_data;
|
21
rust/src/server/player_data.rs
Normal file
21
rust/src/server/player_data.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use godot::{classes::{IResource, Resource}, obj::{Base, NoBase}, prelude::godot_api};
|
||||
use godot::prelude::*;
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Resource)]
|
||||
pub(crate) struct PlayerData {
|
||||
base: Base<Resource>,
|
||||
#[export]
|
||||
pub(crate) id: i32,
|
||||
#[export]
|
||||
pub(crate) username: GString,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl IResource for PlayerData {
|
||||
fn init(base: Base<Resource>) -> Self {
|
||||
let id = 0;
|
||||
let username = "";
|
||||
Self { base, id, username: username.into() }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user