fix(join): increase join_world timeout, log dedicated server stdout/stderr, null check multiplayer
checkpoint / checkpoint (push) Successful in 5m49s

This commit is contained in:
2026-09-15 21:23:45 +00:00
parent e6e22ac5f2
commit 42520b33d5
3 changed files with 25 additions and 6 deletions
+19 -2
View File
@@ -50,6 +50,7 @@ class _WorldServer:
proc: subprocess.Popen proc: subprocess.Popen
status_path: Path status_path: Path
token: str token: str
log_path: Path | None = None
started_at: float = field(default_factory=time.time) started_at: float = field(default_factory=time.time)
last_join: float = field(default_factory=time.time) last_join: float = field(default_factory=time.time)
@@ -150,6 +151,7 @@ class GameServerSupervisor:
token = secrets.token_urlsafe(32) token = secrets.token_urlsafe(32)
status_path = self._runtime_dir / f"world_{world_id}_{port}.json" status_path = self._runtime_dir / f"world_{world_id}_{port}.json"
status_path.unlink(missing_ok=True) status_path.unlink(missing_ok=True)
log_path = self._runtime_dir / f"world_{world_id}_{port}.log"
cmd = [ cmd = [
settings.GODOT_BIN, "--headless", settings.GODOT_BIN, "--headless",
"--path", settings.GAME_PROJECT_DIR, "--path", settings.GAME_PROJECT_DIR,
@@ -160,9 +162,10 @@ class GameServerSupervisor:
"--api-url", settings.GAME_SERVER_INTERNAL_API_URL, "--api-url", settings.GAME_SERVER_INTERNAL_API_URL,
"--server-token", token, "--server-token", token,
] ]
proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) log_file = open(log_path, "w")
proc = subprocess.Popen(cmd, stdout=log_file, stderr=subprocess.STDOUT)
return _WorldServer( return _WorldServer(
world_id=world_id, port=port, proc=proc, status_path=status_path, token=token world_id=world_id, port=port, proc=proc, status_path=status_path, token=token, log_path=log_path
) )
def _free_port(self) -> int: def _free_port(self) -> int:
@@ -199,13 +202,27 @@ class GameServerSupervisor:
if srv.is_ready(): if srv.is_ready():
return return
if not srv.alive(): if not srv.alive():
log_snippet = ""
if srv.log_path and srv.log_path.exists():
try:
log_snippet = srv.log_path.read_text()[-500:]
except OSError:
pass
raise TimeoutError( raise TimeoutError(
f"server process for world {srv.world_id} exited before becoming ready" f"server process for world {srv.world_id} exited before becoming ready"
+ (f": {log_snippet}" if log_snippet else "")
) )
await asyncio.sleep(0.25) await asyncio.sleep(0.25)
log_snippet = ""
if srv.log_path and srv.log_path.exists():
try:
log_snippet = srv.log_path.read_text()[-500:]
except OSError:
pass
raise TimeoutError( raise TimeoutError(
f"server for world {srv.world_id} did not report ready within " f"server for world {srv.world_id} did not report ready within "
f"{settings.GAME_SERVER_READY_TIMEOUT_SECONDS}s" f"{settings.GAME_SERVER_READY_TIMEOUT_SECONDS}s"
+ (f": {log_snippet}" if log_snippet else "")
) )
async def _reap_loop(self) -> None: async def _reap_loop(self) -> None:
+4 -3
View File
@@ -139,10 +139,11 @@ func request(
body: String = "", body: String = "",
content_type: String = "application/json", content_type: String = "application/json",
require_auth: bool = false, require_auth: bool = false,
custom_headers: PackedStringArray = PackedStringArray() custom_headers: PackedStringArray = PackedStringArray(),
custom_timeout: float = 0.0
) -> Dictionary: ) -> Dictionary:
var http := HTTPRequest.new() var http := HTTPRequest.new()
http.timeout = timeout_seconds http.timeout = custom_timeout if custom_timeout > 0.0 else timeout_seconds
http.max_redirects = 8 http.max_redirects = 8
http.accept_gzip = true http.accept_gzip = true
add_child(http) add_child(http)
@@ -268,7 +269,7 @@ func get_avatar_loadout() -> Dictionary:
## its address. On success result.data is {"world_id", "host", "port"}. ## its address. On success result.data is {"world_id", "host", "port"}.
## 503 if this backend has no game-server orchestration. ## 503 if this backend has no game-server orchestration.
func join_world(world_id: int) -> Dictionary: func join_world(world_id: int) -> Dictionary:
return await request(HTTPClient.METHOD_POST, "/api/worlds/%d/join" % world_id, "", "application/json", true) return await request(HTTPClient.METHOD_POST, "/api/worlds/%d/join" % world_id, "", "application/json", true, PackedStringArray(), 25.0)
## Save (replace) the current user's avatar loadout (requires auth). ## Save (replace) the current user's avatar loadout (requires auth).
+1
View File
@@ -107,6 +107,7 @@ func disconnect_from_server() -> void:
peer.close() peer.close()
if _server_root and is_instance_valid(_server_root): if _server_root and is_instance_valid(_server_root):
_server_root.queue_free() _server_root.queue_free()
if multiplayer:
multiplayer.multiplayer_peer = null multiplayer.multiplayer_peer = null
_prediction_buffer.clear() _prediction_buffer.clear()
_remote_states.clear() _remote_states.clear()