diff options
author | Tor Andersson <tor@ccxvii.net> | 2025-02-10 22:09:13 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2025-02-12 15:08:20 +0100 |
commit | 2f9544bf97e38cb27f9151b2383a4a9fab7e355e (patch) | |
tree | 339f5eb4083ae1ef2dc23a55b6cc071f014c2676 | |
parent | dadd13dfd983eb69863be234a73eb3881bfabdd8 (diff) | |
download | server-2f9544bf97e38cb27f9151b2383a4a9fab7e355e.tar.gz |
-rw-r--r-- | schema.sql | 21 | ||||
-rw-r--r-- | server.js | 69 | ||||
-rwxr-xr-x | tools/patchgame.js | 30 |
3 files changed, 86 insertions, 34 deletions
@@ -154,7 +154,9 @@ create view user_dynamic_view as status = 1 and user_count = player_count and players.user_id = users.user_id - and active in ( 'Both', players.role ) + -- and active in ( 'Both', players.role ) + and ( active = 'Both' or instr(active, players.role) ) + ) + ( select count(*) @@ -510,13 +512,17 @@ create view player_view as when 0 then owner_id = user_id when 1 then - active in ( 'Both', role ) + -- active in ( 'Both', role ) + ( active = 'Both' or instr(active, role) ) else 0 end ) as is_active, ( - case when active in ( 'Both', role ) then + case when + -- active in ( 'Both', role ) + ( active = 'Both' or instr(active, role) ) + then clock - (julianday() - julianday(mtime)) else clock @@ -543,7 +549,8 @@ create view time_control_view as join players using(game_id) where status = 1 - and active in ( 'Both', role ) + -- and active in ( 'Both', role ) + and ( active = 'Both' or instr(active, role) ) and clock - (julianday() - julianday(mtime)) < 0 ; @@ -888,7 +895,8 @@ begin update players set clock = clock - (julianday() - julianday(old.mtime)) where players.game_id = old.game_id - and old.active in ( 'Both', players.role ) + -- and old.active in ( 'Both', players.role ) + and ( old.active = 'Both' or instr(old.active, players.role) ) ; end; @@ -902,7 +910,8 @@ begin user_id, old.game_id, (julianday() - julianday(old.mtime)) from players where players.game_id = old.game_id - and old.active in ( 'Both', players.role ) + -- and old.active in ( 'Both', players.role ) + and ( old.active = 'Both' or instr(old.active, players.role) ) ; end; @@ -1539,7 +1539,8 @@ const QUERY_NEXT_GAME_OF_USER = SQL(` join players using(game_id) where status = ${STATUS_ACTIVE} - and active in (role, 'Both') + -- and active in (role, 'Both') + and ( active = 'Both' or instr(active, role) > 0 ) and user_id = ? and is_opposed order by mtime @@ -2197,7 +2198,7 @@ function start_game(game) { state = RULES[game.title_id].setup(seed, game.scenario, options) - SQL_START_GAME.run(state.active, game.game_id) + SQL_START_GAME.run(String(state.active), game.game_id) let replay_id = put_replay(game.game_id, null, ".setup", [ seed, game.scenario, options ]) put_snap(game.game_id, replay_id, state) SQL_INSERT_GAME_STATE.run(game.game_id, JSON.stringify(state)) @@ -2248,7 +2249,7 @@ function rewind_game_to_snap(game_id, snap_id) { SQL_DELETE_GAME_REPLAY.run(game_id, snap.replay_id) SQL_INSERT_GAME_STATE.run(game_id, JSON.stringify(snap_state)) - SQL_REWIND_GAME.run(snap_id - 1, snap_state.active, game_id) + SQL_REWIND_GAME.run(snap_id - 1, String(snap_state.active), game_id) SQL_REWIND_GAME_CLOCK.run(game_id) update_join_clients(game_id) @@ -2538,14 +2539,10 @@ function send_chat_activity_notification(game_id, p) { send_play_notification(p, game_id, "Chat activity") } -function is_active_role(active, role) { - return active === "Both" || active === role -} - function send_game_started_notification(game_id, active) { let players = SQL_SELECT_PLAYERS.all(game_id) for (let p of players) { - let p_is_active = is_active_role(active, p.role) + let p_is_active = is_role_active(active, p.role) if (p_is_active) send_play_notification(p, game_id, "Started - Your turn") else @@ -2553,15 +2550,15 @@ function send_game_started_notification(game_id, active) { } } -function send_your_turn_notification_to_offline_users(game_id, old_active, active) { +function send_your_turn_notification_to_offline_users(game_id, old_active, new_active) { // Only send notifications when the active player changes. - if (old_active === active) + if (!is_changed_active(old_active, new_active)) return let players = SQL_SELECT_PLAYERS.all(game_id) for (let p of players) { - let p_was_active = is_active_role(old_active, p.role) - let p_is_active = is_active_role(active, p.role) + let p_was_active = is_role_active(old_active, p.role) + let p_is_active = is_role_active(new_active, p.role) if (!p_was_active && p_is_active) { if (!is_player_online(game_id, p.user_id)) send_play_notification(p, game_id, "Your turn") @@ -3489,6 +3486,26 @@ if (app.locals.ENABLE_TOURNAMENTS) { * GAME SERVER */ +function is_role_active(active, role) { + return active === role || active === "Both" || active.includes(role) +} + +function is_nobody_active(active) { + return !active || active === "None" +} + +function is_multi_active(active) { + if (!active) + return false + if (Array.isArray(active)) + return true + return active === "Both" || active.includes(",") +} + +function is_changed_active(old_active, new_active) { + return String(old_active) !== String(new_active) +} + function is_player_online(game_id, user_id) { if (game_clients[game_id]) for (let other of game_clients[game_id]) @@ -3515,7 +3532,7 @@ function send_state(socket, state) { socket.send('["state",' + this_view + "," + game_cookies[socket.game_id] + "]") socket.last_view = this_view } - if (!state.active || state.active === "None") { + if (is_nobody_active(state.active)) { socket.send('["finished"]') } } catch (err) { @@ -3557,7 +3574,11 @@ function put_replay(game_id, role, action, args) { } function dont_snap(rules, state, old_active) { - if (state.active === old_active || !state.active || state.active === "None") + if (is_nobody_active(state.active)) + return true + if (is_multi_active(old_active) && is_multi_active(state.active)) + return true + if (!is_changed_active(old_active, state.active)) return true if (rules.dont_snap && rules.dont_snap(state)) return true @@ -3573,17 +3594,16 @@ function put_snap(game_id, replay_id, state) { function put_game_state(game_id, state, old_active, current_role) { // TODO: separate state, undo, and log entries (and reuse "snap" json stringifaction?) - SQL_INSERT_GAME_STATE.run(game_id, JSON.stringify(state)) - if (state.active !== old_active) { - SQL_UPDATE_GAME_ACTIVE.run(state.active, game_id) + if (is_changed_active(old_active, state.active)) { + SQL_UPDATE_GAME_ACTIVE.run(String(state.active), game_id) // add time for the player who took the current action SQL_UPDATE_PLAYERS_ADD_TIME.run(game_id, current_role) } - if (!state.active || state.active === "None") { + if (is_nobody_active(state.active)) { SQL_FINISH_GAME.run(state.result, game_id) if (state.result && state.result !== "None") update_elo_ratings(game_id) @@ -3600,13 +3620,14 @@ function put_new_state(title_id, game_id, state, old_active, role, action, args) put_game_state(game_id, state, old_active, role) - if (state.active !== old_active) + if (is_changed_active(old_active, state.active)) update_join_clients(game_id) + if (game_clients[game_id]) for (let other of game_clients[game_id]) send_state(other, state) - if (!state.active || state.active === "None") + if (is_nobody_active(state.active)) send_game_finished_notification_to_offline_users(game_id, state.result) else send_your_turn_notification_to_offline_users(game_id, old_active, state.active) @@ -3632,11 +3653,11 @@ function on_action(socket, action, args, cookie) { try { let state = get_game_state(socket.game_id) - let old_active = state.active + let old_active = String(state.active) // Don't update cookie during simultaneous turns, as it results // in many in-flight collisions. - if (old_active !== "Both") + if (!is_multi_active(old_active)) game_cookies[socket.game_id] ++ state = RULES[socket.title_id].action(state, socket.role, action, args) @@ -3660,7 +3681,7 @@ function on_resign(socket) { function do_timeout(game_id, role) { let game = SQL_SELECT_GAME.get(game_id) let state = get_game_state(game_id) - let old_active = state.active + let old_active = String(state.active) state = finish_game_state(game.title_id, state, "None", role + " timed out.") put_new_state(game.title_id, game_id, state, old_active, role, ".timeout", null) } @@ -3668,7 +3689,7 @@ function do_timeout(game_id, role) { function do_resign(game_id, role) { let game = SQL_SELECT_GAME.get(game_id) let state = get_game_state(game_id) - let old_active = state.active + let old_active = String(state.active) let result = "None" let roles = get_game_roles(game.title_id, game.scenario, game.options) diff --git a/tools/patchgame.js b/tools/patchgame.js index a28ce1f..de8721e 100755 --- a/tools/patchgame.js +++ b/tools/patchgame.js @@ -71,10 +71,30 @@ function snapshot(state) { return snap } +function is_role_active(active, role) { + return active === role || active === "Both" || active.includes(role) +} + +function is_nobody_active(active) { + return !active || active === "None" +} + +function is_multi_active(active) { + if (!active) + return false + if (Array.isArray(active)) + return true + return active === "Both" || active.includes(",") +} + +function is_changed_active(old_active, new_active) { + return String(old_active) !== String(new_active) +} + function is_valid_action(rules, state, role, action, arg) { if (action === "undo") // for jc, hots, r3, and cr compatibility return true - if (state.active !== role && state.active !== "Both") + if (!is_role_active(state.active, role)) return false let view = rules.view(state, role) let va = view.actions[action] @@ -88,9 +108,11 @@ function is_valid_action(rules, state, role, action, arg) { } function dont_snap(rules, state, old_active) { - if (state.state === "game_over") + if (is_nobody_active(state.active)) + return true + if (is_multi_active(old_active) && is_multi_active(state.active)) return true - if (state.active === old_active) + if (!is_changed_active(old_active, state.active)) return true if (rules.dont_snap && rules.dont_snap(state)) return true @@ -219,7 +241,7 @@ function patch_game(game_id, {validate_actions=true, save_snaps=true, delete_und insert_snap.run(game_id, ++snap_id, item.replay_id, item.state) } - update_active.run(state.active, game_id) + update_active.run(String(state.active), game_id) update_state.run(JSON.stringify(state), game_id) if (state.state === "game_over") |