mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
9d8a8080ba
Extends net_debug_overlay.gd (Phase 1's RTT/offset display) with the rest of task 3.7's list: jitter (new RFC3550-style EWMA in NetworkManager, computed from raw per-sample RTT before Phase 1's own min-filtering, since that filter is deliberately jitter-insensitive by design), input buffer depth and input_lead (both already tracked client-side for task 3.3), snapshot loss (a new EWMA in networked_match.gd over each received snapshot's own server_tick gap - snapshots go out at a steady one-tick cadence, so a gap is direct evidence of a drop or reorder), snapshot age (computed on demand from the same bias-corrected tick estimate the interpolator itself uses), and bandwidth (new rolling per-second byte counters in MatchSim, on the two 60Hz hot-path channels only). Prediction error is deliberately omitted with a comment explaining why: there's no client-side prediction to measure until Phase 4. Verified values are live and plausible, not just present, by calling get_net_debug_stats() directly in a real two-process test and checking the numbers make sense: bandwidth matched the wire format's own byte math almost exactly (measured ~2400 B/s sent against a computed 40B x 60Hz, ~3540 B/s received against 59B x 60Hz), and buffer depth/lead/loss all moved in the correct direction between a clean LAN run and one under simulated 60ms latency + 10% loss. Full regression suite re-run clean.
63 lines
2.4 KiB
GDScript
63 lines
2.4 KiB
GDScript
extends CanvasLayer
|
|
|
|
# Autoload: toggleable network debug overlay (F4 by default — see
|
|
# toggle_net_overlay in project.godot's [input]). Read-only against
|
|
# NetworkManager's clock state (task 1.8). Mirrors perf_overlay.gd's pattern
|
|
# — headless-guarded, hidden by default, no gameplay-state writes.
|
|
|
|
var _label: Label
|
|
|
|
|
|
func _ready() -> void:
|
|
if DisplayServer.get_name() == "headless":
|
|
set_process(false)
|
|
return
|
|
layer = 100
|
|
_label = Label.new()
|
|
_label.add_theme_font_size_override("font_size", 14)
|
|
_label.add_theme_color_override("font_color", Color(0.5, 0.8, 1.0))
|
|
_label.add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.85))
|
|
_label.add_theme_constant_override("shadow_offset_x", 1)
|
|
_label.add_theme_constant_override("shadow_offset_y", 1)
|
|
_label.position = Vector2(12, 90)
|
|
_label.visible = false
|
|
add_child(_label)
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if event.is_action_pressed("toggle_net_overlay") and _label:
|
|
_label.visible = not _label.visible
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
if not _label or not _label.visible:
|
|
return
|
|
if NetworkManager.is_server:
|
|
_label.text = "NET: server, %d peer(s) out %s in %s" % [
|
|
MatchNet.roster.size(), _format_kbps(MatchSim.bytes_sent_per_sec), _format_kbps(MatchSim.bytes_received_per_sec),
|
|
]
|
|
elif NetworkManager.is_client:
|
|
if NetworkManager.rtt_ms < 0.0:
|
|
_label.text = "NET: client, connecting (no clock sample yet)"
|
|
else:
|
|
# task 3.7: RTT, jitter, loss, buffer depth, snapshot age,
|
|
# bandwidth all live here now. Prediction error is intentionally
|
|
# absent — there is no client-side prediction until Phase 4, so
|
|
# there is nothing honest to show for it yet.
|
|
var stats := {}
|
|
var game := get_tree().get_first_node_in_group("game")
|
|
if game and game.has_method("get_net_debug_stats"):
|
|
stats = game.get_net_debug_stats()
|
|
_label.text = "NET: client RTT %.1fms jitter %.1fms offset %.1fms\nbuf depth %s lead %s loss %.1f%% snap age %.1fms\nout %s in %s" % [
|
|
NetworkManager.rtt_ms, NetworkManager.jitter_ms, NetworkManager.clock_offset_ms,
|
|
str(stats.get("input_buffer_depth", -1)), str(stats.get("input_lead", "-")),
|
|
stats.get("snapshot_loss_pct", 0.0), stats.get("snapshot_age_ms", 0.0),
|
|
_format_kbps(MatchSim.bytes_sent_per_sec), _format_kbps(MatchSim.bytes_received_per_sec),
|
|
]
|
|
else:
|
|
_label.text = "NET: offline"
|
|
|
|
|
|
func _format_kbps(bytes_per_sec: float) -> String:
|
|
return "%.2f KB/s" % (bytes_per_sec / 1000.0)
|