Files
tactical-shooter/examples/rollback-debugger/scripts/rollback-debugger.gd
T
shawn b0c83af092 Fresh start: replace with naxIO/netfox-cs-sample foundation
Complete replacement of the tactical-shooter project with the
netfox-cs-sample (MIT) — a CS 1.6 inspired multiplayer FPS built
with Godot 4 and netfox.

## What's new
- Full CS-style gameplay: teams (T/CT), rounds, economy, buy menu
- 6 weapons: Knife, Glock, USP, AK-47, M4A1, AWP
- Bomb plant/defuse with 2 bombsites
- Flashbang & smoke grenades
- Proper netfox rollback netcode at 64 tick
- Network popup UI for host/join
- HUD, crosshair, round timer, scoreboard
- All netfox singletons registered as autoloads (works in exported builds)

## Architecture
- Listen-server (host from client, no dedicated server binary)
- Multiplayer-fps game lives at examples/multiplayer-fps/
- Netfox addons registered as autoloads for exported build compat
- Godot 4.7 with Forward+ renderer

## Removed
- Old headless-server architecture (client_main, server_main, player.gd, etc.)
- Custom netfox bootstrap with ENet fallback
- Old ChaffGames FPS template (2,420 lines, 844 KB)
- SimulationServer GDExtension stub
- Godot-jolt physics (netfox sample uses default Godot physics)
- Duplicate weapon_data.gd, anti_cheat.gd, round_manager.gd, etc.
- Server browser API Python venv (87 MB)
- test_range map and modular assets

## Preserved
- Git history
- Server config at config/default_server_config.cfg
- Windows export preset
- Build directory (gitignored)

Co-authored-by: naxIO <naxIO@users.noreply.github.com>
2026-07-02 20:55:20 -04:00

175 lines
5.8 KiB
GDScript

extends Node
# Data fields
@onready var state_history_data := %"State History Data" as TextEdit
@onready var input_history_data := %"Input History Data" as TextEdit
@onready var network_tick_data := %"Network Tick Data" as LineEdit
@onready var rollback_tick_data := %"Rollback Tick Data" as LineEdit
@onready var has_input_data := %"Has Input Data" as Label
@onready var input_age_data := %"Input Age Data" as Label
@onready var simset_list := %"Simset List" as ItemList
@onready var skipset_list := %"Skipset List" as ItemList
@onready var transmit_state_data := %"Transmit State Data" as Label
# Tool buttons
@onready var nt_before_loop_button := %"NT Before Loop Button" as Button
@onready var nt_before_tick_button := %"NT Before Tick Button" as Button
@onready var nt_on_tick_button := %"NT On Tick Button" as Button
@onready var nr_before_loop_button := %"NR Before Loop Button" as Button
@onready var nr_prepare_button := %"NR Prepare Button" as Button
@onready var nr_process_button := %"NR Process Button" as Button
@onready var nr_record_button := %"NR Record Button" as Button
@onready var nr_after_loop_button := %"NR After Loop" as Button
@onready var nt_after_tick_button := %"NT After Tick Button" as Button
@onready var nt_after_tick_loop_button := %"NT After Tick Loop Button" as Button
@onready var run_nr_tick_button := %"Run NR Tick Button" as Button
@onready var advance_button := %"Advance Button" as Button
@onready var rollback_synchronizer := self.get_tree().root\
.find_children("*", "RollbackSynchronizer", true, false)\
.pop_front() as RollbackSynchronizer
func _ready():
if not rollback_synchronizer:
OS.alert("No RollbackSynchronizer found! Add one to the scene and run again!")
get_tree().quit()
# Render initial data
_render_data()
# Set defaults
input_history_data.text = """{
0: { "Input:movement": Vector2(1, 0) },
1: { "Input:movement": Vector2(1, 1) },
2: { "Input:movement": Vector2(1, 1) },
3: { "Input:movement": Vector2(0, 1) },
}"""
# Start NetworkTime but make sure it doesn't tick on its own
NetworkTime.start()
NetworkTime.set_process(false)
# Connect signals
rollback_synchronizer._on_transmit_state.connect(func(state, tick):
transmit_state_data.text = "@%d: %s" % [tick, var_to_str(state)]
)
nt_before_loop_button.pressed.connect(func():
NetworkTime.before_tick_loop.emit()
)
nt_before_tick_button.pressed.connect(func():
NetworkTime.before_tick.emit(NetworkTime.ticktime, NetworkTime.tick)
)
nt_on_tick_button.pressed.connect(func():
NetworkTime.on_tick.emit(NetworkTime.ticktime, NetworkTime.tick)
)
nr_before_loop_button.pressed.connect(func():
NetworkRollback.before_loop.emit()
)
nr_prepare_button.pressed.connect(func():
NetworkRollback.on_prepare_tick.emit(NetworkRollback.tick)
NetworkRollback.after_prepare_tick.emit(NetworkRollback.tick)
)
nr_process_button.pressed.connect(func():
NetworkRollback.on_process_tick.emit(NetworkRollback.tick)
NetworkRollback._tick += 1
)
nr_record_button.pressed.connect(func():
NetworkRollback.on_record_tick.emit(NetworkRollback.tick)
)
nr_after_loop_button.pressed.connect(func():
NetworkRollback.after_loop.emit()
)
nt_after_tick_button.pressed.connect(func():
NetworkTime.after_tick.emit(NetworkTime.ticktime, NetworkTime.tick)
)
nt_after_tick_loop_button.pressed.connect(func():
NetworkTime.after_tick_loop.emit()
NetworkTime._tick += 1
)
run_nr_tick_button.pressed.connect(func():
NetworkRollback.before_loop.emit()
NetworkRollback.on_prepare_tick.emit(NetworkRollback.tick)
NetworkRollback.after_prepare_tick.emit(NetworkRollback.tick)
NetworkRollback.on_process_tick.emit(NetworkRollback.tick)
NetworkRollback._tick += 1
NetworkRollback.on_record_tick.emit(NetworkRollback.tick)
NetworkRollback.after_loop.emit()
)
advance_button.pressed.connect(func():
NetworkRollback._tick += 1
)
# Update UI after any button press
var buttons = get_parent().find_children("*", "Button")
for button in buttons:
(button as Button).button_down.connect(_read_data)
(button as Button).button_up.connect(_render_data)
func _render_data():
network_tick_data.text = str(NetworkTime.tick)
rollback_tick_data.text = str(NetworkRollback.tick)
state_history_data.text = _serialize_history(rollback_synchronizer._states)
input_history_data.text = _serialize_history(rollback_synchronizer._inputs)
if rollback_synchronizer.has_input():
has_input_data.text = "true"
input_age_data.text = str(rollback_synchronizer.get_input_age())
else:
has_input_data.text = "false"
input_age_data.text = "?"
simset_list.clear()
for sim_entry in rollback_synchronizer._simset.values():
simset_list.add_item(str(sim_entry), null, false)
skipset_list.clear()
for skip_entry in rollback_synchronizer._skipset.values():
skipset_list.add_item(str(skip_entry), null, false)
func _read_data():
if network_tick_data.text.is_valid_int():
NetworkTime._tick = network_tick_data.text.to_int()
if rollback_tick_data.text.is_valid_int():
NetworkRollback._tick = rollback_tick_data.text.to_int()
rollback_synchronizer._states = _parse_history(state_history_data.text)
rollback_synchronizer._inputs = _parse_history(input_history_data.text)
func _serialize_history(history: _PropertyHistoryBuffer) -> String:
var result = PackedStringArray()
for tick in history.ticks():
var snapshot := history.get_snapshot(tick)
result.append("\t%d: %s" % [tick, var_to_str(snapshot.as_dictionary()).replace("\n", "")])
return "{\n%s\n}" % [",\n".join(result)]
func _parse_history(history_string: String) -> _PropertyHistoryBuffer:
var result_data = str_to_var(history_string)
if not result_data is Dictionary:
return _PropertyHistoryBuffer.new()
var result := _PropertyHistoryBuffer.new()
for tick in result_data.keys():
var snapshot := _PropertySnapshot.from_dictionary(result_data[tick])
result.set_snapshot(tick, snapshot)
return result