-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstartup.gd
262 lines (219 loc) · 9.24 KB
/
startup.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
extends Node
@export var run_server_in_debug: bool = true
@export var debug_server_url: String = "ws://127.0.0.1:9091"
@export var production_server_url: String = "wss://planet.voidshipephemeral.space/server/"
@export var capture_mouse_on_startup: bool = false # This is actually annoying so I never turn it on.
var pre_game_overlay_scene: Resource = preload("res://menus/pre_game_overlay/pre_game_overlay.tscn")
var pre_game_overlay: Node
var server_config_file_name: String = "user://server_config.dat"
# This has to be here so that it isn't removed after _init() runs,
# which will cause all instances to look like 0.
var _instance_socket: TCPServer
func _init() -> void:
if OS.get_cmdline_user_args().size() > 0:
for arg: String in OS.get_cmdline_user_args():
var arg_array: Array = arg.split("=")
if arg_array[0] == "server":
print_rich("[color=green]Setting as server based on command line argument.[/color]")
Globals.is_server = true
if arg_array[0] == "client":
print_rich(
"[color=green]Forcing to be client based on command line argument.[/color]"
)
Globals.is_server = false
Globals.force_client = true
if arg_array[0] == "shutdown_server":
print_rich("[color=green]This client will tell the server to shut down.[/color]")
Globals.is_server = false
Globals.shutdown_server = true
# Load server password from local data file
var server_config_file_data: String = Helpers.load_data_from_file(
server_config_file_name
)
Globals.server_config = parse_server_config_file_data(server_config_file_data)
if !Globals.is_server and !Globals.force_client and OS.is_debug_build():
# Check if this is the first instance of a debug run, so only one attempts to be the server
# It also provides us with a unique "instance number" for each debug instance of the game run by the editor
# https://gist.github.com/CrankyBunny/71316e7af809d7d4cf5ec6e2369a30b9
_instance_socket = TCPServer.new()
for n: int in range(0, 4): # Godot Editor only creates up to 4 instances maximum.
if _instance_socket.listen(5000 + n) == OK:
Globals.local_debug_instance_number = n
break
# if Globals.local_debug_instance_number < 0:
# print("Unable to determine instance number. Seems like all TCP ports are in use")
# else:
# print("We are instance number ", Globals.local_debug_instance_number)
func _notification(what: int) -> void:
# This catches the quit command and acts on it,
# since we negated it in the _ready() function.
# https://docs.godotengine.org/en/stable/tutorials/inputs/handling_quit_requests.html
if what == NOTIFICATION_WM_CLOSE_REQUEST:
Helpers.quit_gracefully()
func generate_server_config_data() -> Dictionary:
var server_config: Dictionary = {}
Helpers.log_print("Generating new config data for server", "green")
var jwt_secret: String = Helpers.generate_random_string(64)
server_config["jwt_secret"] = jwt_secret
var server_password: String = Helpers.generate_random_string(64)
server_config["server_password"] = server_password
return server_config
func parse_server_config_file_data(server_config_file_data: String) -> Dictionary:
var server_config: Dictionary = {}
var json: JSON = JSON.new()
var error: int = json.parse(server_config_file_data)
if error != OK:
printerr(
"JSON Parse Error: ",
json.get_error_message(),
" in ",
server_config_file_data,
" at line ",
json.get_error_line()
)
get_tree().quit() # Quits the game due to bad server config data
server_config = json.data
if (
typeof(server_config) != TYPE_DICTIONARY
or not server_config.has("jwt_secret")
or not server_config.has("server_password")
):
server_config = generate_server_config_data()
return server_config
func _ready() -> void:
# Disable auto-quit so that we can catch it ourselves elsewhere
# Note that this alone will defeat Windows X or Alt+F4
# See helper_functions.gd quit_gracefully()
# https://docs.godotengine.org/en/stable/tutorials/inputs/handling_quit_requests.html
get_tree().set_auto_accept_quit(false)
if OS.is_debug_build() and run_server_in_debug:
Globals.url = debug_server_url
else:
Globals.url = production_server_url
Network.reset.connect(connection_reset)
Network.close_pre_game_overlay.connect(hide_pre_game_overlay)
Network.update_pre_game_overlay.connect(update_pre_game_overlay)
if (
!Globals.is_server
and !Globals.force_client
and OS.is_debug_build()
and run_server_in_debug
and Globals.local_debug_instance_number < 1
and OS.get_name() != "Web"
):
print_rich(
"[color=green]Setting as server based being a debug build and run_server_in_debug and being first instance to run.[/color]"
)
Globals.is_server = true
if Globals.is_server:
# Load or generate server config data
var server_config: Dictionary = {}
var server_config_file_data: String = Helpers.load_data_from_file(server_config_file_name)
if server_config_file_data == "":
server_config = generate_server_config_data()
else:
server_config = parse_server_config_file_data(server_config_file_data)
Globals.server_config = server_config
# Save config back out to file, even if we imported it from the file.
Helpers.save_data_to_file(server_config_file_name, JSON.stringify(Globals.server_config))
# Load or generate player data
var player_save_data: Dictionary = {}
var player_save_data_file_data: String = Helpers.load_data_from_file(
Globals.server_player_save_data_file_name
)
if player_save_data_file_data == "":
Helpers.log_print("Generating new player save data file for server", "green")
else:
var json: JSON = JSON.new()
var error: int = json.parse(player_save_data_file_data)
if error != OK:
print(
"JSON Parse Error: ",
json.get_error_message(),
" in ",
player_save_data_file_data,
" at line ",
json.get_error_line()
)
get_tree().quit() # Quits the game due to bad server config data
player_save_data = json.data
if typeof(player_save_data) != TYPE_DICTIONARY:
printerr("Data error in: ", server_config)
get_tree().quit() # Quits the game due to bad server config data
Globals.player_save_data = player_save_data
# Save config back out to file, even if we imported it from the file.
Helpers.save_server_player_save_data_to_file()
if capture_mouse_on_startup and not Globals.is_server:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
if OS.is_debug_build() and !Globals.is_server:
# Delay before connecting to give server a chance to start
await get_tree().create_timer(1.0).timeout
start_connection()
func start_connection() -> void:
display_pre_game_overlay()
if OS.is_debug_build() and Globals.local_debug_instance_number > 0 and not Globals.is_server:
var debug_delay: int = Globals.local_debug_instance_number
while debug_delay > 0:
if pre_game_overlay:
pre_game_overlay.set_msg("Debug delay " + str(debug_delay))
debug_delay = debug_delay - 1
await get_tree().create_timer(0.2).timeout
if pre_game_overlay:
pre_game_overlay.set_msg("Connecting...")
Network.ready_to_connect = true
func connection_reset(delay: int) -> void:
display_pre_game_overlay()
if pre_game_overlay:
pre_game_overlay.set_msg(
Globals.connection_failed_message,
Color(0.79215687513351, 0.26274511218071, 0.56470590829849)
)
if OS.is_debug_build() and Globals.has_connected_once and OS.get_name() != "Web":
# Exit when the server closes in debug mode
# except in web mode, where "exit" has no meaning.
Helpers.log_print(
"Closing due to server disconnecting and this running in Debug mode.", "green"
)
Helpers.quit_gracefully()
var game_scene_node: Node = get_node_or_null("../Main/game_scene")
if game_scene_node and is_instance_valid(game_scene_node):
game_scene_node.queue_free()
await get_tree().create_timer(3).timeout
var retry_delay: int = delay
while retry_delay > 0:
pre_game_overlay.set_msg("Retrying in " + str(retry_delay))
retry_delay = retry_delay - 1
await get_tree().create_timer(1).timeout
if OS.get_name() == "Web":
# Force browser refresh in case there are updates to the game code to download
JavaScriptBridge.eval("location.reload();")
start_connection()
func _input(event: InputEvent) -> void:
if event.is_action_pressed(&"ui_cancel"):
if OS.is_debug_build():
# ESC key closes game in debug mode
Helpers.log_print("Closing due to ESC key.", "green")
Helpers.quit_gracefully()
else: # Releases mouse in normal build
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
if event.is_action_pressed(&"ui_end"):
# END key closes the game
Helpers.log_print("Closing due to END key.", "green")
Helpers.quit_gracefully()
func display_pre_game_overlay() -> void:
if not pre_game_overlay:
pre_game_overlay = pre_game_overlay_scene.instantiate()
add_child(pre_game_overlay)
func hide_pre_game_overlay() -> void:
if pre_game_overlay and is_instance_valid(pre_game_overlay):
pre_game_overlay.queue_free()
pre_game_overlay = null
# Sets player as authority over itself on the client
# when the player spawns
# It cannot be done sooner or anywhere else
func _on_players_spawner_spawned(node: Node) -> void:
node.set_multiplayer_authority(str(node.name).to_int())
func update_pre_game_overlay(message: String, percentage: int = -1) -> void:
if pre_game_overlay:
pre_game_overlay.set_msg(message)
pre_game_overlay.update_progress_bar(percentage)