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 @@
  • The Git repository
  • Account stuff
  • Song reuploading and searching
  • -
  • Levels and level reuploading
  • +
  • Levels and level reuploading
  • <%- if config_get("sessions.allow").as(Bool | Nil) -%>
  • The session creation page (for accessing features in 1.9)
  • <%- end -%> diff --git a/public/template/level.ecr b/public/template/level.ecr new file mode 100644 index 0000000..ec2d343 --- /dev/null +++ b/public/template/level.ecr @@ -0,0 +1,258 @@ + + + + + + + + <%= name %> by <%= username %> + + + + +
    + +
    +
    + <%= + difficulty = (difficulty_set || difficulty_community).try { |n| LevelDifficulty.new(n) } + demon_difficulty = demon_difficulty_int.try { |n| DemonDifficulty.new(n) } + "" + %> + <%- if stars -%> +
    + <%= stars %> +
    + <%- end -%> +
    +
    +
    +
    + <%= name %> +
    +
    + <%= downloads %> downloads <%= likes %> likes +
    +
    + +
    +
    + +
    +
    DESCRIPTION
    +
    + <%= description != "" ? HTML.escape(description) : "No description provided" %> +
    +
    + +
    +
    SONG
    +
    + <%- if song_name -%> + +
    <%= song_name %>
    + <%- if song_author && song_author != "" -%> +
    by <%= song_author %>
    + <%- else -%> +
    unknown artist
    + <%- end -%> + <%- else -%> + idk + <%- end -%> +
    +
    + + + + + + <% + rank = 0 + scores.each do |percent, coins, username, icon_type, color1, color2, cube, ship, ball, ufo, wave, robot, spider, special, set_at| + rank = rank + 1 + + icon_value = [cube, ship, ball, ufo, wave, robot, spider][icon_type] + type_str = ["cube", "ship", "ball", "ufo", "wave", "robot", "spider"][icon_type] + + set_at_date = Time.parse(set_at, Format::TIME_FORMAT, Time::Location::UTC) + %> + + + + + + + + + <%- end -%> + <%- if scores.size == 0 -%> + + + + <%- end -%> +
    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 @@ "" %>
    - <%= name %>#<%= id %> - by <%= username %>
    + <%= name %>#<%= id %> + by <%= username %>
    <%- 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) +}