NEO-32: fix cooldown HUD anchor when serverTimeUtc parse fails
parent
f4ba4cdd92
commit
56f7ae8a81
|
|
@ -4,6 +4,8 @@ extends RefCounted
|
|||
## Uses [code]serverTimeUtc[/code] + receive tick to approximate server "now" between polls.
|
||||
|
||||
const SLOT_COUNT: int = 8
|
||||
## Reject bogus [method Time.get_unix_time_from_datetime_string] results (failed parse → 0).
|
||||
const MIN_VALID_UNIX_ANCHOR: float = 1_000_000_000.0
|
||||
|
||||
var _slot_end_unix: PackedFloat64Array = PackedFloat64Array()
|
||||
var _server_unix_at_receive: float = 0.0
|
||||
|
|
@ -19,11 +21,19 @@ func _init() -> void:
|
|||
func apply_snapshot(snapshot: Dictionary) -> void:
|
||||
for i in range(SLOT_COUNT):
|
||||
_slot_end_unix[i] = 0.0
|
||||
var st: Variant = snapshot.get("serverTimeUtc", "")
|
||||
var st: Variant = snapshot.get("serverTimeUtc", null)
|
||||
var anchor: float = 0.0
|
||||
if st is String:
|
||||
var su: String = st as String
|
||||
var su: String = (st as String).strip_edges()
|
||||
if not su.is_empty():
|
||||
_server_unix_at_receive = Time.get_unix_time_from_datetime_string(su)
|
||||
anchor = Time.get_unix_time_from_datetime_string(su)
|
||||
elif st is float:
|
||||
anchor = st as float
|
||||
elif st is int:
|
||||
anchor = float(st as int)
|
||||
if anchor < MIN_VALID_UNIX_ANCHOR:
|
||||
anchor = Time.get_unix_time_from_system()
|
||||
_server_unix_at_receive = anchor
|
||||
_ticks_msec_at_receive = Time.get_ticks_msec()
|
||||
var slots: Variant = snapshot.get("slots", [])
|
||||
if slots is Array:
|
||||
|
|
@ -36,8 +46,10 @@ func apply_snapshot(snapshot: Dictionary) -> void:
|
|||
continue
|
||||
var end_v: Variant = d.get("cooldownEndsAtUtc", null)
|
||||
if end_v is String and not (end_v as String).is_empty():
|
||||
var end_s: String = end_v as String
|
||||
_slot_end_unix[idx] = Time.get_unix_time_from_datetime_string(end_s)
|
||||
var end_s: String = (end_v as String).strip_edges()
|
||||
var end_unix: float = Time.get_unix_time_from_datetime_string(end_s)
|
||||
if end_unix >= MIN_VALID_UNIX_ANCHOR:
|
||||
_slot_end_unix[idx] = end_unix
|
||||
|
||||
|
||||
func _effective_server_unix() -> float:
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ func _physics_process(_delta: float) -> void:
|
|||
var p: Vector3 = _player.global_position
|
||||
_player_pos_label.text = _build_player_position_text(p)
|
||||
_render_target_lock_label(p)
|
||||
if is_instance_valid(_cooldown_state) and _cooldown_state.has_method("any_slot_cooling"):
|
||||
if _cooldown_state != null and _cooldown_state.has_method("any_slot_cooling"):
|
||||
if bool(_cooldown_state.call("any_slot_cooling")):
|
||||
_render_cooldown_slots_label()
|
||||
|
||||
|
|
@ -317,7 +317,7 @@ func _on_cooldown_poll_timeout() -> void:
|
|||
|
||||
|
||||
func _on_cooldown_snapshot_received(snapshot: Dictionary) -> void:
|
||||
if not is_instance_valid(_cooldown_state) or not _cooldown_state.has_method("apply_snapshot"):
|
||||
if _cooldown_state == null or not _cooldown_state.has_method("apply_snapshot"):
|
||||
return
|
||||
_cooldown_state.call("apply_snapshot", snapshot)
|
||||
_render_cooldown_slots_label()
|
||||
|
|
@ -334,7 +334,7 @@ func _on_cooldown_snapshot_received(snapshot: Dictionary) -> void:
|
|||
func _render_cooldown_slots_label() -> void:
|
||||
if not is_instance_valid(_cooldown_slots_label):
|
||||
return
|
||||
if not is_instance_valid(_cooldown_state) or not _cooldown_state.has_method("build_hud_line"):
|
||||
if _cooldown_state == null or not _cooldown_state.has_method("build_hud_line"):
|
||||
return
|
||||
var line: Variant = _cooldown_state.call("build_hud_line")
|
||||
if line is String:
|
||||
|
|
@ -466,7 +466,7 @@ func _request_hotbar_cast_slot(slot_index: int) -> void:
|
|||
var ability_id: String = outcome[HotbarCastSlotResolver.KEY_ABILITY_ID] as String
|
||||
var resolved_slot: int = int(outcome.get(HotbarCastSlotResolver.KEY_SLOT_INDEX, slot_index))
|
||||
var target_id: Variant = outcome.get(HotbarCastSlotResolver.KEY_TARGET_ID, null)
|
||||
if is_instance_valid(_cooldown_state) and _cooldown_state.has_method("is_slot_cooling"):
|
||||
if _cooldown_state != null and _cooldown_state.has_method("is_slot_cooling"):
|
||||
if bool(_cooldown_state.call("is_slot_cooling", resolved_slot)):
|
||||
if is_instance_valid(_cast_feedback_label):
|
||||
_cast_feedback_label.text = "ability_cast_denied: %s (client guard)" % COOLDOWN_REASON_ON_CD
|
||||
|
|
|
|||
|
|
@ -34,3 +34,16 @@ func test_remaining_positive_when_end_after_server_time() -> void:
|
|||
# Assert
|
||||
assert_that(rem).is_greater(9.0)
|
||||
assert_that(rem).is_less(11.0)
|
||||
|
||||
|
||||
func test_invalid_server_time_string_does_not_lock_slot_ready() -> void:
|
||||
# Arrange: bogus ISO would parse as 0 in Godot; anchor must fall back so slot 0 is not stuck cooling.
|
||||
var state: RefCounted = CooldownStateScript.new()
|
||||
var snap := {"serverTimeUtc": "__not_iso__", "slots": [{"slotIndex": 0}]}
|
||||
|
||||
# Act
|
||||
state.call("apply_snapshot", snap)
|
||||
var cooling: bool = bool(state.call("is_slot_cooling", 0))
|
||||
|
||||
# Assert
|
||||
assert_that(cooling).is_false()
|
||||
|
|
|
|||
Loading…
Reference in New Issue