diff --git a/public/assets/icons/gd/download.png b/public/assets/icons/gd/download.png new file mode 100644 index 0000000..802ccef Binary files /dev/null and b/public/assets/icons/gd/download.png differ diff --git a/public/assets/icons/gd/like.png b/public/assets/icons/gd/like.png new file mode 100644 index 0000000..bf296c6 Binary files /dev/null and b/public/assets/icons/gd/like.png differ diff --git a/public/template/account_management.ecr b/public/template/account_management.ecr index 82d1cb2..89eedaf 100644 --- a/public/template/account_management.ecr +++ b/public/template/account_management.ecr @@ -128,7 +128,7 @@ - "> + "> <%= File.read("public/assets/icons/bell.svg") %> diff --git a/public/template/index.ecr b/public/template/index.ecr index cf0a0ac..0c56a70 100644 --- a/public/template/index.ecr +++ b/public/template/index.ecr @@ -38,7 +38,7 @@
RANK + | PERCENT + | COINS + | + | PLAYER + | TIME + |
---|---|---|---|---|---|
#<%= rank %> | +<%= percent %>% | ++ | + " class="player-icon"> + | ++ <%= username %> + | ++ + | +
+ No scores + | +
comments go here etc etc
+ + + diff --git a/public/template/levels.ecr b/public/template/levels.ecr index a433090..beae059 100644 --- a/public/template/levels.ecr +++ b/public/template/levels.ecr @@ -55,7 +55,10 @@ display: flex; flex-direction: column; align-items: center; - gap: 1em + gap: 1em; + } + .level a { + text-decoration: none; } @@ -69,8 +72,8 @@ "" %> <%- end -%> diff --git a/src/endpoints/levels/uploadLevel.cr b/src/endpoints/levels/uploadLevel.cr index 4ad72e7..31ac167 100644 --- a/src/endpoints/levels/uploadLevel.cr +++ b/src/endpoints/levels/uploadLevel.cr @@ -64,18 +64,16 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(context : HTTP::Server::C LOG.debug { "allowed objects: #{allowed_objects.inspect}" } if forbidden_obj = level_objects.find do |obj| - if !obj.has_key?("1") - false - end - - id = obj["1"].to_i - if allowed_objects.size > 0 - if !allowed_objects.includes?(id) - true - end - else - if forbidden_objects.includes?(id) - true + if obj.has_key?("1") + id = obj["1"].to_i + if allowed_objects.size > 0 + if !allowed_objects.includes?(id) + true + end + else + if forbidden_objects.includes?(id) + true + end end end end @@ -83,12 +81,12 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(context : HTTP::Server::C return "-1" end - if exploit_obj = level_objects.find do |obj| + if exploit_obj = level_objects.find { |obj| # target color ID - (obj.has_key?("23") && obj["23"].to_i < 0 || obj["23"].to_i > 1100) || + (obj.has_key?("23") && (obj["23"].to_i < 0 || obj["23"].to_i > 1100)) || # target group ID - (obj.has_key?("51") && obj["51"].to_i < 0 || obj["51"].to_i > 1100) - end + (obj.has_key?("51") && (obj["51"].to_i < 0 || obj["51"].to_i > 1100)) + } LOG.info { "preventing upload of level attempting to exploit invalid color/group IDs" } return "-1" end @@ -147,7 +145,7 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(context : HTTP::Server::C # create new level next_id = IDs.get_next_id("levels") - DATABASE.exec("insert into levels (id, name, user_id, description, original, game_version, binary_version, password, requested_stars, unlisted, version, extra_data, level_info, editor_time, editor_time_copies, song_id, length, objects, coins, has_ldm, two_player) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", next_id, Clean.clean_special(params["levelName"])[..20-1], user_id, description[..140-1], params["original"].to_i, params["gameVersion"].to_i, (params["binaryVersion"]? || "0").to_i, params["password"] == "0" ? nil : params["password"].to_i, requested_stars, (params["unlisted"]? || "0").to_i == 1, params["levelVersion"].to_i, Clean.clean_special(extraString), Clean.clean_b64(params["levelInfo"]? || ""), (params["wt"]? || "0").to_i, (params["wt2"]? || "0").to_i, song_id.to_i, level_length, objects, coins, (params["ldm"]? || "0").to_i == 1, two_player) + DATABASE.exec("insert into levels (id, name, user_id, description, original, game_version, binary_version, password, requested_stars, unlisted, version, extra_data, level_info, editor_time, editor_time_copies, song_id, length, objects, coins, has_ldm, two_player) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", next_id, Clean.clean_basic(params["levelName"])[..20-1], user_id, description[..140-1], params["original"].to_i, params["gameVersion"].to_i, (params["binaryVersion"]? || "0").to_i, params["password"] == "0" ? nil : params["password"].to_i, requested_stars, (params["unlisted"]? || "0").to_i == 1, params["levelVersion"].to_i, Clean.clean_special(extraString), Clean.clean_b64(params["levelInfo"]? || ""), (params["wt"]? || "0").to_i, (params["wt2"]? || "0").to_i, song_id.to_i, level_length, objects, coins, (params["ldm"]? || "0").to_i == 1, two_player) File.write(DATA_FOLDER / "levels" / "#{next_id.to_s}.lvl", Base64.decode(params["levelString"])) diff --git a/src/template_endpoints/levels.cr b/src/template_endpoints/levels.cr index 40913b8..52d7e62 100644 --- a/src/template_endpoints/levels.cr +++ b/src/template_endpoints/levels.cr @@ -6,7 +6,7 @@ levels_per_page = 10 CrystalGauntlet.template_endpoints[{ name: "list_levels", - path: "/tools/levels", + path: "/levels", methods: ["get"] }] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) { context.response.content_type = "text/html" @@ -15,3 +15,24 @@ CrystalGauntlet.template_endpoints[{ levels = DATABASE.query_all("select levels.id, name, users.username, levels.community_difficulty, levels.difficulty, levels.demon_difficulty, levels.featured, levels.epic from levels left join users on levels.user_id = users.id order by levels.id desc limit #{levels_per_page} offset #{page * levels_per_page}", as: {Int32, String, String, Int32?, Int32?, Int32?, Bool, Bool}) ECR.embed("./public/template/levels.ecr", context.response) } + +CrystalGauntlet.template_endpoints[{ + name: "level_page", + path: "/levels/{id<\\d+>}", + methods: ["get"] +}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) { + context.response.content_type = "text/html" + id = params["id"].as(String).to_i + + begin + name, username, difficulty_community, difficulty_set, demon_difficulty_int, featured, epic, downloads, likes, stars, description, song_id, song_name, song_author, song_url, song_author_url = DATABASE.query_one("select levels.name, users.username, community_difficulty, difficulty, demon_difficulty, featured, epic, downloads, likes, levels.stars, description, song_id, song_data.name, song_authors.name, songs.url, song_authors.source from levels left join users on levels.user_id = users.id left join songs on songs.id = levels.song_id left join song_data on song_data.id = levels.song_id left join song_authors on song_data.author_id = song_authors.id where levels.id = ?", id, as: {String, String, Int32?, Int32?, Int32?, Bool, Bool, Int32, Int32, Int32?, String, Int32, String?, String?, String?, String?}) + rescue err + LOG.error {"whar.... #{err}"} + context.response.status = HTTP::Status::NOT_FOUND + return + end + + scores = DATABASE.query_all("select distinct percent, level_scores.coins, users.username, users.icon_type, users.color1, users.color2, users.cube, users.ship, users.ball, users.ufo, users.wave, users.robot, users.spider, users.special, set_at from level_scores join users on level_scores.account_id = users.account_id where level_id = ? order by percent desc, level_scores.coins desc, set_at limit 25", id, as: {Int32, Int32, String, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, String}) + + ECR.embed("./public/template/level.ecr", context.response) +}