class_name RankedProfileState extends RefCounted # Read-only server projection. The client deliberately stores no tier bands # or rating formula: it displays the backend's committed view verbatim after # validating the shape and numeric safety of the response. var available := false var rating := 0.0 var rd := 0.0 var volatility := 0.0 var ranked_games := 0 var tier := "" var provisional := false var season_id := "" var season_ends_at_unix := 0 var error_message := "" func apply(payload: Dictionary) -> bool: var required := ["rating", "rd", "volatility", "ranked_games", "tier", "provisional"] for key in required: if not payload.has(key): return _reject("Profile response is missing " + key) if not (payload["rating"] is int or payload["rating"] is float) or not (payload["rd"] is int or payload["rd"] is float) or not (payload["volatility"] is int or payload["volatility"] is float) or not (payload["ranked_games"] is int or payload["ranked_games"] is float) or not payload["tier"] is String or not payload["provisional"] is bool: return _reject("Profile response contains invalid types") var next_rating := float(payload["rating"]) var next_rd := float(payload["rd"]) 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 not _valid_tier(next_tier): return _reject("Profile response contains invalid values") rating = next_rating rd = next_rd volatility = next_volatility ranked_games = next_games tier = next_tier provisional = bool(payload["provisional"]) season_id = "" if payload.has("season_id"): if not payload["season_id"] is String or not is_valid_opaque_id(String(payload["season_id"])): return _reject("Profile response contains invalid season identifier") season_id = String(payload["season_id"]) season_ends_at_unix = 0 if payload.has("season_ends_at"): if not payload["season_ends_at"] is String or not is_valid_season_timestamp(String(payload["season_ends_at"])): return _reject("Profile response contains invalid season expiry") var parsed_season_end := Time.get_unix_time_from_datetime_string(String(payload["season_ends_at"])) if parsed_season_end < 0: return _reject("Profile response contains invalid season expiry") season_ends_at_unix = int(parsed_season_end) available = true error_message = "" return true static func is_valid_season_timestamp(value: String) -> bool: if value.is_empty(): return false var timestamp_pattern := RegEx.create_from_string("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$") return timestamp_pattern.search(value) != null static func is_valid_opaque_id(value: String) -> bool: if value.length() < 16 or value.length() > 128: return false var id_pattern := RegEx.create_from_string("^[A-Za-z0-9_-]+$") return id_pattern.search(value) != null static func _valid_nonnegative_integer(value: Variant) -> bool: if value is int: return int(value) >= 0 if value is float: return is_finite(float(value)) and float(value) >= 0.0 and float(value) == floor(float(value)) 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 func display_text(now_unix: int = -1) -> String: if not available: return error_message if not error_message.is_empty() else "Ranked profile unavailable" var status := "Provisional" if provisional else tier var text := "%s · %d ranked game%s" % [status, ranked_games, "" if ranked_games == 1 else "s"] if season_ends_at_unix > 0: var current_unix := int(Time.get_unix_time_from_system()) if now_unix < 0 else now_unix var remaining_days := maxi(0, int(ceil(float(season_ends_at_unix - current_unix) / 86400.0))) text += " · Season ends in %dd" % remaining_days return text func _reject(reason: String) -> bool: available = false error_message = reason return false