diff --git a/db/migrations/4_level_scores.sql b/db/migrations/4_level_scores.sql new file mode 100644 index 0000000..963f9dd --- /dev/null +++ b/db/migrations/4_level_scores.sql @@ -0,0 +1,18 @@ +-- +migrate up +CREATE TABLE level_scores ( + account_id INTEGER NOT NULL references accounts(id), + level_id INTEGER NOT NULL references levels(id), + daily_id INTEGER, + + percent INTEGER NOT NULL, + attempts INTEGER NOT NULL DEFAULT 0, + clicks INTEGER NOT NULL DEFAULT 0, + coins INTEGER NOT NULL DEFAULT 0, + progress TEXT NOT NULL DEFAULT "", + time INTEGER NOT NULL, + + set_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'now')) +); + +-- +migrate down +DROP TABLE level_scores; \ No newline at end of file diff --git a/src/endpoints/levels/levelScores.cr b/src/endpoints/levels/levelScores.cr new file mode 100644 index 0000000..0a490a4 --- /dev/null +++ b/src/endpoints/levels/levelScores.cr @@ -0,0 +1,103 @@ +require "uri" + +include CrystalGauntlet + +CrystalGauntlet.endpoints["/getGJLevelScores211.php"] = ->(body : String): String { + params = URI::Params.parse(body) + puts params.inspect + + account_id = Accounts.get_account_id_from_params(params) + if !account_id || !Accounts.verify_gjp(account_id, params["gjp"]) + return "-1" + end + + level_id = params["levelID"].to_i32 + daily_id = params["s10"].to_i32 + if daily_id == 0 + daily_id = nil + end + + if params["percent"]? && params["percent"]? != "0" + # set score + + attempts = params["s1"].to_i - 8354 + clicks = params["s2"].to_i - 3991 + time = params["s3"].to_i - 4085 + # todo: fix + # progress = XorCrypt.encrypt_string(GDBase64.decode_string(params["s6"]), "41274") + coins = params["s9"].to_i - 5819 + if coins > 3 + return "-1" + end + percent = params["percent"].to_i + if percent > 100 + 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 + # 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}) + + 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, "", time, Time.utc.to_s("%Y-%m-%d %H:%M:%S"), account_id, level_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, "", time) + end + end + + # return set scores + + type = params["type"]? ? params["type"] : "1" + + case type + when 0 + # friends + # todo + when 1 + # global + # todo + when 2 + # weekly + # todo + end + + scores = [] of String + + 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 level_id = ? order by percent, level_scores.coins desc", level_id do |rs| + percent = rs.read(Int32) + coins = rs.read(Int32) + set_at = rs.read(String) + + username = rs.read(String) + user_id = rs.read(Int32) + icon_type = rs.read(Int32) + color1 = rs.read(Int32) + color2 = rs.read(Int32) + + icon_value = [rs.read(Int32), rs.read(Int32), rs.read(Int32), rs.read(Int32), rs.read(Int32), rs.read(Int32), rs.read(Int32)][icon_type] + + special = rs.read(Int32) + + account_id = rs.read(Int32) + + scores << Format.fmt_hash({ + 1 => username, + 2 => user_id, + 9 => icon_value, + 10 => color1, + 11 => color2, + 14 => icon_type, + 15 => special, + 16 => account_id, + 3 => percent, + 6 => percent == 100 ? 1 : (percent > 75 ? 2 : 3), # ??? + 13 => coins, + 42 => set_at + }) + end + + scores.join("|") +} diff --git a/src/endpoints/levels/uploadLevel.cr b/src/endpoints/levels/uploadLevel.cr index cc21493..77bc60b 100644 --- a/src/endpoints/levels/uploadLevel.cr +++ b/src/endpoints/levels/uploadLevel.cr @@ -6,8 +6,9 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(body : String): String { params = URI::Params.parse(body) puts params.inspect + # todo: green user fixes? pretty please? ext_id = Accounts.get_ext_id_from_params(params) - if ext_id == "-1" || !Accounts.verify_gjp(ext_id, params["gjp"]) + if !ext_id || !Accounts.verify_gjp(ext_id.to_i, params["gjp"]) return "-1" end user_id = Accounts.get_user_id(ext_id) diff --git a/src/endpoints/users/updateUser.cr b/src/endpoints/users/updateUser.cr index f54da34..3a4ee19 100644 --- a/src/endpoints/users/updateUser.cr +++ b/src/endpoints/users/updateUser.cr @@ -8,17 +8,18 @@ CrystalGauntlet.endpoints["/updateGJUserScore22.php"] = ->(body : String): Strin params = URI::Params.parse(body) puts params.inspect - account_id = Accounts.get_ext_id_from_params(params) - if !Accounts.verify_gjp(account_id, params["gjp"]) + account_id = Accounts.get_account_id_from_params(params) + if !account_id || !Accounts.verify_gjp(account_id, params["gjp"]) return "-1" end - user_id = Accounts.get_user_id(account_id) + user_id = Accounts.get_user_id(account_id.to_s) # todo: prevent username change unless it's a capitalization change # todo: update account username casing w/ user username # todo: keep track of stat changes to look out for leaderboard cheating & whatnot # todo: cap out demon count at the current amount of uploaded demons? same for stars & user coins. could be expensive though + # todo: cap icon type DATABASE.exec("update users set username=?, stars=?, demons=?, coins=?, user_coins=?, diamonds=?, icon_type=?, color1=?, color2=?, cube=?, ship=?, ball=?, ufo=?, wave=?, robot=?, spider=?, explosion=?, special=?, glow=?, last_played=? where id=?", params["userName"], params["stars"].to_i32, params["demons"].to_i32, params["coins"].to_i32, params["userCoins"].to_i32, params["diamonds"].to_i32, params["iconType"].to_i32, params["color1"].to_i32, params["color2"].to_i32, params["accIcon"].to_i32, params["accShip"].to_i32, params["accBall"].to_i32, params["accBird"].to_i32, params["accDart"].to_i32, params["accRobot"].to_i32, params["accSpider"].to_i32, params["accExplosion"].to_i32, params["special"].to_i32, params["accGlow"].to_i32, Time.utc.to_s("%Y-%m-%d %H:%M:%S"), user_id) diff --git a/src/lib/accounts.cr b/src/lib/accounts.cr index db0f520..358913c 100644 --- a/src/lib/accounts.cr +++ b/src/lib/accounts.cr @@ -6,16 +6,25 @@ include CrystalGauntlet module CrystalGauntlet::Accounts extend self - def get_ext_id_from_params(params : URI::Params) : String + def get_account_id_from_params(params : URI::Params) : Int32 | Nil + if params["accountID"]? && params["accountID"]? != "0" + # todo: validate password + params["accountID"].to_i32 + else + nil + end + end + + def get_ext_id_from_params(params : URI::Params) : String | Nil return "1" if params.has_key?("udid") && params["udid"] != "" # todo: numeric id check params["udid"] - elsif params.has_key?("account_id") && params["account_id"] != "" && params["account_id"] != "0" + elsif params.has_key?("accountID") && params["accountID"] != "" && params["accountID"] != "0" # todo: validate password - params["account_id"] + params["accountID"] else - "-1" + nil end end @@ -29,7 +38,7 @@ module CrystalGauntlet::Accounts end end - def verify_gjp(account_id : String, gjp : String) : Bool + def verify_gjp(account_id : Int32, gjp : String) : Bool hash = DATABASE.scalar("select password from accounts where id = ?", account_id).as(String) bcrypt = Crypto::Bcrypt::Password.new(hash) bcrypt.verify(GJP.decrypt(gjp))