diff --git a/db/migrations/1_levels.sql b/db/migrations/1_levels.sql index e89db49..c324f27 100644 --- a/db/migrations/1_levels.sql +++ b/db/migrations/1_levels.sql @@ -21,7 +21,8 @@ CREATE TABLE levels ( extra_data BLOB NOT NULL, level_info BLOB NOT NULL, - -- checksums, presumably + -- times spent in the editor + -- wt1 doesn't count copies, wt2 does wt1 TEXT NOT NULL, wt2 TEXT NOT NULL, diff --git a/src/endpoints/levels/downloadLevel.cr b/src/endpoints/levels/downloadLevel.cr index d0acc9b..df9bf0b 100644 --- a/src/endpoints/levels/downloadLevel.cr +++ b/src/endpoints/levels/downloadLevel.cr @@ -9,7 +9,7 @@ CrystalGauntlet.endpoints["/downloadGJLevel22.php"] = ->(body : String): String response = [] of String - DATABASE.query("select levels.id, levels.name, levels.level_data, 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 from levels join users on levels.user_id = users.id where levels.id = ?", params["levelID"].to_i32) do |rs| + DATABASE.query("select levels.id, levels.name, levels.level_data, 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, wt1, wt2 from levels join users on levels.user_id = users.id where levels.id = ?", params["levelID"].to_i32) do |rs| if rs.move_next id = rs.read(Int32) name = rs.read(String) @@ -48,6 +48,9 @@ CrystalGauntlet.endpoints["/downloadGJLevel22.php"] = ->(body : String): String user_account_id = rs.read(Int32 | Nil) user_registered = rs.read(Bool) + wt1 = rs.read(String) + wt2 = rs.read(String) + xor_pass = "0" if !password password = "0" @@ -65,36 +68,47 @@ CrystalGauntlet.endpoints["/downloadGJLevel22.php"] = ->(body : String): String 4 => level_data, 5 => version, 6 => user_id, + # this is suppoused to be the amount of people who have + # voted on a level, but this is unused by the game, so + # we just always tell the game 10 people have voted 8 => 10, - 9 => difficulty ? difficulty.to_star_difficulty : 0, # 0=N/A 10=EASY 20=NORMAL 30=HARD 40=HARDER 50=INSANE 50=AUTO 50=DEMON + # 0=N/A 10=EASY 20=NORMAL 30=HARD 40=HARDER 50=INSANE 50=AUTO 50=DEMON + # divided by above value, which is why its multiplied by 10 + 9 => (difficulty ? difficulty.to_star_difficulty : 0).not_nil! * 10, 10 => downloads, - 11 => 1, 12 => !Songs.is_custom_song(song_id) ? song_id : 0, 13 => game_version, + # likes - dislikes 14 => likes, + # dislikes - likes + 16 => -likes, + 15 => length, 17 => difficulty && difficulty.demon?, - # 0 for n/a, 10 for easy, 20, for medium, ... - 43 => (demon_difficulty || DemonDifficulty::Hard).to_demon_difficulty, - 25 => difficulty && difficulty.auto?, 18 => stars || 0, 19 => featured, - 42 => epic, - 45 => objects, - 15 => length, - 30 => original || 0, - 31 => two_player, + 25 => difficulty && difficulty.auto?, + 27 => xor_pass, + # todo + # upload date 28 => "1", + # update date 29 => "1", + 30 => original || 0, + 31 => two_player,26 => params.has_key?("extras") ? level_info : nil, 35 => Songs.is_custom_song(song_id) ? song_id : 0, 36 => extra_data, 37 => coins, 38 => rated_coins, 39 => requested_stars || 0, - 46 => 1, - 47 => 2, 40 => has_ldm, - 27 => xor_pass, - 26 => params.has_key?("extras") ? level_info : nil + 42 => epic, + # 0 for n/a, 10 for easy, 20, for medium, ... + 43 => (demon_difficulty || DemonDifficulty::Hard).to_demon_difficulty, + # todo + 44 => false, + 45 => objects, + 46 => wt1, + 47 => wt2 }) response << Hashes.gen_solo(level_data) diff --git a/src/endpoints/levels/getLevelComments.cr b/src/endpoints/levels/getLevelComments.cr index a01e8db..d91ee38 100644 --- a/src/endpoints/levels/getLevelComments.cr +++ b/src/endpoints/levels/getLevelComments.cr @@ -38,31 +38,30 @@ CrystalGauntlet.endpoints["/getGJComments21.php"] = ->(body : String): String { special = rs.read(Int32) - comment_str = Format.fmt_comment({ - 2 => GDBase64.encode(comment), - 3 => user_id, - 4 => likes, - 5 => 0, - 7 => likes < -3, - 9 => Time.parse(created_at, Format::TIME_FORMAT, Time::Location::UTC), - 6 => id, - 10 => percent || 0, - 12 => "0,0,0", # todo: badge - 11 => "0", - }) - - comment_str += ":" + Format.fmt_comment({ - 1 => username || "-", - 7 => 1, - 9 => icon_value, - 10 => color1, - 11 => color2, - 14 => icon_type, - 15 => special, - 16 => account_id || udid - }) - - users_str << comment_str + users_str << [ + Format.fmt_comment({ + 2 => GDBase64.encode(comment), + 3 => user_id, + 4 => likes, + 5 => 0, + 6 => id, + 7 => likes < -3, + 8 => account_id, + 9 => Time.parse(created_at, Format::TIME_FORMAT, Time::Location::UTC), + 10 => percent || 0, + 11 => "0", + 12 => "0,0,0", # todo: badge + }), + Format.fmt_comment({ + 1 => username || "-", + 9 => icon_value, + 10 => color1, + 11 => color2, + 14 => icon_type, + 15 => special, + 16 => account_id || udid + }) + ].join(":") end end diff --git a/src/endpoints/levels/getLevels.cr b/src/endpoints/levels/getLevels.cr index 6c673d4..cff2dcd 100644 --- a/src/endpoints/levels/getLevels.cr +++ b/src/endpoints/levels/getLevels.cr @@ -182,7 +182,7 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(body : String): String { hash_data = [] of Tuple(Int32, Int32, Bool) # fucking help - DATABASE.query_all("select levels.id, levels.name, 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 #{query_base} limit #{levels_per_page} offset #{page_offset}", as: {Int32, String, Int32, String, Int32 | Nil, Int32, Int32 | Nil, Int32, Int32, Int32, Int32, Int32, Bool, Bool, Int32, Int32, Int32 | Nil, Int32 | Nil, Int32 | Nil, Int32 | Nil, Bool, Bool, Bool, String, String | Nil, Int32 | Nil, Bool}).map() do |id, name, user_id, description, original, game_version, requested_stars, version, song_id, length, objects, coins, has_ldm, two_player, downloads, likes, set_difficulty_int, community_difficulty_int, demon_difficulty_int, stars, featured, epic, rated_coins, user_username, user_udid, user_account_id, user_registered| + DATABASE.query_all("select levels.id, levels.name, 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, wt1, wt2 #{query_base} limit #{levels_per_page} offset #{page_offset}", as: {Int32, String, Int32, String, Int32 | Nil, Int32, Int32 | Nil, Int32, Int32, Int32, Int32, Int32, Bool, Bool, Int32, Int32, Int32 | Nil, Int32 | Nil, Int32 | Nil, Int32 | Nil, Bool, Bool, Bool, String, String | Nil, Int32 | Nil, Bool, String, String}).map() do |id, name, user_id, description, original, game_version, requested_stars, version, song_id, length, objects, coins, has_ldm, two_player, downloads, likes, set_difficulty_int, community_difficulty_int, demon_difficulty_int, stars, featured, epic, rated_coins, user_username, user_udid, user_account_id, user_registered, wt1, wt2| set_difficulty = set_difficulty_int && LevelDifficulty.new(set_difficulty_int) community_difficulty = community_difficulty_int && LevelDifficulty.new(community_difficulty_int) difficulty = set_difficulty || community_difficulty @@ -192,33 +192,44 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(body : String): String { results << Format.fmt_hash({ 1 => id, 2 => name, + 3 => GDBase64.encode(description), 5 => version, 6 => user_id, + # this is suppoused to be the amount of people who have + # voted on a level, but this is unused by the game, so + # we just always tell the game 10 people have voted 8 => 10, - 9 => difficulty ? difficulty.to_star_difficulty : 0, # 0=N/A 10=EASY 20=NORMAL 30=HARD 40=HARDER 50=INSANE 50=AUTO 50=DEMON + # 0=N/A 10=EASY 20=NORMAL 30=HARD 40=HARDER 50=INSANE 50=AUTO 50=DEMON + # divided by above value, which is why its multiplied by 10 + 9 => (difficulty ? difficulty.to_star_difficulty : 0).not_nil! * 10, 10 => downloads, 12 => !Songs.is_custom_song(song_id) ? song_id : 0, 13 => game_version, + # likes - dislikes 14 => likes, + # dislikes - likes + 16 => -likes, + 15 => length, 17 => difficulty && difficulty.demon?, - # 0 for n/a, 10 for easy, 20, for medium, ... - 43 => (demon_difficulty || DemonDifficulty::Hard).to_demon_difficulty, - 25 => difficulty && difficulty.auto?, 18 => stars || 0, 19 => featured, - 42 => epic, - 45 => objects, - 3 => GDBase64.encode(description), - 15 => length, + # 0 for n/a, 10 for easy, 20, for medium, ... + 25 => difficulty && difficulty.auto?, 30 => original || 0, 31 => two_player, + 35 => Songs.is_custom_song(song_id) ? song_id : 0, 37 => coins, 38 => rated_coins, 39 => requested_stars || 0, - 46 => 1, - 47 => 2, 40 => has_ldm, - 35 => Songs.is_custom_song(song_id) ? song_id : 0, + 42 => epic, + 43 => (demon_difficulty || DemonDifficulty::Hard).to_demon_difficulty, + # is in gauntlet + # todo + 44 => false, + 45 => objects, + 46 => wt1, + 47 => wt2 }) users << "#{user_id}:#{user_username}:#{user_registered ? user_account_id : user_udid}" diff --git a/src/endpoints/levels/uploadLevel.cr b/src/endpoints/levels/uploadLevel.cr index 4fbca5b..dbcabe9 100644 --- a/src/endpoints/levels/uploadLevel.cr +++ b/src/endpoints/levels/uploadLevel.cr @@ -44,6 +44,8 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(body : String): String { # todo: verify object count, coins and twoplayer (i'm sure it's possible) + # todo: check seed2? + if DATABASE.scalar("select count(*) from levels where id = ? and user_id = ?", params["levelID"], params["accountID"]).as(Int64) > 0 # update existing level # todo diff --git a/src/endpoints/songs/getSongInfo.cr b/src/endpoints/songs/getSongInfo.cr index f7b477d..2d93712 100644 --- a/src/endpoints/songs/getSongInfo.cr +++ b/src/endpoints/songs/getSongInfo.cr @@ -24,10 +24,10 @@ CrystalGauntlet.endpoints["/getGJSongInfo.php"] = ->(body : String): String { 3 => song_author_id, 4 => song_author_name, 5 => (song_size || 0) / (1000 * 1000), - 6 => "", + 6 => "", # yt video id; unused i think? + 7 => "", # yt video url; unused also?? + 8 => "1", # if the song is verified/scouted 10 => song_download || "", - 7 => "", - 8 => "1" }) end else diff --git a/src/endpoints/users/getProfileComments.cr b/src/endpoints/users/getProfileComments.cr index 37107fd..4d6fbfb 100644 --- a/src/endpoints/users/getProfileComments.cr +++ b/src/endpoints/users/getProfileComments.cr @@ -27,10 +27,10 @@ CrystalGauntlet.endpoints["/getGJAccountComments20.php"] = ->(body : String): St 2 => Base64.encode(comment).strip("\n"), 3 => account_id, 4 => likes, - 5 => 0, + 5 => 0, # dislikes; unused + 6 => id, 7 => likes < -3, # todo: config? 9 => Time.parse(created_at, Format::TIME_FORMAT, Time::Location::UTC), - 6 => id }) end end diff --git a/src/endpoints/users/getUser.cr b/src/endpoints/users/getUser.cr index f07b834..502ee2a 100644 --- a/src/endpoints/users/getUser.cr +++ b/src/endpoints/users/getUser.cr @@ -43,17 +43,17 @@ CrystalGauntlet.endpoints["/getGJUserInfo20.php"] = ->(body : String): String { return CrystalGauntlet::Format.fmt_hash({ 1 => username, 2 => user_id, - 13 => coins, - 17 => user_coins, - 10 => color1, - 11 => color2, 3 => stars, - 46 => diamonds, 4 => demons, 8 => creator_points, + 10 => color1, + 11 => color2, + 13 => coins, + 16 => id, + 17 => user_coins, + # todo: messages can actually be disabled for _everyone_; this is actually an enum (0: all, 1: only friends, 2: none) 18 => !messages_enabled, 19 => !friend_requests_enabled, - 50 => !comments_enabled, 20 => youtube_url || "", 21 => cube, 22 => ship, @@ -62,10 +62,9 @@ CrystalGauntlet.endpoints["/getGJUserInfo20.php"] = ->(body : String): String { 25 => wave, 26 => robot, 28 => glow, - 43 => spider, - 47 => explosion, + # registered or not; always 1 here + 29 => 1, 30 => 1, # rank; todo - 16 => id, # 31 = isnt (0) or is (1) friend or (3) incoming request or (4) outgoing request # todo 31 => 0, @@ -73,11 +72,21 @@ CrystalGauntlet.endpoints["/getGJUserInfo20.php"] = ->(body : String): String { # 32 => id, # 35 => comment, # 37 => date, + # todo: how many messages you have; exclusive to if you're viewing your own profile + 38 => 0, + # todo: above, but friend requests + 39 => 0, + # todo: above: but how many new friends the user has + 40 => 0, + 43 => spider, 44 => twitter_url || "", 45 => twitch_url || "", - 29 => 1, + 46 => diamonds, + 48 => explosion, # badge, todo - 49 => 0 + 49 => 0, + # todo: this is actually also an enum (0: all, 1: only friends, 2: none) + 50 => !comments_enabled, }) else "-1" diff --git a/src/endpoints/users/getUsers.cr b/src/endpoints/users/getUsers.cr index e1e739e..c11da7e 100644 --- a/src/endpoints/users/getUsers.cr +++ b/src/endpoints/users/getUsers.cr @@ -32,17 +32,17 @@ CrystalGauntlet.endpoints["/getGJUsers20.php"] = ->(body : String): String { results << Format.fmt_hash({ 1 => username, 2 => id, - 13 => coins, - 17 => user_coins, + 3 => stars, + 4 => demons, + 8 => creator_points, 9 => icon_value, 10 => color1, 11 => color2, + 13 => coins, 14 => icon_type, 15 => special, 16 => account_id || udid, - 3 => stars, - 8 => creator_points, - 4 => demons + 17 => user_coins, }) end end diff --git a/src/enums.cr b/src/enums.cr index 5f8342f..3b5f1e9 100644 --- a/src/enums.cr +++ b/src/enums.cr @@ -19,19 +19,19 @@ module CrystalGauntlet def to_star_difficulty case self when .auto? - 50 + 5 when .easy? - 10 + 1 when .normal? - 20 + 2 when .hard? - 30 + 3 when .harder? - 40 + 4 when .insane? - 50 + 5 when .demon? - 50 + 5 end end end