Compare commits

...

51 Commits
v0.0.2 ... main

Author SHA1 Message Date
3ea967ae59 Update README.md 2025-03-24 16:19:02 +00:00
eb22e7673f set version to 0.0.4
All checks were successful
Build Godot Project1 / godot (cadastery-server.x86_64, server) (push) Successful in 2m32s
Build Godot Project1 / godot (cadastery.exe, windows) (push) Successful in 23s
Build Godot Project1 / godot (cadastery.x86_64, linux) (push) Successful in 27s
Build Godot Project1 / godot (cadastery.zip, mac) (push) Successful in 35s
2025-02-15 19:01:06 +10:00
a604032b79 change consoler to read a file with intervals 2025-02-15 03:07:14 +10:00
f10eebd332 add message_sent signal to consoler 2025-02-15 02:14:27 +10:00
f9603e0a5a tweak consoler 2025-02-15 02:04:54 +10:00
ed41775543 add server console input 2025-02-15 02:02:05 +10:00
809da74969 update Dockerfile 2025-02-15 01:36:31 +10:00
42ef398544 add escaping bbcode tags to chat 2025-02-15 00:56:09 +10:00
1db73736c4 add mono font to chat 2025-02-15 00:40:55 +10:00
c3e906f33c add extra check for controlling player 2025-02-15 00:35:10 +10:00
fa6cf57e0c refactor networker and menus 2025-02-15 00:33:40 +10:00
df06be7542 add _ to private variables 2025-02-15 00:16:38 +10:00
2268cb995d add chat history 2025-02-15 00:07:49 +10:00
01f93fbbad add max message length and disable context menu with chat 2025-02-15 00:02:09 +10:00
293c19170d add caret blink to chat 2025-02-14 23:58:40 +10:00
65c0124861 set max name length 2025-02-14 23:51:48 +10:00
2d9933b627 add Menu class and refactor men-related cursor capturing 2025-02-14 23:49:00 +10:00
7ba087f6e1 close chat on submit 2025-02-14 23:24:19 +10:00
97c15d5b1b set version to 0.0.3
All checks were successful
Build Godot Project1 / godot (cadastery-server.x86_64, server) (push) Successful in 2m37s
Build Godot Project1 / godot (cadastery.exe, windows) (push) Successful in 24s
Build Godot Project1 / godot (cadastery.x86_64, linux) (push) Successful in 21s
Build Godot Project1 / godot (cadastery.zip, mac) (push) Successful in 38s
2025-02-14 21:35:23 +10:00
3b182431ae hide chat scrollbar when chat is not opened 2025-02-14 01:12:51 +10:00
76676f53f5 make messages scroll to bottom on new messages 2025-02-14 01:05:40 +10:00
d90c0e5ee7 add space after player name in chat message 2025-02-14 00:25:24 +10:00
bb7f8ead08 add chat 2025-02-13 23:35:52 +10:00
bb3b75929a return jumping to _physics_process 2025-02-13 23:35:49 +10:00
c57c724f0a change _input to _unhandled_input in most classes 2025-02-13 23:15:47 +10:00
26fe3ae790 add better player names to logging 2025-02-13 21:49:30 +10:00
f41c047f26 change Menu node to CanvasLayer 2025-02-13 21:30:58 +10:00
3568f927fc change server history appending 2025-02-13 21:28:29 +10:00
3d53e70e7b add title script 2025-02-13 21:23:53 +10:00
6a02e40f01 add server address history 2025-02-13 21:16:31 +10:00
030fa8bfaf fix title labels alignment 2025-02-13 20:43:33 +10:00
e1c073adcb nevermind even that doesn't work 2025-02-13 20:42:11 +10:00
904e2b2ccd simplify host registering check 2025-02-13 20:41:07 +10:00
58c35aa9c3 nevermind it doesn't work 2025-02-13 20:40:28 +10:00
2fd79f4d5f prevent creating empty player on dedicated servers, a better way 2025-02-13 20:34:57 +10:00
9aa67cf490 finally add player name displaying 2025-02-13 20:29:24 +10:00
25a3b67cd2 tweak logging 2025-02-13 20:13:34 +10:00
c1719d8dd5 add more logging 2025-02-13 20:07:20 +10:00
2867918c92 add camera effects for running and crouching 2025-02-13 19:43:21 +10:00
64ba702ac3 add controller inputs to ui actions 2025-02-13 19:16:09 +10:00
8c5cf83803 add ability to move camera slower on controller 2025-02-13 19:12:47 +10:00
0652e0d23b add running and crouching 2025-02-13 19:08:08 +10:00
669eed3a1b enable shadows 2025-02-13 18:47:48 +10:00
31e94e6408 increase MOVE_ACCELERATION 2025-02-13 18:40:17 +10:00
c5a7af2869 tweak test material 2025-02-13 18:29:54 +10:00
c3a4a70280 add handlers for PlayerSpawner's signals 2025-02-13 17:49:32 +10:00
be821a2645 return name to players 2025-02-13 17:43:30 +10:00
25196cc796 remove player_connected signal 2025-02-13 17:43:11 +10:00
facc984c36 add player holder to Main 2025-02-13 17:35:56 +10:00
1d4632a66c clear default name label 2025-02-13 17:30:16 +10:00
07cfb07cc6 add player formatting and checking for active multiplayer instance 2025-02-13 17:15:46 +10:00
22 changed files with 660 additions and 127 deletions

View File

@ -7,10 +7,7 @@ ENV GODOT_FILE_NAME="Godot_v${GODOT_VERSION}-stable_linux.x86_64"
ENV GODOT_GAME_NAME="cadastery-server"
RUN dnf update -y
RUN dnf install -y wget
RUN dnf install -y unzip
RUN dnf install -y wayland-devel
RUN dnf install -y fontconfig
RUN dnf install -y wget unzip wayland-devel fontconfig
ADD https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}-stable//${GODOT_FILE_NAME}.zip ./
RUN mkdir -p ~/.cache \

View File

@ -1 +1,3 @@
Source code for Cadastery.
If you want to clone the repo, make sure you have [Git LFS](https://git-lfs.com/) installed.

View File

@ -11,7 +11,7 @@ config_version=5
[application]
config/name="cadastery"
config/version="0.0.2"
config/version="0.0.4"
run/main_scene="res://scenes/title.tscn"
config/use_custom_user_dir=true
config/custom_user_dir_name="Cadastery"
@ -28,6 +28,7 @@ Networker="*res://scripts/globals/networker.gd"
Cursorer="*res://scripts/globals/cursorer.gd"
Referencer="*res://scripts/globals/referencer.gd"
Inputer="*res://scripts/globals/inputer.gd"
Consoler="*res://scripts/globals/consoler.gd"
[debug]
@ -40,7 +41,6 @@ gdscript/warnings/integer_division=0
window/size/viewport_width=1920
window/size/viewport_height=1080
window/size/always_on_top=true
window/size/window_width_override=1280
window/size/window_height_override=720
window/stretch/mode="canvas_items"
@ -49,7 +49,6 @@ window/stretch/aspect="expand"
[editor]
movie_writer/movie_file="E:/Projects/Games/cadastery/build/records/movie.avi"
movie_writer/fps=120
[filesystem]
@ -58,6 +57,20 @@ import/blender/enabled=false
[input]
ui_accept={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null)
]
}
ui_cancel={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null)
]
}
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)
@ -130,6 +143,28 @@ toggle_debug={
"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":96,"key_label":0,"unicode":96,"location":0,"echo":false,"script":null)
]
}
crouch={
"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":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null)
]
}
run={
"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":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":7,"pressure":0.0,"pressed":true,"script":null)
]
}
look_slow={
"deadzone": 0.5,
"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":8,"pressure":0.0,"pressed":true,"script":null)
]
}
chat={
"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":84,"key_label":0,"unicode":116,"location":0,"echo":false,"script":null)
]
}
[layer_names]
@ -144,4 +179,3 @@ common/physics_interpolation=true
[rendering]
textures/vram_compression/import_etc2_astc=true
anti_aliasing/quality/msaa_3d=2

View File

@ -4,5 +4,7 @@
[resource]
albedo_texture = ExtResource("1_f5hmr")
metallic_specular = 0.0
roughness = 0.0
uv1_triplanar = true
texture_filter = 0

View File

@ -1,6 +1,7 @@
[gd_scene load_steps=4 format=3 uid="uid://b7fc42grqckl0"]
[gd_scene load_steps=5 format=3 uid="uid://b7fc42grqckl0"]
[ext_resource type="Script" path="res://scripts/main.gd" id="1_80y7k"]
[ext_resource type="PackedScene" uid="uid://b3t5vrqb1eym2" path="res://scenes/ui/chat.tscn" id="3_8t1rc"]
[ext_resource type="Material" uid="uid://00ldcihmubqo" path="res://resources/materials/test_triplanar.tres" id="3_vk6ds"]
[sub_resource type="Environment" id="Environment_wyusq"]
@ -18,12 +19,18 @@ use_collision = true
[node name="CSGBox3D" type="CSGBox3D" parent="Geometry/CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -50, 0)
size = Vector3(10, 100, 10)
size = Vector3(40, 100, 10)
[node name="Lights" type="Node" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Lights"]
transform = Transform3D(0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, -0.75, 0.433013, 0, 0, 0)
shadow_enabled = true
shadow_blur = 2.0
[node name="UI" type="Node" parent="."]
[node name="Chat" parent="UI" instance=ExtResource("3_8t1rc")]
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_wyusq")

View File

@ -19,6 +19,9 @@ properties/1/replication_mode = 1
properties/2/path = NodePath("Camera3D:rotation")
properties/2/spawn = true
properties/2/replication_mode = 1
properties/3/path = NodePath("Camera3D:position")
properties/3/spawn = true
properties/3/replication_mode = 1
[node name="Player" type="CharacterBody3D"]
collision_layer = 2
@ -41,7 +44,6 @@ mesh = SubResource("BoxMesh_v4nai")
[node name="NameLabel" type="Label3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 2.15, 0)
text = "name"
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_j6f6x")

View File

@ -1,19 +1,15 @@
[gd_scene load_steps=2 format=3 uid="uid://bnlglddkrxuf0"]
[gd_scene load_steps=3 format=3 uid="uid://bnlglddkrxuf0"]
[ext_resource type="Script" path="res://scripts/ui/play_menu.gd" id="1_gunvo"]
[ext_resource type="Script" path="res://scripts/title.gd" id="1_l5ce6"]
[node name="Title" type="Node"]
script = ExtResource("1_l5ce6")
[node name="Menu" type="Control" parent="."]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="PlayMenu" type="CanvasLayer" parent="."]
script = ExtResource("1_gunvo")
[node name="PlayMenu" type="Panel" parent="Menu"]
layout_mode = 1
[node name="Panel" type="Panel" parent="PlayMenu"]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
@ -25,9 +21,8 @@ offset_right = 321.5
offset_bottom = 84.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_gunvo")
[node name="MarginContainer" type="MarginContainer" parent="Menu/PlayMenu"]
[node name="MarginContainer" type="MarginContainer" parent="PlayMenu/Panel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -39,53 +34,55 @@ theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="GridContainer" type="GridContainer" parent="Menu/PlayMenu/MarginContainer"]
[node name="GridContainer" type="GridContainer" parent="PlayMenu/Panel/MarginContainer"]
layout_mode = 2
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 20
columns = 3
[node name="NameLabel" type="Label" parent="Menu/PlayMenu/MarginContainer/GridContainer"]
[node name="NameLabel" type="Label" parent="PlayMenu/Panel/MarginContainer/GridContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 26
text = "Name:"
horizontal_alignment = 2
[node name="NameEdit" type="LineEdit" parent="Menu/PlayMenu/MarginContainer/GridContainer"]
[node name="NameEdit" type="LineEdit" parent="PlayMenu/Panel/MarginContainer/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
focus_neighbor_right = NodePath("../HostButton")
focus_neighbor_bottom = NodePath("../AddressEdit")
theme_override_font_sizes/font_size = 26
text = "ass"
max_length = 16
[node name="HostButton" type="Button" parent="Menu/PlayMenu/MarginContainer/GridContainer"]
[node name="HostButton" type="Button" parent="PlayMenu/Panel/MarginContainer/GridContainer"]
layout_mode = 2
focus_neighbor_left = NodePath("../NameEdit")
focus_neighbor_bottom = NodePath("../JoinButton")
theme_override_font_sizes/font_size = 26
text = "Host"
[node name="AddressLabel" type="Label" parent="Menu/PlayMenu/MarginContainer/GridContainer"]
[node name="AddressLabel" type="Label" parent="PlayMenu/Panel/MarginContainer/GridContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 26
text = "Address:"
horizontal_alignment = 2
[node name="AddressEdit" type="LineEdit" parent="Menu/PlayMenu/MarginContainer/GridContainer"]
[node name="AddressEdit" type="LineEdit" parent="PlayMenu/Panel/MarginContainer/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
focus_neighbor_top = NodePath("../NameEdit")
focus_neighbor_right = NodePath("../JoinButton")
theme_override_font_sizes/font_size = 26
text = "127.0.0.1"
[node name="JoinButton" type="Button" parent="Menu/PlayMenu/MarginContainer/GridContainer"]
[node name="JoinButton" type="Button" parent="PlayMenu/Panel/MarginContainer/GridContainer"]
layout_mode = 2
focus_neighbor_left = NodePath("../AddressEdit")
focus_neighbor_top = NodePath("../HostButton")
theme_override_font_sizes/font_size = 26
text = "Join"
[node name="ErrorLabel" type="Label" parent="Menu/PlayMenu"]
[node name="ErrorLabel" type="Label" parent="PlayMenu/Panel"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5

71
scenes/ui/chat.tscn Normal file
View File

@ -0,0 +1,71 @@
[gd_scene load_steps=2 format=3 uid="uid://b3t5vrqb1eym2"]
[ext_resource type="Script" path="res://scripts/ui/chat.gd" id="1_ulx3h"]
[node name="Chat" type="CanvasLayer"]
script = ExtResource("1_ulx3h")
[node name="MsgMargin" type="MarginContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_bottom = -70.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
theme_override_constants/margin_left = 50
theme_override_constants/margin_right = 50
[node name="MsgVBox" type="VBoxContainer" parent="MsgMargin"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 0
[node name="MsgScroll" type="ScrollContainer" parent="MsgMargin/MsgVBox"]
layout_mode = 2
size_flags_vertical = 3
follow_focus = true
horizontal_scroll_mode = 0
[node name="MsgMargin" type="MarginContainer" parent="MsgMargin/MsgVBox/MsgScroll"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 30
[node name="MsgContainer" type="VBoxContainer" parent="MsgMargin/MsgVBox/MsgScroll/MsgMargin"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
alignment = 2
[node name="EditMargin" type="MarginContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = 1010.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
theme_override_constants/margin_left = 50
theme_override_constants/margin_right = 50
theme_override_constants/margin_bottom = 20
[node name="EditVBox" type="VBoxContainer" parent="EditMargin"]
layout_mode = 2
mouse_filter = 2
[node name="MessageEdit" type="LineEdit" parent="EditMargin/EditVBox"]
layout_mode = 2
theme_override_font_sizes/font_size = 26
max_length = 256
context_menu_enabled = false
caret_blink = true

View File

@ -0,0 +1,26 @@
[gd_scene load_steps=2 format=3 uid="uid://bjrbngxkvhn7f"]
[sub_resource type="SystemFont" id="SystemFont_mq8v6"]
font_names = PackedStringArray("Monospace")
font_weight = 600
subpixel_positioning = 0
[node name="RichTextLabel" type="RichTextLabel"]
clip_contents = false
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 0.501961)
theme_override_constants/outline_size = 20
theme_override_fonts/mono_font = SubResource("SystemFont_mq8v6")
theme_override_font_sizes/bold_italics_font_size = 26
theme_override_font_sizes/italics_font_size = 26
theme_override_font_sizes/mono_font_size = 26
theme_override_font_sizes/normal_font_size = 26
theme_override_font_sizes/bold_font_size = 26
bbcode_enabled = true
text = "[b]name:[/b] lorem ipsum"
fit_content = true

View File

@ -50,7 +50,7 @@ func _process(_delta: float) -> void:
_control.queue_redraw()
func _input(event: InputEvent) -> void:
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_debug"):
mode = wrapi(mode + 1, 0, Mode.size()) as Mode
_update_visibility()
@ -112,7 +112,7 @@ func _update_visibility() -> void:
func _unproject(pos: Vector3) -> Vector2:
return Referencer.main_camera.unproject_position(pos)
return Referencer.camera.unproject_position(pos)
func _update_controls_label() -> void:
@ -196,8 +196,8 @@ func _append_event(key: String, frame: int, args: Array[Variant]) -> void:
func _draw_vector(from: Vector3, to: Vector3, color: Color) -> void:
if (
not Referencer.main_camera.is_position_in_frustum(from)
and not Referencer.main_camera.is_position_in_frustum(to)
not Referencer.camera.is_position_in_frustum(from)
and not Referencer.camera.is_position_in_frustum(to)
):
return
@ -223,7 +223,7 @@ func _draw_triangle(
func _draw_marker(pos: Vector3, radius: float, color: Color) -> void:
if not Referencer.main_camera.is_position_in_frustum(pos):
if not Referencer.camera.is_position_in_frustum(pos):
return
var x_start := _unproject(pos + (Vector3.LEFT * radius))
@ -240,7 +240,7 @@ func _draw_marker(pos: Vector3, radius: float, color: Color) -> void:
func _draw_circle(pos: Vector3, color: Color) -> void:
if not Referencer.main_camera.is_position_in_frustum(pos):
if not Referencer.camera.is_position_in_frustum(pos):
return
var point := _unproject(pos)
@ -284,7 +284,7 @@ func _on_control_draw() -> void:
_set_label_texts()
if !Referencer.main_camera:
if !Referencer.camera:
return
for v: Dictionary in _vectors_to_draw.values():

View File

@ -0,0 +1,35 @@
extends Node
signal message_sent(message: String)
const SERVER_CONSOLE_INPUT_PATH := "user://server_console_input"
const READING_INTERVAL: float = 10
var reading_timer: float = 0
func _process(delta: float) -> void:
if not Networker.is_dedicated or Referencer.chat == null:
return
reading_timer -= delta
if reading_timer > 0:
return
reading_timer = READING_INTERVAL
var file := FileAccess.open(SERVER_CONSOLE_INPUT_PATH, FileAccess.READ)
if not file:
return
var text := file.get_as_text()
if not text:
return
message_sent.emit(text)
file = FileAccess.open(SERVER_CONSOLE_INPUT_PATH, FileAccess.WRITE)
if not file:
return
file.store_string("")

View File

@ -2,10 +2,12 @@ extends Node
func _ready() -> void:
Networker.main_loaded.connect(_on_networker_main_loaded)
Referencer.main_loaded.connect(_on_referencer_main_loaded)
Referencer.menu_opened.connect(_on_referencer_menu_opened)
Referencer.menu_closed.connect(_on_referencer_menu_closed)
func _input(event: InputEvent) -> void:
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_cursor_capture"):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
@ -13,5 +15,13 @@ func _input(event: InputEvent) -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _on_networker_main_loaded() -> void:
func _on_referencer_main_loaded() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _on_referencer_menu_opened(_menu: Menu) -> void:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func _on_referencer_menu_closed(_menu: Menu) -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

View File

@ -10,14 +10,15 @@ const RIGHT_TRIGGER_SPRITE_INDEX: int = 21
const LEFT_STICK_SPRITE_INDEX: int = 23
const RIGHT_STICK_SPRITE_INDEX: int = 24
@export var prompt_grid_size: int = 5
@export var prompt_icon_size: int = 60
@export var mode: Mode = Mode.KB_MOUSE
@export var controller: ControllerType = ControllerType.XBOX:
@export var _controller: ControllerType = ControllerType.XBOX:
set(value):
_set_current_prompts(value)
controller = value
_controller = value
@export var _prompt_grid_size: int = 5
@export var _prompt_icon_size: int = 60
var _prompts_xbox := preload("res://assets/textures/ui/prompts_xbox.png")
var _prompts_sony := preload("res://assets/textures/ui/prompts_sony.png")
@ -43,7 +44,6 @@ func _input(event: InputEvent) -> void:
var event_mode := _get_event_mode(event)
if mode == Mode.KB_MOUSE and event_mode == Mode.CONTROLLER:
mode = Mode.CONTROLLER
mode_changed.emit(mode)
if mode == Mode.CONTROLLER and event_mode == Mode.KB_MOUSE:
@ -54,6 +54,10 @@ func _input(event: InputEvent) -> void:
Debugger.text("input", _get_event_prompt_current_mode(event))
func can_control_player() -> bool:
return Referencer.menu == null
func get_action_prompt(action: StringName) -> String:
var events := InputMap.action_get_events(action)
var bbcode: PackedStringArray = []
@ -128,17 +132,17 @@ func _get_prompt_sprite(index: int) -> String:
region.position.y,
region.size.x,
region.size.y,
prompt_icon_size,
prompt_icon_size,
_prompt_icon_size,
_prompt_icon_size,
_current_prompts.resource_path
]
)
func _get_prompt_sprite_region(index: int) -> Rect2i:
var corner_x: int = index % prompt_grid_size
var corner_y: int = index / prompt_grid_size
var sprite_size: int = _current_prompts.get_width() / prompt_grid_size
var corner_x: int = index % _prompt_grid_size
var corner_y: int = index / _prompt_grid_size
var sprite_size: int = _current_prompts.get_width() / _prompt_grid_size
return Rect2i(
corner_x * sprite_size,
corner_y * sprite_size,
@ -154,13 +158,13 @@ func _get_controller_type() -> void:
or controller_name.contains("sony")
or controller_name.contains("playstation")
):
controller = ControllerType.SONY
_controller = ControllerType.SONY
elif controller_name.contains("steam"):
controller = ControllerType.STEAM
_controller = ControllerType.STEAM
elif controller_name.contains("nintendo"):
controller = ControllerType.NINTENDO
_controller = ControllerType.NINTENDO
else:
controller = ControllerType.XBOX
_controller = ControllerType.XBOX
func _set_current_prompts(type: ControllerType) -> void:

View File

@ -1,16 +1,16 @@
extends Node
signal main_loaded
signal player_connected(peer_id: int, player_info: Dictionary)
signal player_registered(peer_id: int, player_info: Dictionary)
signal network_error(message: String)
const DEFAULT_PORT: int = 10567
const MAX_PEERS: int = 20
var _peer: ENetMultiplayerPeer
var players := {}
var is_dedicated: bool = false
var _peer: ENetMultiplayerPeer
var _local_player_info := {"name": ""}
var _players := {}
var _title_scene := preload("res://scenes/title.tscn")
var _main_scene := preload("res://scenes/main.tscn")
@ -27,7 +27,7 @@ func _ready() -> void:
func _process(_delta: float) -> void:
Debugger.text("_local_player_info", _local_player_info)
Debugger.text("_players", _players)
Debugger.text("players", players)
func set_local_player_info(username: String) -> void:
@ -36,15 +36,20 @@ func set_local_player_info(username: String) -> void:
func host_game(dedicated: bool = false) -> void:
print("Hosting...")
is_dedicated = dedicated
_peer = ENetMultiplayerPeer.new()
var error := _peer.create_server(DEFAULT_PORT, MAX_PEERS)
if error:
match error:
ERR_ALREADY_IN_USE:
network_error.emit("Already in use")
var message := "Already in use"
printerr(message)
network_error.emit(message)
ERR_CANT_CREATE:
network_error.emit("Cannot create server")
var message := "Cannot create server"
printerr(message)
network_error.emit(message)
return
multiplayer.set_multiplayer_peer(_peer)
@ -52,9 +57,9 @@ func host_game(dedicated: bool = false) -> void:
_load_main()
if not dedicated:
_players[1] = _local_player_info
player_connected.emit(1, _local_player_info)
_add_player(multiplayer.get_unique_id())
players[1] = _local_player_info
print("Registered player ", 1, " ", _local_player_info)
_add_player(multiplayer.get_unique_id(), _local_player_info)
func join_game(address: String) -> void:
@ -65,9 +70,13 @@ func join_game(address: String) -> void:
if error:
match error:
ERR_ALREADY_IN_USE:
network_error.emit("Already in use")
var message := "Already in use"
printerr(message)
network_error.emit(message)
ERR_CANT_CREATE:
network_error.emit("Cannot create server")
var message := "Cannot create client"
printerr(message)
network_error.emit(message)
return
multiplayer.set_multiplayer_peer(_peer)
@ -75,7 +84,25 @@ func join_game(address: String) -> void:
_load_main()
func _switch_scene(from: Node, to: PackedScene) -> void:
func is_active() -> bool:
return (
multiplayer.multiplayer_peer.get_connection_status()
== MultiplayerPeer.CONNECTION_CONNECTED
)
func is_dedicated_server() -> bool:
return OS.has_feature("dedicated_server") or DisplayServer.get_name() == "headless"
func player_name(peer_id: int) -> String:
if peer_id in players:
return str(peer_id) + " (" + players[peer_id]["name"] + ")"
return str(peer_id)
func _switch_scene(to: PackedScene, from: Node) -> void:
if from:
from.queue_free()
@ -86,71 +113,71 @@ func _switch_scene(from: Node, to: PackedScene) -> void:
func _load_main() -> void:
print("Loading Main...")
_switch_scene($/root/Title, _main_scene)
main_loaded.emit()
_switch_scene(_main_scene, $/root/Title)
func _add_player(peer_id: int) -> void:
func _add_player(peer_id: int, player_info: Dictionary) -> void:
if not multiplayer.is_server():
return
print("Adding player ", peer_id)
print("Adding player ", player_name(peer_id))
var player := _player_scene.instantiate() as Player
player.name = str(peer_id)
var player_holder: Node = $/root/Main/Players
player_holder.add_child(player, true)
Referencer.main.player_holder.add_child(player, true)
player.set_info(player_info)
func _remove_player(peer_id: int) -> void:
if not multiplayer.is_server():
return
print("Removing player ", peer_id)
var player_holder: Node = $/root/Main/Players
var player := player_holder.get_node_or_null(str(peer_id))
print("Removing player ", player_name(peer_id))
var player := Referencer.main.player_holder.get_node_or_null(str(peer_id))
if player:
player.queue_free()
@rpc("any_peer", "reliable")
func _register_player(player_info: Dictionary) -> void:
print("Registering player ", player_info)
var peer_id := multiplayer.get_remote_sender_id()
print("Registering player ", peer_id, " ", player_info)
if peer_id == 1 and !player_info["name"]:
print("Registered player's id is ",peer_id, " and name is empty, so skipping")
print("Skipping registering the host")
return
_players[peer_id] = player_info
player_connected.emit(peer_id, player_info)
players[peer_id] = player_info
player_registered.emit(peer_id, player_info)
func _on_multiplayer_peer_connected(peer_id: int) -> void:
print("Peer connected ", peer_id)
_add_player(peer_id)
_register_player.rpc_id(peer_id, _local_player_info)
_add_player(peer_id, _local_player_info)
func _on_multiplayer_peer_disconnected(peer_id: int) -> void:
print("Peer disconnected ", peer_id)
_players.erase(peer_id)
print("Peer disconnected ", player_name(peer_id))
_remove_player(peer_id)
players.erase(peer_id)
func _on_multiplayer_connected_to_server() -> void:
print("Connected to server")
var peer_id := multiplayer.get_unique_id()
_players[peer_id] = _local_player_info
player_connected.emit(peer_id, _local_player_info)
print("Connected to server as ", peer_id)
players[peer_id] = _local_player_info
print("Registered player ", peer_id, " ", _local_player_info)
func _on_multiplayer_connection_failed() -> void:
printerr("Connection failed")
multiplayer.set_multiplayer_peer(null)
network_error.emit("Connection failed")
var message := "Connection failed"
printerr(message)
network_error.emit(message)
func _on_multiplayer_server_disconnected() -> void:
printerr("Server disconnected")
_players.clear()
_switch_scene($/root/Main, _title_scene)
network_error.emit("Server disconnected")
var message := "Server disconnected"
printerr(message)
players.clear()
_switch_scene(_title_scene, $/root/Main)
network_error.emit(message)

View File

@ -1,4 +1,23 @@
extends Node
var main_camera: Camera3D
signal main_loaded
signal menu_opened(menu_node: Menu)
signal menu_closed(menu_node: Menu)
var camera: Camera3D
var player: Player
var menu: Menu
var main: Main:
set(value):
if value:
main_loaded.emit()
main = value
var chat: Chat
func set_menu_opened(menu_node: Menu) -> void:
menu_opened.emit(menu_node)
func set_menu_closed(menu_node: Menu) -> void:
menu_closed.emit(menu_node)

View File

@ -4,6 +4,10 @@ const SETTING_USAGE_FLAG := PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_EDIT
const CONFIG_PATH := "user://settings.cfg"
@export_group("Gameplay")
@export_subgroup("Camera")
@export var camera_fov: float = 75:
set(value):
camera_fov = value
@export_group("Video")
@export_subgroup("Display")

View File

@ -1,6 +1,37 @@
class_name Main
extends Node
@onready var player_holder: Node = $Players
@onready var _player_spawner: MultiplayerSpawner = $PlayerSpawner
func _ready() -> void:
_player_spawner.spawned.connect(_on_player_spawner_spawned)
_player_spawner.despawned.connect(_on_player_spawner_despawned)
Referencer.main = self
func _exit_tree() -> void:
Referencer.main = null
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("menu"):
if event.is_action_pressed("menu") and Inputer.can_control_player():
get_tree().quit()
func _on_player_spawner_spawned(node: Node) -> void:
if node is not Player:
return
var player := node as Player
print("Spawned player ", Networker.player_name(int(str(player.name))))
func _on_player_spawner_despawned(node: Node) -> void:
if node is not Player:
return
var player := node as Player
print("Despawned player ", Networker.player_name(int(str(player.name))))

View File

@ -2,48 +2,63 @@ class_name Player
extends CharacterBody3D
const MOVE_SPEED: float = 5
const MOVE_ACCELERATION: float = 25
const RUN_SPEED: float = 10
const CROUCH_SPEED: float = 1
const MOVE_ACCELERATION: float = 50
const MOVE_DECELERATION: float = 25
const JUMP_FORCE: float = 10
const FALL_SPEED: float = 20
const FALL_ACCELERATION: float = 25
const CAMERA_CROUCH_HEIGHT: float = 1
const CAMERA_HEIGHT_SPEED: float = 5
const CAMERA_RUN_EXTRA_FOV: float = 5
const CAMERA_FOV_SPEED: float = 50
@export var _respawn_height: float = -100
var _peer_id: int = 1
var _respawn_point: Vector3
var _default_camera_height: float
var _crouch: bool = false
var _run: bool = false
@onready var _camera: Camera3D = $Camera3D
@onready var _name_label: Label3D = $NameLabel
func _enter_tree() -> void:
set_multiplayer_authority(str(name).to_int())
_peer_id = str(name).to_int()
print("Player ", Networker.player_name(_peer_id), " enter tree")
set_multiplayer_authority(_peer_id)
func _ready() -> void:
_name_label.text = str(name)
Networker.player_registered.connect(_on_networker_player_registered)
if not is_multiplayer_authority():
return
_default_camera_height = _camera.position.y
_respawn_point = global_position
_camera.make_current()
Referencer.player = self
Referencer.main_camera = _camera
print("Player ", name, " ready")
Referencer.camera = _camera
print("Player ", Networker.player_name(_peer_id), " ready authority")
func _exit_tree() -> void:
if not is_multiplayer_authority():
print("Player ", Networker.player_name(_peer_id), " exit tree")
if Networker.is_active() and not is_multiplayer_authority():
return
Referencer.player = null
Referencer.main_camera = null
print("Player ", name, " exiting tree")
Referencer.camera = null
func _process(delta: float) -> void:
if not is_multiplayer_authority():
if Networker.is_active() and not is_multiplayer_authority():
Debugger.vector(
"look" + str(name),
_camera.global_position,
@ -54,6 +69,7 @@ func _process(delta: float) -> void:
_process_respawning()
_process_controller_rotating(delta)
_process_camera(delta)
func _physics_process(delta: float) -> void:
@ -63,7 +79,7 @@ func _physics_process(delta: float) -> void:
_lateral_movement(delta)
_vertical_movement(delta)
_jumping(delta)
_jumping()
move_and_slide()
@ -72,9 +88,28 @@ func _unhandled_input(event: InputEvent) -> void:
if not is_multiplayer_authority():
return
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
if event is InputEventMouseMotion:
_handle_mouse_rotating(event as InputEventMouseMotion)
if (
not Inputer.can_control_player()
or Input.mouse_mode != Input.MOUSE_MODE_CAPTURED
):
return
if event is InputEventMouseMotion:
_handle_mouse_rotating(event as InputEventMouseMotion)
if event.is_action_pressed("crouch"):
_crouch = true
if event.is_action_released("crouch"):
_crouch = false
if event.is_action_pressed("run"):
_run = true
if event.is_action_released("run"):
_run = false
func set_info(player_info: Dictionary) -> void:
_name_label.text = player_info["name"]
func _process_respawning() -> void:
@ -94,36 +129,62 @@ func _process_controller_rotating(delta: float) -> void:
)
)
var look_slow: float = 0.25 if Input.is_action_pressed("look_slow") else 1.0
rotate_y(
(
-controller_raw_input.x
* deg_to_rad(Settings.controller_sensitivity_horizontal)
* look_slow
* delta
* (-1 if Settings.invert_controller_horizontal else 1)
* (-1.0 if Settings.invert_controller_horizontal else 1.0)
)
)
_camera.rotate_x(
(
-controller_raw_input.y
* deg_to_rad(Settings.controller_sensitivity_vertical)
* look_slow
* delta
* (-1 if Settings.invert_controller_vertical else 1)
* (-1.0 if Settings.invert_controller_vertical else 1.0)
)
)
_camera.rotation.x = clampf(_camera.rotation.x, PI / -2, PI / 2)
func _process_camera(delta: float) -> void:
var camera_height := _default_camera_height
if _crouch:
camera_height = CAMERA_CROUCH_HEIGHT
_camera.position.y = move_toward(
_camera.position.y, camera_height, CAMERA_HEIGHT_SPEED * delta
)
var camera_fov := Settings.camera_fov
if _run:
camera_fov += CAMERA_RUN_EXTRA_FOV
_camera.fov = move_toward(_camera.fov, camera_fov, CAMERA_FOV_SPEED * delta)
func _lateral_movement(delta: float) -> void:
var input_dir := Input.get_vector(
"move_left", "move_right", "move_forward", "move_back"
)
var has_input := input_dir.length() > 0
var has_input := input_dir.length() > 0 and Inputer.can_control_player()
if has_input:
var direction := (
(transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
)
var new_velocity := direction * MOVE_SPEED
var speed := MOVE_SPEED
Debugger.text("move_mode", "move", 2)
if _crouch:
speed = CROUCH_SPEED
Debugger.text("move_mode", "crouch", 2)
if _run:
speed = RUN_SPEED
Debugger.text("move_mode", "run", 2)
var new_velocity := direction * speed
new_velocity.y = velocity.y
velocity = velocity.move_toward(new_velocity, MOVE_ACCELERATION * delta)
else:
@ -141,8 +202,12 @@ func _vertical_movement(delta: float) -> void:
velocity.y = 0
func _jumping(_delta: float) -> void:
if Input.is_action_just_pressed("jump") and is_on_floor():
func _jumping() -> void:
if (
Inputer.can_control_player()
and Input.is_action_just_pressed("jump")
and is_on_floor()
):
velocity.y = JUMP_FORCE
@ -165,3 +230,9 @@ func _handle_mouse_rotating(event: InputEventMouseMotion) -> void:
)
)
_camera.rotation.x = clamp(_camera.rotation.x, -PI / 2, PI / 2)
func _on_networker_player_registered(peer_id: int, player_info: Dictionary) -> void:
if peer_id != _peer_id:
return
set_info(player_info)

13
scripts/title.gd Normal file
View File

@ -0,0 +1,13 @@
extends Node
@onready var _play_menu: PlayMenu = $PlayMenu
func _ready() -> void:
print("Cadastery v", ProjectSettings.get_setting("application/config/version"))
if Networker.is_dedicated_server():
Networker.call_deferred("host_game", true)
return
_play_menu.open()

124
scripts/ui/chat.gd Normal file
View File

@ -0,0 +1,124 @@
class_name Chat
extends Menu
const BBCODE_TAGS_TO_ESCAPE: PackedStringArray = [
"url", "img", "font", "dropcap", "opentype_features", "outline_size"
]
var _chat_message_scene := preload("res://scenes/ui/chat_message.tscn")
var _message_history: PackedStringArray = []
var _history_position: int = 0
@onready var _msg_scroll: ScrollContainer = $MsgMargin/MsgVBox/MsgScroll
@onready
var _msg_container: Container = $MsgMargin/MsgVBox/MsgScroll/MsgMargin/MsgContainer
@onready var _msg_margin: Container = $MsgMargin/MsgVBox/MsgScroll/MsgMargin
@onready var _message_edit: LineEdit = $EditMargin/EditVBox/MessageEdit
func _ready() -> void:
_message_edit.text_submitted.connect(_on_message_edit_text_submitted)
_message_edit.visible = false
_msg_margin.minimum_size_changed.connect(_on_message_margin_minimum_size_changed)
Consoler.message_sent.connect(_on_consoler_message_sent)
Referencer.chat = self
func _exit_tree() -> void:
Referencer.chat = null
func _input(event: InputEvent) -> void:
if event.is_action_pressed("menu"):
call_deferred("close")
if _message_edit.has_focus():
if (
event.is_action_pressed("ui_up")
and _history_position + 1 <= _message_history.size()
):
_history_position += 1
_set_message_from_history()
if event.is_action_pressed("ui_down") and _history_position - 1 >= 0:
_history_position -= 1
_set_message_from_history()
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("chat") and not is_open:
open()
func open() -> void:
super.open()
_message_edit.visible = true
_message_edit.grab_focus()
_msg_scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
func close() -> void:
super.close()
_message_edit.release_focus()
_message_edit.visible = false
_msg_scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER
@rpc("any_peer", "call_local", "reliable", 1)
func _send_message(message: String, server: bool = false) -> void:
var player_name := ""
if not server:
player_name = (
Networker.players[multiplayer.get_remote_sender_id()]["name"] as String
)
var message_node := _make_message_node(message, player_name)
_msg_container.add_child(message_node)
print(player_name + ": " + message)
func _make_message_node(message: String, player_name: String = "") -> RichTextLabel:
var node := _chat_message_scene.instantiate() as RichTextLabel
if player_name:
node.text = "[b]" + player_name + ":[/b] " + _escape_bbcode_tags(message)
else:
node.text = message
return node
func _set_message_from_history() -> void:
if _history_position > 0:
var message := _message_history[-_history_position]
_message_edit.text = message
else:
_message_edit.text = ""
func _escape_bbcode_tags(message: String) -> String:
for tag_name in BBCODE_TAGS_TO_ESCAPE:
var tag := "[" + tag_name
if tag in message:
message = message.replace(tag, "[lb]" + tag_name)
return message
func _on_message_edit_text_submitted(message: String) -> void:
if _message_edit.text == "":
return
_message_edit.text = ""
if not (message in _message_history):
_message_history.append(message)
_history_position = 0
close()
_send_message.rpc(message, false)
func _on_message_margin_minimum_size_changed() -> void:
_msg_scroll.set_deferred(
"scroll_vertical", int(_msg_scroll.get_v_scroll_bar().max_value) + 100
)
func _on_consoler_message_sent(message: String) -> void:
_send_message.rpc(message, true)

16
scripts/ui/menu.gd Normal file
View File

@ -0,0 +1,16 @@
class_name Menu
extends CanvasLayer
var is_open: bool = false
func open() -> void:
is_open = true
Referencer.menu = self
Referencer.set_menu_opened(self)
func close() -> void:
is_open = false
Referencer.menu = null
Referencer.set_menu_closed(self)

View File

@ -1,17 +1,20 @@
extends Panel
class_name PlayMenu
extends Menu
@onready var _name_edit: LineEdit = $MarginContainer/GridContainer/NameEdit
@onready var _address_edit: LineEdit = $MarginContainer/GridContainer/AddressEdit
@onready var _host_button: Button = $MarginContainer/GridContainer/HostButton
@onready var _join_button: Button = $MarginContainer/GridContainer/JoinButton
@onready var _error_label: Label = $ErrorLabel
const SERVER_HISTORY_PATH := "user://server_history"
var _server_history: PackedStringArray = []
var _history_position: int = 0
@onready var _name_edit: LineEdit = $Panel/MarginContainer/GridContainer/NameEdit
@onready var _address_edit: LineEdit = $Panel/MarginContainer/GridContainer/AddressEdit
@onready var _host_button: Button = $Panel/MarginContainer/GridContainer/HostButton
@onready var _join_button: Button = $Panel/MarginContainer/GridContainer/JoinButton
@onready var _error_label: Label = $Panel/ErrorLabel
func _ready() -> void:
print("Cadastery v", ProjectSettings.get_setting("application/config/version"))
if OS.has_feature("dedicated_server") or DisplayServer.get_name() == "headless":
Networker.call_deferred("host_game", true)
if Networker.is_dedicated_server():
return
_host_button.pressed.connect(_on_host_button_pressed)
@ -20,6 +23,32 @@ func _ready() -> void:
_error_label.text = ""
_name_edit.grab_focus()
var file := FileAccess.open(SERVER_HISTORY_PATH, FileAccess.READ)
if file:
_server_history = file.get_as_text().strip_edges().split("\n")
func _input(event: InputEvent) -> void:
if _address_edit.has_focus():
if (
event.is_action_pressed("ui_up")
and _history_position + 1 <= _server_history.size()
):
_history_position += 1
_set_address_from_history()
if event.is_action_pressed("ui_down") and _history_position - 1 >= 0:
_history_position -= 1
_set_address_from_history()
func _set_address_from_history() -> void:
if _history_position > 0:
var address := _server_history[-_history_position]
_address_edit.text = address
else:
_address_edit.text = ""
func _display_error(message: String) -> void:
_error_label.text = message
@ -42,8 +71,20 @@ func _on_join_button_pressed() -> void:
_join_button.disabled = true
_host_button.disabled = true
var address := _address_edit.text
if not (address in _server_history):
_server_history.append(address)
var file := FileAccess.open(SERVER_HISTORY_PATH, FileAccess.READ_WRITE)
if file:
file.seek_end()
file.store_line(address)
else:
file = FileAccess.open(SERVER_HISTORY_PATH, FileAccess.WRITE)
file.store_line(address)
Networker.set_local_player_info(_name_edit.text)
Networker.join_game(_address_edit.text)
Networker.join_game(address)
func _on_networker_network_error(message: String) -> void: