dumb daily levels implementation

This commit is contained in:
Jill 2023-01-04 02:20:45 +03:00
parent 17a4332e24
commit 9b55f5c423
7 changed files with 135 additions and 12 deletions

View File

@ -0,0 +1,28 @@
-- +migrate up
CREATE TABLE daily_queue (
level_id INTEGER NOT NULL references levels(id),
idx SERIAL NOT NULL PRIMARY KEY
);
CREATE TABLE daily_levels (
level_id INTEGER NOT NULL references levels(id),
idx SERIAL NOT NULL PRIMARY KEY,
expires_at TEXT NOT NULL
);
CREATE TABLE weekly_queue (
level_id INTEGER NOT NULL references levels(id),
idx SERIAL NOT NULL PRIMARY KEY
);
CREATE TABLE weekly_levels (
level_id INTEGER NOT NULL references levels(id),
idx SERIAL NOT NULL PRIMARY KEY,
expires_at TEXT NOT NULL
);
-- +migrate down
DROP TABLE daily_queue;
DROP TABLE daily_levels;
DROP TABLE weekly_queue;
DROP TABLE weekly_levels;

View File

@ -18,6 +18,7 @@ require "./lib/clean"
require "./lib/songs"
require "./lib/ids"
require "./lib/level"
require "./lib/dailies"
if File.exists?(".env")
Dotenv.load

View File

@ -9,7 +9,30 @@ CrystalGauntlet.endpoints["/downloadGJLevel22.php"] = ->(context : HTTP::Server:
response = [] of String
DATABASE.query("select levels.id, levels.name, levels.extra_data, levels.level_info, levels.password, levels.user_id, levels.description, levels.original, levels.game_version, levels.requested_stars, levels.version, levels.song_id, levels.length, levels.objects, levels.coins, levels.has_ldm, levels.two_player, levels.downloads, levels.likes, levels.difficulty, levels.community_difficulty, levels.demon_difficulty, levels.stars, levels.featured, levels.epic, levels.rated_coins, users.username, users.udid, users.account_id, users.registered, editor_time, editor_time_copies from levels join users on levels.user_id = users.id where levels.id = ?", params["levelID"].to_i32) do |rs|
level_id = params["levelID"].to_i32
daily_num = nil # for hash checks
case level_id
when -1
# daily
level_id, _, idx = Dailies.fetch_current_level(false)
if !level_id || !idx
return "-1"
end
daily_num = idx
when -2
# weekly
level_id, _, idx = Dailies.fetch_current_level(true)
if !level_id || !idx
return "-1"
end
daily_num = idx + Dailies::WEEKLY_OFFSET
when -3
# events
raise "events?? what the hell"
end
DATABASE.query("select levels.id, levels.name, levels.extra_data, levels.level_info, levels.password, levels.user_id, levels.description, levels.original, levels.game_version, levels.requested_stars, levels.version, levels.song_id, levels.length, levels.objects, levels.coins, levels.has_ldm, levels.two_player, levels.downloads, levels.likes, levels.difficulty, levels.community_difficulty, levels.demon_difficulty, levels.stars, levels.featured, levels.epic, levels.rated_coins, users.username, users.udid, users.account_id, users.registered, editor_time, editor_time_copies from levels join users on levels.user_id = users.id where levels.id = ?", level_id) do |rs|
if rs.move_next
id = rs.read(Int32)
name = rs.read(String)
@ -102,6 +125,7 @@ CrystalGauntlet.endpoints["/downloadGJLevel22.php"] = ->(context : HTTP::Server:
38 => rated_coins,
39 => requested_stars || 0,
40 => has_ldm,
41 => daily_num,
42 => epic,
# 0 for n/a, 10 for easy, 20, for medium, ...
43 => (demon_difficulty || DemonDifficulty::Hard).to_demon_difficulty,
@ -113,9 +137,13 @@ CrystalGauntlet.endpoints["/downloadGJLevel22.php"] = ->(context : HTTP::Server:
})
response << Hashes.gen_solo(level_data)
thing = [user_id, stars || 0, (difficulty && difficulty.demon?) || 0, id, rated_coins, featured, password, 0].map { |x| Format.fmt_value(x) }
thing = [user_id, stars || 0, (difficulty && difficulty.demon?) || 0, id, rated_coins, featured, password, daily_num || 0].map { |x| Format.fmt_value(x) }
response << Hashes.gen_solo_2(thing.join(","))
if daily_num
response << [user_id, user_username, user_account_id].join(":")
end
return response.join("#")
else
return "-1"

View File

@ -0,0 +1,25 @@
require "uri"
include CrystalGauntlet
CrystalGauntlet.endpoints["/getGJDailyLevel.php"] = ->(context : HTTP::Server::Context): String {
params = URI::Params.parse(context.request.body.not_nil!.gets_to_end)
LOG.debug { params.inspect }
weekly = params["weekly"] == "1"
level_id = nil
expires = nil
level_id, expires, idx = Dailies.fetch_current_level(weekly)
if !level_id || !expires || !idx
"-1"
else
if weekly
idx += Dailies::WEEKLY_OFFSET
end
[idx, expires].join("|")
end
}

View File

@ -156,11 +156,13 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(context : HTTP::Server::Con
when "13" # friends
# todo
when "21" # daily
# todo
order = "daily_levels.idx desc"
joins << "join daily_levels on levels.id = daily_levels.level_id"
when "22" # weekly
# todo
order = "weekly_levels.idx desc"
joins << "join weekly_levels on levels.id = weekly_levels.level_id"
when "23" # event (unused)
# todo
# todo..?
end
if !can_see_unlisted

View File

@ -33,14 +33,12 @@ CrystalGauntlet.endpoints["/getGJLevelScores211.php"] = ->(context : HTTP::Serve
return "-1"
end
# todo: account for dailies
if DATABASE.scalar("select count(*) from level_scores where account_id = ? and level_id = ?", account_id, level_id).as(Int64) > 0
if DATABASE.scalar("select count(*) from level_scores where account_id = ? and level_id = ? and daily_id is ?", account_id, level_id, daily_id).as(Int64) > 0
# check if an update is truly necessary
percent_old, coins_old = DATABASE.query_one("select percent, coins from level_scores where account_id = ? and level_id = ?", account_id, level_id, as: {Int32, Int32})
percent_old, coins_old = DATABASE.query_one("select percent, coins from level_scores where account_id = ? and level_id = ? and daily_id is ?", account_id, level_id, daily_id, as: {Int32, Int32})
if percent > percent_old || coins > coins_old
DATABASE.exec("update level_scores set account_id=?, level_id=?, daily_id=?, percent=?, attempts=?, clicks=?, coins=?, progress=?, time=?, set_at=? where account_id = ? and level_id = ?", account_id, level_id, daily_id, percent, attempts, clicks, coins, progress, time, Time.utc.to_s(Format::TIME_FORMAT), account_id, level_id)
DATABASE.exec("update level_scores set account_id=?, percent=?, attempts=?, clicks=?, coins=?, progress=?, time=?, set_at=? where account_id = ? and level_id = ? and daily_id is ?", account_id, percent, attempts, clicks, coins, progress, time, Time.utc.to_s(Format::TIME_FORMAT), account_id, level_id, daily_id)
end
else
DATABASE.exec("insert into level_scores (account_id, level_id, daily_id, percent, attempts, clicks, coins, progress, time) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", account_id, level_id, daily_id, percent, attempts, clicks, coins, progress, time)
@ -51,7 +49,7 @@ CrystalGauntlet.endpoints["/getGJLevelScores211.php"] = ->(context : HTTP::Serve
type = params["type"]? ? params["type"] : "1"
where_query = ["level_id = ?"]
where_query = ["level_id = ? and daily_id is ?"]
case type
when 0
@ -68,7 +66,7 @@ CrystalGauntlet.endpoints["/getGJLevelScores211.php"] = ->(context : HTTP::Serve
scores = [] of String
i = 0
DATABASE.query_each "select percent, level_scores.coins, set_at, users.username, users.id, users.icon_type, users.color1, users.color2, users.cube, users.ship, users.ball, users.ufo, users.wave, users.robot, users.spider, users.special, users.account_id from level_scores join users on level_scores.account_id = users.account_id where (#{where_query.join(") and (")}) order by percent desc, level_scores.coins desc limit 200", level_id do |rs|
DATABASE.query_each "select percent, level_scores.coins, set_at, users.username, users.id, users.icon_type, users.color1, users.color2, users.cube, users.ship, users.ball, users.ufo, users.wave, users.robot, users.spider, users.special, users.account_id from level_scores join users on level_scores.account_id = users.account_id where (#{where_query.join(") and (")}) order by percent desc, level_scores.coins desc limit 200", level_id, daily_id do |rs|
i += 1
percent = rs.read(Int32)
coins = rs.read(Int32)

41
src/lib/dailies.cr Normal file
View File

@ -0,0 +1,41 @@
include CrystalGauntlet
module CrystalGauntlet::Dailies
extend self
# todo: merge the two tables into one maybe
WEEKLY_OFFSET = 100001
def grab_new_level(weekly : Bool, prev = Time.utc) : {Int32 | Nil, Int32 | Nil, Int32 | Nil}
begin
level_id = DATABASE.query_one("select level_id from #{weekly ? "weekly_queue" : "daily_queue"} order by idx desc limit 1", as: {Int32})
rescue
return {nil, nil, nil}
else
next_id = IDs.get_next_id(weekly ? "weekly_levels" : "daily_levels")
# todo: configurable?
timespan = weekly ? 1.weeks : 1.days
expires_at = prev + timespan
DATABASE.exec("insert into #{weekly ? "weekly_levels" : "daily_levels"} (level_id, idx, expires_at) values (?, ?, ?)", level_id, next_id, expires_at.to_s(Format::TIME_FORMAT))
return {level_id, timespan.total_seconds.to_i, next_id}
end
end
def fetch_current_level(weekly : Bool) : {Int32 | Nil, Int32 | Nil, Int32 | Nil}
begin
level_id, expires_at, idx = DATABASE.query_one("select level_id, expires_at, idx from #{weekly ? "weekly_levels" : "daily_levels"} order by idx desc limit 1", as: {Int32, String, Int32})
rescue
# make up a brand new daily; using current time because no previous ones have existed
level_id, expires, idx = grab_new_level(weekly)
else
# check if it has expired, roll a new one if so
expires = (Time.utc - Time.parse(expires_at, Format::TIME_FORMAT, Time::Location::UTC)).total_seconds.to_i
if expires <= 0
level_id, expires, idx = grab_new_level(weekly, Time.parse(expires_at, Format::TIME_FORMAT, Time::Location::UTC))
end
end
return level_id, expires, idx
end
end