fix(multiplayer): enforce ranked tier enum

This commit is contained in:
Josh Creek
2026-09-01 22:39:06 +01:00
parent c580e46125
commit dac414274e
3 changed files with 8 additions and 1 deletions
+5 -1
View File
@@ -29,7 +29,7 @@ func apply(payload: Dictionary) -> bool:
var next_volatility := float(payload["volatility"])
var next_games := int(payload["ranked_games"])
var next_tier := String(payload["tier"])
if not is_finite(next_rating) or not is_finite(next_rd) or not is_finite(next_volatility) or not _valid_nonnegative_integer(payload["ranked_games"]) or next_rating < 0.0 or next_rd < 0.0 or next_volatility < 0.0 or next_games < 0 or next_tier.is_empty():
if not is_finite(next_rating) or not is_finite(next_rd) or not is_finite(next_volatility) or not _valid_nonnegative_integer(payload["ranked_games"]) or next_rating < 0.0 or next_rd < 0.0 or next_volatility < 0.0 or next_games < 0 or not _valid_tier(next_tier):
return _reject("Profile response contains invalid values")
rating = next_rating
rd = next_rd
@@ -77,6 +77,10 @@ static func _valid_nonnegative_integer(value: Variant) -> bool:
return false
static func _valid_tier(value: String) -> bool:
return value in ["PROVISIONAL", "BRONZE", "SILVER", "GOLD", "PLATINUM", "DIAMOND"]
func set_error(reason: String) -> void:
available = false
error_message = reason
@@ -157,6 +157,7 @@ func test_ranked_profile_is_backend_display_data_and_rejects_unsafe_values() ->
assert_true(not profile.apply({"rating": -1.0, "rd": 200.0, "volatility": 0.06, "ranked_games": 3, "tier": "GOLD", "provisional": false}), "negative rating is rejected")
assert_true(not profile.available, "unsafe response is not displayed")
assert_true(not profile.apply({"rating": 1500.0, "rd": 200.0, "volatility": 0.06, "ranked_games": 3, "tier": "", "provisional": false}), "empty tier is rejected")
assert_true(not profile.apply({"rating": 1500.0, "rd": 200.0, "volatility": 0.06, "ranked_games": 3, "tier": "MASTER", "provisional": false}), "unknown tier is rejected")
assert_true(not profile.apply({"rating": 1500.0, "rd": 200.0, "volatility": 0.06, "ranked_games": 3, "tier": "GOLD", "provisional": "false"}), "string boolean is rejected")
assert_true(not profile.apply({"rating": 1500.0, "rd": 200.0, "volatility": 0.06, "ranked_games": 3.5, "tier": "GOLD", "provisional": false}), "fractional ranked games is rejected")
+2
View File
@@ -1587,6 +1587,8 @@ Ticket timestamp normalization now preserves an invalid sentinel for malformed o
Ranked profile projection now rejects fractional `ranked_games` values instead of silently truncating them, matching the OpenAPI integer contract.
Ranked profile projection now enforces the OpenAPI tier enum, rejecting unknown tier labels before they reach the HUD.
Signed MatchNet claims now also require exact JSON string/integer types for every identity, protocol, expiry, slot, team, and generation field; string-number coercion is rejected before canonical signature verification.
Presentation progress: a shared `Game/themes/cosmic_clash_theme.tres` now gives the menu, lobby, matchmaking, and settings surfaces consistent button, input, option, and label styling. The custom-font portion of `TODO.md` remains open until a distributable font asset is selected.