b0c83af092
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>
135 lines
2.9 KiB
GDScript
135 lines
2.9 KiB
GDScript
extends RefCounted
|
|
class_name _HistoryBuffer
|
|
|
|
# Maps ticks (int) to arbitrary data, stored in a sliding ring buffer
|
|
|
|
var _capacity := 64
|
|
var _buffer := []
|
|
var _previous := []
|
|
|
|
var _tail := 0
|
|
var _head := 0
|
|
|
|
static func of(capacity: int, data: Dictionary) -> _HistoryBuffer:
|
|
var history_buffer := _HistoryBuffer.new(capacity)
|
|
for idx in data:
|
|
history_buffer.set_at(idx, data[idx])
|
|
return history_buffer
|
|
|
|
func _init(capacity: int = 64):
|
|
_capacity = capacity
|
|
_buffer.resize(_capacity)
|
|
_previous.resize(_capacity)
|
|
|
|
func duplicate(deep: bool = false) -> _HistoryBuffer:
|
|
var buffer := _HistoryBuffer.new(_capacity)
|
|
|
|
buffer._buffer = _buffer.duplicate(deep)
|
|
buffer._previous = _previous.duplicate()
|
|
buffer._tail = _tail
|
|
buffer._head = _head
|
|
|
|
return buffer
|
|
|
|
func push(value: Variant) -> void:
|
|
_buffer[_head % _capacity] = value
|
|
_previous[_head % _capacity] = _head
|
|
_head += 1
|
|
_tail += maxi(0, size() - capacity())
|
|
|
|
func pop() -> Variant:
|
|
assert(is_not_empty(), "History buffer is empty!")
|
|
|
|
var value = _buffer[_tail % _capacity]
|
|
_tail += 1
|
|
return value
|
|
|
|
func set_at(at: int, value: Variant) -> void:
|
|
# Why does this need so many branches?
|
|
if is_empty():
|
|
# Buffer is empty, jump to specified index
|
|
_tail = at
|
|
_head = at
|
|
push(value)
|
|
elif at < _head - capacity():
|
|
# Trying to set something that would wrap back around and overwrite
|
|
# current data
|
|
return
|
|
elif at == _head:
|
|
# Simply adding a new item
|
|
push(value)
|
|
elif at < _head:
|
|
_buffer[at % _capacity] = value
|
|
# Update prev-buffer
|
|
for i in range(at, _head):
|
|
if _previous[i % _capacity] == i:
|
|
break
|
|
_previous[i % _capacity] = at
|
|
_tail = mini(_tail, at)
|
|
elif at >= _head + _capacity:
|
|
# We're leaving all data behind
|
|
_tail = at
|
|
_head = at
|
|
|
|
_previous.fill(null)
|
|
_buffer.fill(null)
|
|
|
|
push(value)
|
|
elif at >= _head:
|
|
# Skipping forward a bit
|
|
var previous := _head - 1
|
|
while _head < at:
|
|
_previous[_head % _capacity] = previous
|
|
_head += 1
|
|
_tail += maxi(0, size() - _capacity)
|
|
|
|
push(value)
|
|
|
|
func has_at(at: int) -> bool:
|
|
if is_empty(): return false
|
|
if at < _head - capacity(): return false
|
|
if at >= _head: return false
|
|
return _previous[at % _capacity] == at
|
|
|
|
func get_at(at: int, default: Variant = null) -> Variant:
|
|
if not has_at(at):
|
|
return default
|
|
return _buffer[at % _capacity]
|
|
|
|
func has_latest_at(at: int) -> bool:
|
|
if is_empty(): return false
|
|
if at < _tail: return false
|
|
return true
|
|
|
|
func size() -> int:
|
|
return _head - _tail
|
|
|
|
func capacity() -> int:
|
|
return _capacity
|
|
|
|
func get_earliest_index() -> int:
|
|
return _tail
|
|
|
|
func get_latest_index() -> int:
|
|
return _head - 1
|
|
|
|
func get_latest_index_at(at: int) -> int:
|
|
if not has_latest_at(at):
|
|
return -1
|
|
if at >= _head:
|
|
return get_latest_index()
|
|
|
|
return _previous[at % _capacity]
|
|
|
|
func get_latest_at(at: int) -> Variant:
|
|
return get_at(get_latest_index_at(at))
|
|
|
|
func clear():
|
|
_tail = _head
|
|
|
|
func is_empty() -> bool:
|
|
return size() == 0
|
|
|
|
func is_not_empty() -> bool:
|
|
return not is_empty()
|