diff options
author | Tor Andersson <tor@ccxvii.net> | 2024-01-01 17:36:56 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2024-01-28 13:50:27 +0100 |
commit | a1c93c992e456764415b5a4c302e1137673c0a5f (patch) | |
tree | aae79b1ce9c085e66f5ecf4022a2ea93cc07b92e | |
parent | 12a2a48e5e1158e1fcbed03862ab17e2869c09e4 (diff) | |
download | server-a1c93c992e456764415b5a4c302e1137673c0a5f.tar.gz |
Add time control enforcement.
-rw-r--r-- | schema.sql | 45 | ||||
-rw-r--r-- | server.js | 33 | ||||
-rw-r--r-- | views/head.pug | 6 |
3 files changed, 77 insertions, 7 deletions
@@ -482,17 +482,53 @@ create view player_view as is_invite, ( case status - when 0 then owner_id = user_id - when 1 then active in ( 'Both', role ) - else 0 + when 0 then + owner_id = user_id + when 1 then + active in ( 'Both', role ) + else + 0 end - ) as is_active + ) as is_active, + ( + case when pace = 0 then + 21.0 - (julianday() - julianday(mtime)) + else + case when active in ( 'Both', role ) then + 10.0 - ((julianday() - julianday(mtime)) + time_used - time_added) + else + 10.0 - (time_used - time_added) + end + end + ) as time_left from games join players using(game_id) join users using(user_id) ; +drop view if exists time_control_view; +create view time_control_view as + select + game_id, + user_id, + role, + ( + case when pace = 0 then + 21.0 - (julianday() - julianday(mtime)) + else + 10.0 - ((julianday() - julianday(mtime)) + time_used - time_added) + end + ) as time_left, + is_opposed + from + games + join players using(game_id) + where + status = 1 + and active in ( 'Both', role ) + ; + drop view if exists your_turn_reminder; create view your_turn_reminder as select @@ -584,7 +620,6 @@ begin delete from game_notes where game_id = old.game_id; delete from last_notified where game_id = old.game_id; delete from unread_chats where game_id = old.game_id; - update players set time_added = null where game_id = old.game_id; end; -- Triggers to clean up without relying on foreign key cascades @@ -1183,7 +1183,8 @@ function is_game_ready(player_count, players) { load_rules() const SQL_INSERT_GAME = SQL("INSERT INTO games (owner_id,title_id,scenario,options,player_count,pace,is_private,is_random,notice,is_match) VALUES (?,?,?,?,?,?,?,?,?,?) returning game_id").pluck() -const SQL_DELETE_GAME = SQL("DELETE FROM games WHERE game_id=? AND owner_id=?") +const SQL_DELETE_GAME_BY_OWNER = SQL("delete from games where game_id=? and owner_id=?") +const SQL_DELETE_GAME = SQL("delete from games where game_id=?") const SQL_START_GAME = SQL(` update games set @@ -1430,6 +1431,7 @@ function annotate_game_info(game, user_id, unread) { let your_count = 0 let your_role = null + let time_left = Infinity let roles = get_game_roles(game.title_id, game.scenario, options) @@ -1449,6 +1451,9 @@ function annotate_game_info(game, user_id, unread) { game.your_turn = true } + if (p.is_active) + time_left = Math.min(time_left, p.time_left) + let link if (p.is_invite) link = `<a class="is_invite" href="/user/${p.name}">${p.name}?</a>` @@ -1475,6 +1480,9 @@ function annotate_game_info(game, user_id, unread) { }).join(", ") } + if (game.is_ready && game.status === 1) + game.time_left = time_left + if (your_count > 0) { game.is_yours = true if (your_count === 1) @@ -1675,7 +1683,7 @@ app.post("/create/:title_id", must_be_logged_in, function (req, res) { app.get("/delete/:game_id", must_be_logged_in, function (req, res) { let game_id = req.params.game_id let title_id = SQL_SELECT_GAME_TITLE.get(game_id) - let info = SQL_DELETE_GAME.run(game_id, req.user.user_id) + let info = SQL_DELETE_GAME_BY_OWNER.run(game_id, req.user.user_id) if (info.changes === 0) return res.send("Not authorized to delete that game ID.") if (info.changes === 1) @@ -2444,6 +2452,27 @@ setInterval(purge_game_ticker, 31 * 60 * 1000) setTimeout(purge_game_ticker, 89 * 1000) /* + * TIME CONTROL + */ + +const QUERY_LIST_TIME_CONTROL = SQL("select * from time_control_view join users using(user_id) where time_left < 0") + +function time_control_ticker() { + for (let item of QUERY_LIST_TIME_CONTROL.all()) { + if (item.is_opposed) { + console.log("TIMED OUT GAME:", item.game_id, item.name, item.time_left) + do_resign(item.game_id, item.role, "timed out") + } else { + SQL_DELETE_GAME.run(item.game_id) + } + } +} + +// Run time control checks every 13 minutes. +setInterval(time_control_ticker, 13 * 60 * 1000) +setTimeout(time_control_ticker, 13 * 1000) + +/* * GAME SERVER */ diff --git a/views/head.pug b/views/head.pug index e47033a..5cbb726 100644 --- a/views/head.pug +++ b/views/head.pug @@ -118,6 +118,12 @@ mixin gamelist(list,hide_title=0) div Created: #{item.ctime} when 1 div Last move: #{item.mtime} + if item.time_left <= 0 + div Time left: none. + else if item.time_left <= 2 + div Time left: #{ item.time_left * 24 | 0 } hours + else if item.time_left <= 5 + div Time left: #{ item.time_left | 0 } days when 2 div Finished: #{item.mtime} div Result: !{item.result} |