fix: two-player lobby deadlock

This commit is contained in:
2026-08-30 23:42:26 +03:00
parent d5eb26d330
commit 93af738e36
9 changed files with 77 additions and 10 deletions
+6 -3
View File
@@ -99,16 +99,18 @@ void game_lifecycle_set_bot_scheduler(game_lifecycle_t *lifecycle, scheduler_t s
lifecycle_result_t game_lifecycle_join(game_lifecycle_t *lifecycle, role_t requested_role, const char *name,
uint8_t *session_index) {
if (lifecycle == NULL) return LIFECYCLE_RESULT_UNAUTHORIZED;
if ((requested_role == ROLE_PLAYER_1 || requested_role == ROLE_PLAYER_2) && lifecycle->game.state.phase != PHASE_LOBBY) {
if ((requested_role == ROLE_PLAYER_1 || requested_role == ROLE_PLAYER_2 || requested_role == ROLE_AUTO_PLAYER) && lifecycle->game.state.phase != PHASE_LOBBY) {
return LIFECYCLE_RESULT_NO_PLAYER_SLOT;
}
if (requested_role == ROLE_PLAYER_2 && !session_manager_player_present(&lifecycle->sessions, 0U)) {
return LIFECYCLE_RESULT_NO_PLAYER_SLOT;
}
if (requested_role == ROLE_PLAYER_2 && (lifecycle->game.state.mode == MODE_BOT || lifecycle->bot_reserved)) {
if ((requested_role == ROLE_PLAYER_2 || requested_role == ROLE_AUTO_PLAYER) && (lifecycle->game.state.mode == MODE_BOT || lifecycle->bot_reserved)) {
return LIFECYCLE_RESULT_NO_PLAYER_SLOT;
}
return session_result(session_manager_join(&lifecycle->sessions, requested_role, name, lifecycle->random, session_index));
const lifecycle_result_t result = session_result(session_manager_join(&lifecycle->sessions, requested_role, name, lifecycle->random, session_index));
if (result == LIFECYCLE_RESULT_OK) ++lifecycle->game.state.version;
return result;
}
lifecycle_result_t game_lifecycle_resume(game_lifecycle_t *lifecycle,
@@ -126,6 +128,7 @@ lifecycle_result_t game_lifecycle_leave(game_lifecycle_t *lifecycle, uint8_t ses
if (lifecycle == NULL || session_index >= kSessionCapacity || !lifecycle->sessions.entries[session_index].occupied) return LIFECYCLE_RESULT_UNAUTHORIZED;
const bool player = session_index < kPlayerCapacity && lifecycle->sessions.entries[session_index].role == (role_t)session_index;
if (player && lifecycle->game.state.phase != PHASE_LOBBY) return_to_lobby(lifecycle);
else ++lifecycle->game.state.version;
memset(&lifecycle->sessions.entries[session_index], 0, sizeof(session_t));
++lifecycle->recovery_generation;
lifecycle->recovery_reason = RECOVERY_REASON_SESSION_LEFT;
+2 -1
View File
@@ -206,6 +206,7 @@ static bool parse_join(const http_api_request_t *request, char name[kDisplayName
if (strcmp(role_value, "player1") == 0) *role = ROLE_PLAYER_1;
else if (strcmp(role_value, "player2") == 0) *role = ROLE_PLAYER_2;
else if (strcmp(role_value, "spectator") == 0) *role = ROLE_SPECTATOR;
else if (strcmp(role_value, "player") == 0) *role = ROLE_AUTO_PLAYER;
else *role = (role_t)UINT8_MAX;
return true;
}
@@ -353,7 +354,7 @@ bool http_api_handle(http_api_t *api, const http_api_request_t *request, http_ap
response_error(response, 400U, "MALFORMED_JSON", "Некорректный JSON", version);
return true;
}
if (role > ROLE_SPECTATOR) {
if (role > ROLE_AUTO_PLAYER) {
response_error(response, 400U, "INVALID_ROLE", "Некорректная роль", version);
return true;
}
+3 -1
View File
@@ -48,11 +48,13 @@ session_result_t session_manager_join(session_manager_t *manager, role_t request
char sanitized[kDisplayNameBytes + 1] = {0};
if (!sanitize_name(name, sanitized)) return SESSION_RESULT_INVALID_NAME;
int8_t selected = -1;
if (requested_role == ROLE_PLAYER_1) selected = first_free(manager, 0, 1);
if (requested_role == ROLE_AUTO_PLAYER) selected = first_free(manager, 0, kPlayerCapacity);
else if (requested_role == ROLE_PLAYER_1) selected = first_free(manager, 0, 1);
else if (requested_role == ROLE_PLAYER_2) selected = first_free(manager, 1, 2);
else if (requested_role == ROLE_SPECTATOR) selected = first_free(manager, kPlayerCapacity, kSessionCapacity);
else return SESSION_RESULT_INVALID_ROLE;
if (selected < 0) return requested_role == ROLE_SPECTATOR ? SESSION_RESULT_NO_SPECTATOR_SLOT : SESSION_RESULT_NO_PLAYER_SLOT;
if (requested_role == ROLE_AUTO_PLAYER) requested_role = (role_t)selected;
session_t candidate = {.role = requested_role, .occupied = true, .connected = true};
memcpy(candidate.name, sanitized, sizeof(candidate.name));
for (uint8_t offset = 0; offset < kSessionTokenBytes; offset += 4) {