Added serving of the customers, npc exits, main menu, settings, saving and loading.

This commit is contained in:
Patrick 2024-12-20 19:51:46 +01:00
parent 3d1e6caa29
commit 2b6d1faadb
19 changed files with 360 additions and 39 deletions

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=5 format=3 uid="uid://cvnjpnvchvakj"] [gd_scene load_steps=6 format=3 uid="uid://cvnjpnvchvakj"]
[ext_resource type="Script" path="res://scripts/entities/npc.gd" id="1_jvyx6"] [ext_resource type="Script" path="res://scripts/entities/npc.gd" id="1_jvyx6"]
@ -10,6 +10,9 @@ material = SubResource("StandardMaterial3D_e5nrh")
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_am313"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_am313"]
[sub_resource type="BoxShape3D" id="BoxShape3D_wyrst"]
size = Vector3(3, 2, 3)
[node name="Npc" type="CharacterBody3D"] [node name="Npc" type="CharacterBody3D"]
script = ExtResource("1_jvyx6") script = ExtResource("1_jvyx6")
@ -30,3 +33,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0)
pixel_size = 0.01 pixel_size = 0.01
billboard = 1 billboard = 1
no_depth_test = true no_depth_test = true
[node name="Area3D" type="Area3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="Area3D"]
shape = SubResource("BoxShape3D_wyrst")

View file

@ -1,10 +1,11 @@
[gd_scene load_steps=10 format=3 uid="uid://cbyee7drds7qu"] [gd_scene load_steps=11 format=3 uid="uid://cbyee7drds7qu"]
[ext_resource type="Script" path="res://scripts/maps/map.gd" id="1_4npcs"] [ext_resource type="Script" path="res://scripts/maps/map.gd" id="1_4npcs"]
[ext_resource type="Script" path="res://scripts/maps/objectives/hungry.gd" id="2_qrp84"] [ext_resource type="Script" path="res://scripts/maps/objectives/hungry.gd" id="2_qrp84"]
[ext_resource type="PackedScene" uid="uid://bsghm187n6ykx" path="res://scenes/objects/closet.tscn" id="2_yvpvm"] [ext_resource type="PackedScene" uid="uid://bsghm187n6ykx" path="res://scenes/objects/closet.tscn" id="2_yvpvm"]
[ext_resource type="PackedScene" uid="uid://cvnjpnvchvakj" path="res://scenes/entities/npc.tscn" id="3_x3gyc"] [ext_resource type="PackedScene" uid="uid://cvnjpnvchvakj" path="res://scenes/entities/npc.tscn" id="3_x3gyc"]
[ext_resource type="PackedScene" uid="uid://ctpubhh2gsccb" path="res://scenes/maps/elements/guncase.tscn" id="5_k7k8h"] [ext_resource type="PackedScene" uid="uid://ctpubhh2gsccb" path="res://scenes/maps/elements/guncase.tscn" id="5_k7k8h"]
[ext_resource type="PackedScene" uid="uid://bghvogpaly2a6" path="res://scenes/objects/food/burger.tscn" id="6_rpxne"]
[sub_resource type="Environment" id="Environment_pq0iv"] [sub_resource type="Environment" id="Environment_pq0iv"]
@ -50,6 +51,9 @@ script = ExtResource("2_qrp84")
[node name="Closet" parent="NPCCarryables" instance=ExtResource("2_yvpvm")] [node name="Closet" parent="NPCCarryables" instance=ExtResource("2_yvpvm")]
transform = Transform3D(0.642788, -0.766044, 0, 0.766044, 0.642788, 0, 0, 0, 1, 29, 10, -30) transform = Transform3D(0.642788, -0.766044, 0, 0.766044, 0.642788, 0, 0, 0, 1, 29, 10, -30)
[node name="NPCExit" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 44)
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(-0.899519, 0.375, 0.224144, -0.224144, -0.836516, 0.5, 0.375, 0.399519, 0.836516, 0, 0, 0) transform = Transform3D(-0.899519, 0.375, 0.224144, -0.224144, -0.836516, 0.5, 0.375, 0.399519, 0.836516, 0, 0, 0)
@ -75,3 +79,6 @@ shape = SubResource("BoxShape3D_rnmx0")
[node name="Guncase" parent="." instance=ExtResource("5_k7k8h")] [node name="Guncase" parent="." instance=ExtResource("5_k7k8h")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 24, 1, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 24, 1, 0)
[node name="Burger" parent="." instance=ExtResource("6_rpxne")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.99429, 3.5, 15.1689)

View file

@ -0,0 +1,40 @@
[gd_scene load_steps=7 format=3 uid="uid://bghvogpaly2a6"]
[ext_resource type="Script" path="res://scripts/player_interactables/food/burger.gd" id="1_nd3af"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ntjpg"]
albedo_color = Color(0.765272, 0.371996, 0, 1)
[sub_resource type="BoxMesh" id="BoxMesh_366d1"]
material = SubResource("StandardMaterial3D_ntjpg")
size = Vector3(0.2, 0.05, 0.2)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_6edt3"]
albedo_color = Color(0.432741, 0.166278, 0, 1)
[sub_resource type="BoxMesh" id="BoxMesh_7qoyo"]
material = SubResource("StandardMaterial3D_6edt3")
size = Vector3(0.2, 0.05, 0.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_cksw3"]
size = Vector3(0.2, 0.15, 0.2)
[node name="Burger" type="RigidBody3D"]
mass = 0.5
script = ExtResource("1_nd3af")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.05, 0)
mesh = SubResource("BoxMesh_366d1")
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_7qoyo")
skeleton = NodePath("../MeshInstance3D")
[node name="MeshInstance3D3" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
mesh = SubResource("BoxMesh_366d1")
skeleton = NodePath("../MeshInstance3D2")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("BoxShape3D_cksw3")

41
scenes/ui/main_menu.tscn Normal file
View file

@ -0,0 +1,41 @@
[gd_scene load_steps=2 format=3 uid="uid://dca627msg6a0i"]
[ext_resource type="Script" path="res://scripts/ui/main_menu.gd" id="1_ffbt7"]
[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
script = ExtResource("1_ffbt7")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
[node name="Title" type="Label" parent="VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 48
text = "Hotel Madness"
[node name="Join" type="Button" parent="VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Create/Join Room"
[node name="Settings" type="Button" parent="VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Settings"

68
scenes/ui/settings.tscn Normal file
View file

@ -0,0 +1,68 @@
[gd_scene load_steps=2 format=3 uid="uid://tax7d1r2lvay"]
[ext_resource type="Script" path="res://scripts/ui/settings.gd" id="1_nw44a"]
[node name="Settings" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_nw44a")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -115.0
offset_top = -50.5
offset_right = 115.0
offset_bottom = 50.5
grow_horizontal = 2
grow_vertical = 2
[node name="Title" type="Label" parent="VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Settings:"
[node name="DefaultServerIP" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/DefaultServerIP"]
layout_mode = 2
text = "Default Server IP: "
[node name="LineEdit" type="LineEdit" parent="VBoxContainer/DefaultServerIP"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
[node name="DefaultUsername" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/DefaultUsername"]
layout_mode = 2
text = "Default Username: "
[node name="LineEdit" type="LineEdit" parent="VBoxContainer/DefaultUsername"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
[node name="DefaultRoomname" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/DefaultRoomname"]
layout_mode = 2
text = "Default Roomname: "
[node name="LineEdit" type="LineEdit" parent="VBoxContainer/DefaultRoomname"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
[node name="Return" type="Button" parent="VBoxContainer"]
layout_mode = 2
text = "Save and Return"

View file

@ -1,7 +1,10 @@
## SPDX-License-Identifier: GPL-3.0-or-later
## Copyright (c) 2024 interstellardevelopment.org
class_name Bullet class_name Bullet
extends CharacterBody3D extends CharacterBody3D
const SPEED: int = 20 const SPEED: int = 40
func _ready() -> void: func _ready() -> void:
if ($Timer as Timer).timeout.connect(_on_timeout): if ($Timer as Timer).timeout.connect(_on_timeout):

View file

@ -15,6 +15,7 @@ var carrying: bool = false
# 1 - wait # 1 - wait
# 2 - get carryable object # 2 - get carryable object
# 3 - chase a player # 3 - chase a player
# 4 - leave the building
var phase: int = 0 var phase: int = 0
@onready var timer: Timer = $Timer @onready var timer: Timer = $Timer
@ -24,23 +25,29 @@ var skip: bool = false
@onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D @onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D
func _ready() -> void: func _ready() -> void:
if ($Area3D as Area3D).body_entered.connect(_on_body_entered):
pass
if Networking.isManager: if Networking.isManager:
if timer.timeout.connect(_timer_done): if timer.timeout.connect(_timer_done):
pass pass
find_objective() find_objective()
func _on_body_entered(body: Node) -> void:
if body is Player and target is Objective:
(body as Player).serve_chance(target as Objective, self)
func _timer_done() -> void: func _timer_done() -> void:
if angry_meter > 0: if angry_meter > 0 and target is Objective:
angry_meter -= 1 angry_meter -= 1
if target is Objective: Networking.npc_text_sync_call("I want a %s! (%ds left)" % [(target as Objective).subtype, angry_meter], target as Objective, self)
Networking.npc_text_sync_call("%s %ds left" % [(target as Objective).displayed, angry_meter], self)
else: else:
Networking.npc_text_sync_call("", self)
(target as Objective).occupied = false (target as Objective).occupied = false
Networking.npc_text_sync_call("", target as Objective, self)
timer.stop() timer.stop()
phase = 2 phase = 2
func set_text(text: String) -> void: func set_text_and_target(text: String, new_target: Objective) -> void:
target = new_target
($Label3D as Label3D).text = text ($Label3D as Label3D).text = text
# determines the closest resting place and navigates to it. # determines the closest resting place and navigates to it.
@ -93,14 +100,22 @@ func set_down(node: Carryable) -> void:
find_player(99999999999) find_player(99999999999)
phase = 0 phase = 0
func served() -> void:
set_text_and_target("", null)
phase = 4
func _physics_process(_delta: float) -> void: func _physics_process(_delta: float) -> void:
if Networking.isManager and phase != 1: if Networking.isManager and phase != 1:
if phase == 3: if phase == 3:
find_player(position.distance_to(target.position)) find_player(position.distance_to(target.position))
elif phase == 2 and target is Objective: elif phase == 2 and target == null:
find_carryable() find_carryable()
elif phase == 0 and target is Player: elif phase == 0 and target is Player:
find_objective() find_objective()
elif phase == 4 and target == null:
target = get_tree().root.get_node("/root/"+Game.mapname+"/NPCExit")
navigation_agent.set_target_position(target.position)
timer.stop()
if navigation_agent.is_navigation_finished(): if navigation_agent.is_navigation_finished():
if phase == 0: if phase == 0:
@ -112,6 +127,8 @@ func _physics_process(_delta: float) -> void:
angry_meter = (target as Objective).time + 1 angry_meter = (target as Objective).time + 1
_timer_done() _timer_done()
timer.start() timer.start()
if phase == 4:
Networking.npc_free_sync_call(self)
return return
var current_agent_position: Vector3 = global_position var current_agent_position: Vector3 = global_position

View file

@ -4,7 +4,6 @@
class_name Player class_name Player
extends CharacterBody3D extends CharacterBody3D
const SPEED: float = 5.0 const SPEED: float = 5.0
const JUMP_VELOCITY: float = 4.5 const JUMP_VELOCITY: float = 4.5
@ -12,7 +11,7 @@ var activated: bool = false
var freecam: bool = false var freecam: bool = false
var incapacitated: bool = false var incapacitated: bool = false
var interaction: String var interaction: String
var revival_target: Player var target: Node3D
var carrying_gun: bool = false var carrying_gun: bool = false
@onready var camera: Camera3D = $Camera3D @onready var camera: Camera3D = $Camera3D
@ -56,10 +55,10 @@ func _physics_process(delta: float) -> void:
($CollisionShape3D as CollisionShape3D).disabled = !($CollisionShape3D as CollisionShape3D).disabled ($CollisionShape3D as CollisionShape3D).disabled = !($CollisionShape3D as CollisionShape3D).disabled
if Input.is_action_just_pressed("interact") and interaction != null: if Input.is_action_just_pressed("interact") and interaction != null:
if interaction == "revive" and revival_target != null: if interaction == "revive" and target != null:
Networking.revive_sync_call(revival_target) Networking.revive_sync_call(target as Player)
($InteractDialog as Label).text = "" ($InteractDialog as Label).text = ""
revival_target = null target = null
if interaction == "gun": if interaction == "gun":
var guncase: Guncase = get_node("/root/%s/Guncase" % Game.mapname) var guncase: Guncase = get_node("/root/%s/Guncase" % Game.mapname)
if !guncase.held: if !guncase.held:
@ -70,6 +69,10 @@ func _physics_process(delta: float) -> void:
carrying_gun = false carrying_gun = false
($InteractDialog as Label).text = "" ($InteractDialog as Label).text = ""
Networking.guncase_sync_call(false, self) Networking.guncase_sync_call(false, self)
if interaction == "serve":
Networking.npc_served_sync_call(target as NPC)
($InteractDialog as Label).text = ""
target = null
if Input.is_action_just_pressed("shoot") and carrying_gun: if Input.is_action_just_pressed("shoot") and carrying_gun:
Networking.bullet_sync_call(position + (transform.basis * Vector3(0, 0, -1)).normalized(), rotation) Networking.bullet_sync_call(position + (transform.basis * Vector3(0, 0, -1)).normalized(), rotation)
@ -78,15 +81,10 @@ func _physics_process(delta: float) -> void:
var direction: Vector3 = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() var direction: Vector3 = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
#TODO: Hacky solution -> Also can be exploited to revive everyone in the game constantly. #TODO: Hacky solution -> Also can be exploited to revive everyone in the game constantly.
if revival_target != null and position.distance_to(revival_target.position) > 3: if target != null and position.distance_to(target.position) > 3:
($InteractDialog as Label).text = ""
interaction = ""
revival_target = null
if interaction == "gun":
if position.distance_to((get_node("/root/%s/Guncase" % Game.mapname) as Guncase).position) > 3:
($InteractDialog as Label).text = "" ($InteractDialog as Label).text = ""
interaction = "" interaction = ""
target = null
if direction: if direction:
velocity.x = direction.x * SPEED velocity.x = direction.x * SPEED
@ -126,9 +124,9 @@ func incapacitate() -> void:
func revive_chance(body: Player) -> void: func revive_chance(body: Player) -> void:
if activated: if activated:
revival_target = body target = body
interaction = "revive" interaction = "revive"
($InteractDialog as Label).text = "Press E to revive %s." % revival_target.name ($InteractDialog as Label).text = "Press E to revive %s." % target.name
func revive() -> void: func revive() -> void:
rotation.x = 0 rotation.x = 0
@ -137,6 +135,7 @@ func revive() -> void:
func gun_chance() -> void: func gun_chance() -> void:
if activated: if activated:
interaction = "gun" interaction = "gun"
target = get_node("/root/%s/Guncase" % Game.mapname) as Guncase
if carrying_gun: if carrying_gun:
($InteractDialog as Label).text = "Press E to set down gun." ($InteractDialog as Label).text = "Press E to set down gun."
else: else:
@ -147,3 +146,9 @@ func show_gun() -> void:
func hide_gun() -> void: func hide_gun() -> void:
($Gun as MeshInstance3D).hide() ($Gun as MeshInstance3D).hide()
func serve_chance(objective: Objective, npc: NPC) -> void:
if activated:
interaction = "serve"
target = npc
($InteractDialog as Label).text = "Press E to serve the guest a %s." % objective.subtype

View file

@ -1,3 +1,6 @@
## SPDX-License-Identifier: GPL-3.0-or-later
## Copyright (c) 2024 interstellardevelopment.org
class_name Carryable class_name Carryable
extends RigidBody3D extends RigidBody3D

View file

@ -1,3 +1,6 @@
## SPDX-License-Identifier: GPL-3.0-or-later
## Copyright (c) 2024 interstellardevelopment.org
class_name Guncase class_name Guncase
extends StaticBody3D extends StaticBody3D

View file

@ -1,6 +1,16 @@
## SPDX-License-Identifier: GPL-3.0-or-later
## Copyright (c) 2024 interstellardevelopment.org
class_name Hungry class_name Hungry
extends Objective extends Objective
func _ready() -> void: func _ready() -> void:
displayed = "Hungry" type = "hungry"
time = 5 match randi() % 2:
0:
subtype = "burger"
1:
subtype = "burger"
2:
subtype = "impossible"
time = 20

View file

@ -6,4 +6,5 @@ extends Node3D
var occupied: bool = false var occupied: bool = false
var time: int var time: int
var displayed: String var type: String
var subtype: String

View file

@ -0,0 +1,7 @@
## SPDX-License-Identifier: GPL-3.0-or-later
## Copyright (c) 2024 interstellardevelopment.org
extends Food
func _ready() -> void:
food_name = "burger"

View file

@ -0,0 +1,7 @@
## SPDX-License-Identifier: GPL-3.0-or-later
## Copyright (c) 2024 interstellardevelopment.org
class_name Food
extends RigidBody3D
var food_name: String = "Placeholder"

View file

@ -4,6 +4,8 @@
extends Control extends Control
func _ready() -> void: func _ready() -> void:
($VBoxContainer/UsernameHBox/LineEdit as LineEdit).text = Game.username
($VBoxContainer/RoomnameHBox/LineEdit as LineEdit).text = Game.roomname
if ($VBoxContainer/SendButton as Button).pressed.connect(_on_send_button_pressed): if ($VBoxContainer/SendButton as Button).pressed.connect(_on_send_button_pressed):
pass pass

15
scripts/ui/main_menu.gd Normal file
View file

@ -0,0 +1,15 @@
extends Control
func _ready() -> void:
if ($VBoxContainer/Join as Button).pressed.connect(_on_join_clicked):
pass
if ($VBoxContainer/Settings as Button).pressed.connect(_on_settings_clicked):
pass
func _on_join_clicked() -> void:
if get_tree().change_scene_to_file("res://scenes/ui/create_room.tscn") != OK:
Log.error("Couldn't change to the create room scene! Closing application.", "Couldn't change to the create room scene!")
func _on_settings_clicked() -> void:
if get_tree().change_scene_to_file("res://scenes/ui/settings.tscn") != OK:
Log.error("Couldn't change to the settings scene! Closing application.", "Couldn't change to the settings scene!")

16
scripts/ui/settings.gd Normal file
View file

@ -0,0 +1,16 @@
extends Control
func _ready() -> void:
($VBoxContainer/DefaultServerIP/LineEdit as LineEdit).text = Game.ip
($VBoxContainer/DefaultUsername/LineEdit as LineEdit).text = Game.username
($VBoxContainer/DefaultRoomname/LineEdit as LineEdit).text = Game.roomname
if ($VBoxContainer/Return as Button).pressed.connect(_on_return_clicked):
pass
func _on_return_clicked() -> void:
Game.ip = ($VBoxContainer/DefaultServerIP/LineEdit as LineEdit).text
Game.username = ($VBoxContainer/DefaultUsername/LineEdit as LineEdit).text
Game.roomname = ($VBoxContainer/DefaultRoomname/LineEdit as LineEdit).text
Game.save_settings()
if get_tree().change_scene_to_file("res://scenes/ui/main_menu.tscn") != OK:
Log.error("Couldn't change to the main menu scene! Closing application.", "Couldn't change to the main menu scene!")

View file

@ -15,14 +15,16 @@ var ip: String = "127.0.0.1"
var port: int = 25262 var port: int = 25262
var max_clients: int = 1024 var max_clients: int = 1024
var roomname: String = "sample" var roomname: String = ""
var username: String = "sample" var username: String = ""
var mapname: String = "Testmap" var mapname: String = "Testmap"
var launchmode: int = 0 var launchmode: int = 0
func _ready() -> void: func _ready() -> void:
Log.info("Running on: %s %s (%s)" % [OS.get_distribution_name(), OS.get_version(), OS.get_name()]) Log.info("Running on: %s %s (%s)" % [OS.get_distribution_name(), OS.get_version(), OS.get_name()])
randomize()
load_settings()
var args: PackedStringArray = OS.get_cmdline_args() var args: PackedStringArray = OS.get_cmdline_args()
var skip: bool = false var skip: bool = false
for i: int in range(args.size()): for i: int in range(args.size()):
@ -55,8 +57,8 @@ func _ready() -> void:
match launchmode: match launchmode:
0: 0:
# Menu # Menu
if get_tree().change_scene_to_file("res://scenes/ui/create_room.tscn") != OK: if get_tree().change_scene_to_file("res://scenes/ui/main_menu.tscn") != OK:
Log.error("Couldn't change to the create room scene! Closing application.", "Couldn't change to the create room scene!") Log.error("Couldn't change to the main menu scene! Closing application.", "Couldn't change to the main menu scene!")
1: 1:
# Client with direct join # Client with direct join
Networking.join_room() Networking.join_room()
@ -64,3 +66,31 @@ func _ready() -> void:
# Server # Server
if Networking.start_server() != OK: if Networking.start_server() != OK:
Log.error("Failed to start server! Closing application.", "Failed to start server!") Log.error("Failed to start server! Closing application.", "Failed to start server!")
func save_settings() -> void:
var to_save: Dictionary = {
"ip": ip,
"roomname": roomname,
"username": username,
}
var to_save_string: String = JSON.stringify(to_save)
var save_file: FileAccess = FileAccess.open("user://save.json", FileAccess.WRITE)
if save_file == null:
Log.warning("Couldn't save the json.")
return
save_file.store_string(to_save_string)
func load_settings() -> void:
var save_file: FileAccess = FileAccess.open("user://save.json", FileAccess.READ)
if save_file == null:
Log.warning("Couldn't open the json save.")
return
var json_string: String = save_file.get_as_text()
var json: JSON = JSON.new()
if json.parse(json_string) != OK:
Log.warning("Couldn't parse the json save.")
return
var data: Dictionary = json.data
ip = data["ip"]
roomname = data["roomname"]
username = data["username"]

View file

@ -7,6 +7,9 @@
# The peers are untrusted. They may cheat, or try something stupid. If at all possible, Direct peer to peer communication should be prevented. # The peers are untrusted. They may cheat, or try something stupid. If at all possible, Direct peer to peer communication should be prevented.
# Rather, such a thing should be done through the game manager if possible. # Rather, such a thing should be done through the game manager if possible.
#TODO: All instances, where any peer can send something, we have to extensively check it, else it may crash somebodys game! Especially NodePaths!
#TODO: Non affiliated peer log messages!
extends Node extends Node
signal playerlist_changed() signal playerlist_changed()
@ -328,19 +331,22 @@ func object_sync(position: Vector3, rotation: Vector3, node_name: String) -> voi
else: else:
Log.warning("Non-manager peer tried to send a manager only request: object_sync") Log.warning("Non-manager peer tried to send a manager only request: object_sync")
func npc_text_sync_call(text: String, target: Objective, npc: NPC) -> void:
for peer: int in Networking.get_ids():
if rpc_id(peer, "npc_text_sync", text, target.get_path(), npc.get_path()) != OK:
Log.warning("Couldn't send RPC to %d!" % peer)
# Synchronizes the text above the npc. # Synchronizes the text above the npc.
@rpc("any_peer", "call_local", "reliable") @rpc("any_peer", "call_local", "reliable")
func npc_text_sync(text: String, npc: NodePath) -> void: func npc_text_sync(text: String, target: NodePath, npc: NodePath) -> void:
if managerID == multiplayer.get_remote_sender_id(): if managerID == multiplayer.get_remote_sender_id():
(get_node(npc) as NPC).set_text(text) if text == "":
(get_node(npc) as NPC).set_text_and_target(text, null)
else:
(get_node(npc) as NPC).set_text_and_target(text, get_node(target) as Objective)
else: else:
Log.warning("Non-manager peer tried to send a manager only request: npc_text_sync") Log.warning("Non-manager peer tried to send a manager only request: npc_text_sync")
func npc_text_sync_call(text: String, npc: NPC) -> void:
for peer: int in Networking.get_ids():
if rpc_id(peer, "npc_text_sync", text, npc.get_path()) != OK:
Log.warning("Couldn't send RPC to %d!" % peer)
func pick_up_sync_call(npc: NPC, carryable: Carryable) -> void: func pick_up_sync_call(npc: NPC, carryable: Carryable) -> void:
# We need to get the path before we send out sync calls. This is because the value changes depending on whether or not it already ran locally. # We need to get the path before we send out sync calls. This is because the value changes depending on whether or not it already ran locally.
var carryable_path: NodePath = carryable.get_path() var carryable_path: NodePath = carryable.get_path()
@ -407,7 +413,10 @@ func guncase_sync(status: bool, player_path: NodePath) -> void:
#TODO: Anti-Cheat -> Check if the gun is even available! #TODO: Anti-Cheat -> Check if the gun is even available!
if verify_id(multiplayer.get_remote_sender_id()): if verify_id(multiplayer.get_remote_sender_id()):
(get_node("/root/%s/Guncase" % Game.mapname) as Guncase).held = status (get_node("/root/%s/Guncase" % Game.mapname) as Guncase).held = status
if status == true:
(get_node(player_path) as Player).show_gun() (get_node(player_path) as Player).show_gun()
else:
(get_node(player_path) as Player).hide_gun()
func bullet_sync_call(position: Vector3, rotation: Vector3) -> void: func bullet_sync_call(position: Vector3, rotation: Vector3) -> void:
for peer: int in Networking.get_ids(): for peer: int in Networking.get_ids():
@ -423,3 +432,32 @@ func bullet_sync(position: Vector3, rotation: Vector3) -> void:
bullet_instance.position = position bullet_instance.position = position
bullet_instance.rotation = rotation bullet_instance.rotation = rotation
get_node("/root/"+Game.mapname).add_child(bullet_instance) get_node("/root/"+Game.mapname).add_child(bullet_instance)
func npc_free_sync_call(npc: NPC) -> void:
var npc_path: NodePath = npc.get_path()
for peer: int in Networking.get_ids():
if rpc_id(peer, "npc_free_sync", npc_path) != OK:
Log.warning("Couldn't send RPC to %d!" % peer)
# Synchronizes the npc removal.
@rpc("any_peer", "call_local", "reliable")
func npc_free_sync(npc_path: NodePath) -> void:
if managerID == multiplayer.get_remote_sender_id():
get_node(npc_path).queue_free()
else:
Log.warning("Non-manager peer tried to send a manager only request: npc_free_sync")
func npc_served_sync_call(npc: NPC) -> void:
var npc_path: NodePath = npc.get_path()
for peer: int in Networking.get_ids():
if rpc_id(peer, "npc_served_sync", npc_path) != OK:
Log.warning("Couldn't send RPC to %d!" % peer)
# Synchronizes when the npc was served.
@rpc("any_peer", "call_local", "reliable")
func npc_served_sync(npc_path: NodePath) -> void:
#TODO: Anti-Cheat -> Prevent unauthorized serving!
if verify_id(multiplayer.get_remote_sender_id()):
(get_node(npc_path) as NPC).served()
else:
Log.warning("Non-manager peer tried to send a manager only request: npc_served_sync")