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>
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
# Contributors
|
||||
|
||||
This addon, and the entirety of [netfox] is a shared effort of [Fox's Sake
|
||||
Studio], and the community. The following is the list of community contributors
|
||||
involved with netfox:
|
||||
|
||||
* Alberto Klocker <albertok@gmail.com>
|
||||
* Andrew Davis <jonandrewdavis@gmail.com>
|
||||
* Bryan Lee <42545742+bryanmylee@users.noreply.github.com>
|
||||
* Dan Schuman <danschuman@gmail.com>
|
||||
* Dustie <77035922+DustieDog@users.noreply.github.com>
|
||||
* Eric Volpone <ericvolpone@gmail.com>
|
||||
* Gordon MacPherson <gordon@gordonite.tech>
|
||||
* Jake Cattrall <krazyjakee@gmail.com>
|
||||
* Jon Stevens <1030863+jonstvns@users.noreply.github.com>
|
||||
* Joseph Michael Ware <9at25jnr3@mozmail.com>
|
||||
* Matias Kumpulainen <kumpulainen.matias@gmail.com>
|
||||
* Nicolas Batty <nicolas.batty@gmail.com>
|
||||
* Ricky <77863853+RickyYCheng@users.noreply.github.com>
|
||||
* Riordan-DC <44295008+Riordan-DC@users.noreply.github.com>
|
||||
* Russ <622740+russ-@users.noreply.github.com>
|
||||
* Ryan Roden-Corrent <github@rcorre.net>
|
||||
* TheYellowArchitect <dmalandris@uth.gr>
|
||||
* TheYellowArchitect <hello@theyellowarchitect.com>
|
||||
* camperotactico <alberto.vgdd@gmail.com>
|
||||
* gk98s <89647115+gk98s@users.noreply.github.com>
|
||||
* zibetnu <9at25jnr3@mozmail.com>
|
||||
|
||||
[netfox]: https://github.com/foxssake/netfox
|
||||
[Fox's Sake Studio]: https://github.com/foxssake/
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
extends RefCounted
|
||||
class_name _Bitset
|
||||
|
||||
# Stores a list of booleans, representing them efficiently as a PackedByteArray
|
||||
|
||||
var _data: PackedByteArray
|
||||
var _bit_count: int
|
||||
|
||||
static func of_bools(values: Array) -> _Bitset:
|
||||
var result := _Bitset.new(values.size())
|
||||
for i in values.size():
|
||||
if values[i]:
|
||||
result.set_bit(i)
|
||||
return result
|
||||
|
||||
func _init(bit_count: int):
|
||||
var bytes := bit_count / 8
|
||||
if bit_count % 8 > 0:
|
||||
bytes += 1
|
||||
|
||||
_data = PackedByteArray()
|
||||
_data.resize(bytes)
|
||||
|
||||
_bit_count = bit_count
|
||||
|
||||
func bit_count() -> int:
|
||||
return _bit_count
|
||||
|
||||
func is_empty() -> bool:
|
||||
return _bit_count == 0
|
||||
|
||||
func is_not_empty() -> bool:
|
||||
return _bit_count != 0
|
||||
|
||||
func get_bit(idx: int) -> bool:
|
||||
assert(idx < _bit_count, "Accessing bit %d on bitset of size %d!" % [idx, _bit_count])
|
||||
var byte_idx := idx / 8
|
||||
var bit_idx := idx % 8
|
||||
|
||||
return (_data[byte_idx] >> bit_idx) & 0x1 != 0
|
||||
|
||||
func set_bit(idx: int) -> void:
|
||||
assert(idx < _bit_count, "Accessing bit %d on bitset of size %d!" % [idx, _bit_count])
|
||||
var byte_idx := idx / 8
|
||||
var bit_idx := idx % 8
|
||||
|
||||
_data[byte_idx] |= 0x1 << bit_idx
|
||||
|
||||
func clear_bit(idx: int) -> void:
|
||||
assert(idx < _bit_count, "Accessing bit %d on bitset of size %d!" % [idx, _bit_count])
|
||||
var byte_idx := idx / 8
|
||||
var bit_idx := idx % 8
|
||||
|
||||
_data[byte_idx] &= ~(0x1 << bit_idx)
|
||||
|
||||
func toggle_bit(idx: int) -> void:
|
||||
assert(idx < _bit_count, "Accessing bit %d on bitset of size %d!" % [idx, _bit_count])
|
||||
var byte_idx := idx / 8
|
||||
var bit_idx := idx % 8
|
||||
|
||||
_data[byte_idx] ^= 0x1 << bit_idx
|
||||
|
||||
func get_set_indices() -> Array[int]:
|
||||
var result := [] as Array[int]
|
||||
for i in _data.size():
|
||||
var byte := _data[i]
|
||||
|
||||
if byte & 0x01: result.append(i * 8 + 0)
|
||||
if byte & 0x02: result.append(i * 8 + 1)
|
||||
if byte & 0x04: result.append(i * 8 + 2)
|
||||
if byte & 0x08: result.append(i * 8 + 3)
|
||||
|
||||
if byte & 0x10: result.append(i * 8 + 4)
|
||||
if byte & 0x20: result.append(i * 8 + 5)
|
||||
if byte & 0x40: result.append(i * 8 + 6)
|
||||
if byte & 0x80: result.append(i * 8 + 7)
|
||||
return result
|
||||
|
||||
func equals(other) -> bool:
|
||||
if other is _Bitset:
|
||||
return other._bit_count == _bit_count and other._data == _data
|
||||
else:
|
||||
return false
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_empty():
|
||||
return "Bitset(n=0)"
|
||||
else:
|
||||
var body := ""
|
||||
for i in _bit_count:
|
||||
if i != 0 and i % 4 == 0:
|
||||
body += " "
|
||||
if get_bit(i): body += "1"
|
||||
else: body += "0"
|
||||
return "Bitset(n=%d, %s)" % [_bit_count, body]
|
||||
@@ -0,0 +1 @@
|
||||
uid://c46ub48lgga12
|
||||
@@ -0,0 +1,57 @@
|
||||
extends RefCounted
|
||||
class_name _Graph
|
||||
|
||||
# Represents a graph, in the sense of a set of nodes, arbitrarily connected by
|
||||
# links
|
||||
|
||||
var _links_from := {} # `from` to `to[]`
|
||||
var _links_to := {} # `to` to `from[]`
|
||||
|
||||
func link(from: Variant, to: Variant) -> void:
|
||||
if has_link(from, to):
|
||||
return
|
||||
|
||||
_append(_links_from, from, to)
|
||||
_append(_links_to, to, from)
|
||||
|
||||
func unlink(from: Variant, to: Variant) -> void:
|
||||
_erase(_links_from, from, to)
|
||||
_erase(_links_to, to, from)
|
||||
|
||||
func erase(node: Variant) -> void:
|
||||
var links_to := _links_from.get(node, [])
|
||||
var links_from := _links_to.get(node, [])
|
||||
|
||||
_links_from.erase(node)
|
||||
_links_to.erase(node)
|
||||
|
||||
for link in links_to:
|
||||
_erase(_links_to, link, node)
|
||||
|
||||
for link in links_from:
|
||||
_erase(_links_from, link, node)
|
||||
|
||||
func get_linked_from(from: Variant) -> Array:
|
||||
return _links_from.get(from, [])
|
||||
|
||||
func get_linked_to(to: Variant) -> Array:
|
||||
return _links_to.get(to, [])
|
||||
|
||||
func has_link(from: Variant, to: Variant) -> bool:
|
||||
return get_linked_from(from).has(to)
|
||||
|
||||
func _append(pool: Dictionary, key: Variant, value: Variant) -> void:
|
||||
if not pool.has(key):
|
||||
pool[key] = [value]
|
||||
else:
|
||||
pool[key].append(value)
|
||||
|
||||
func _erase(pool: Dictionary, key: Variant, value: Variant) -> void:
|
||||
if not pool.has(key):
|
||||
return
|
||||
|
||||
var values := pool[key] as Array
|
||||
values.erase(value)
|
||||
|
||||
if values.is_empty():
|
||||
pool.erase(key)
|
||||
@@ -0,0 +1 @@
|
||||
uid://djknl4n6akxue
|
||||
@@ -1,70 +1,134 @@
|
||||
extends RefCounted
|
||||
class_name _HistoryBuffer
|
||||
|
||||
# Maps ticks (int) to arbitrary data
|
||||
var _buffer: Dictionary = {}
|
||||
# Maps ticks (int) to arbitrary data, stored in a sliding ring buffer
|
||||
|
||||
func get_snapshot(tick: int):
|
||||
if _buffer.has(tick):
|
||||
return _buffer[tick]
|
||||
else:
|
||||
return null
|
||||
var _capacity := 64
|
||||
var _buffer := []
|
||||
var _previous := []
|
||||
|
||||
func set_snapshot(tick: int, data):
|
||||
_buffer[tick] = data
|
||||
var _tail := 0
|
||||
var _head := 0
|
||||
|
||||
func get_buffer() -> Dictionary:
|
||||
return _buffer
|
||||
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 get_earliest_tick() -> int:
|
||||
return _buffer.keys().min()
|
||||
func _init(capacity: int = 64):
|
||||
_capacity = capacity
|
||||
_buffer.resize(_capacity)
|
||||
_previous.resize(_capacity)
|
||||
|
||||
func get_latest_tick() -> int:
|
||||
return _buffer.keys().max()
|
||||
func duplicate(deep: bool = false) -> _HistoryBuffer:
|
||||
var buffer := _HistoryBuffer.new(_capacity)
|
||||
|
||||
func get_closest_tick(tick: int) -> int:
|
||||
if _buffer.has(tick):
|
||||
return tick
|
||||
buffer._buffer = _buffer.duplicate(deep)
|
||||
buffer._previous = _previous.duplicate()
|
||||
buffer._tail = _tail
|
||||
buffer._head = _head
|
||||
|
||||
if _buffer.is_empty():
|
||||
return -1
|
||||
return buffer
|
||||
|
||||
var earliest_tick = get_earliest_tick()
|
||||
if tick < earliest_tick:
|
||||
return earliest_tick
|
||||
func push(value: Variant) -> void:
|
||||
_buffer[_head % _capacity] = value
|
||||
_previous[_head % _capacity] = _head
|
||||
_head += 1
|
||||
_tail += maxi(0, size() - capacity())
|
||||
|
||||
var latest_tick = get_latest_tick()
|
||||
if tick > latest_tick:
|
||||
return latest_tick
|
||||
func pop() -> Variant:
|
||||
assert(is_not_empty(), "History buffer is empty!")
|
||||
|
||||
return _buffer.keys() \
|
||||
.filter(func (key): return key < tick) \
|
||||
.max()
|
||||
var value = _buffer[_tail % _capacity]
|
||||
_tail += 1
|
||||
return value
|
||||
|
||||
func get_history(tick: int):
|
||||
var closest_tick = get_closest_tick(tick)
|
||||
return _buffer.get(closest_tick)
|
||||
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
|
||||
|
||||
func trim(earliest_tick_to_keep: int):
|
||||
var ticks := _buffer.keys()
|
||||
for tick in ticks:
|
||||
if tick < earliest_tick_to_keep:
|
||||
_buffer.erase(tick)
|
||||
_previous.fill(null)
|
||||
_buffer.fill(null)
|
||||
|
||||
func clear():
|
||||
_buffer.clear()
|
||||
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 _buffer.size()
|
||||
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 _buffer.is_empty()
|
||||
return size() == 0
|
||||
|
||||
func has(tick) -> bool:
|
||||
return _buffer.has(tick)
|
||||
|
||||
func ticks() -> Array:
|
||||
return _buffer.keys()
|
||||
|
||||
func erase(tick):
|
||||
_buffer.erase(tick)
|
||||
func is_not_empty() -> bool:
|
||||
return not is_empty()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
extends RefCounted
|
||||
class_name _IntervalScheduler
|
||||
|
||||
# Returns true on every nth `is_now()` call
|
||||
|
||||
var interval := 1
|
||||
var _idx := 0
|
||||
|
||||
func _init(p_interval: int = 1):
|
||||
interval = p_interval
|
||||
|
||||
func is_now() -> bool:
|
||||
if interval <= 0:
|
||||
return false
|
||||
elif interval == 1:
|
||||
return true
|
||||
elif _idx + 1 >= interval:
|
||||
_idx = 0
|
||||
return true
|
||||
else:
|
||||
_idx += 1
|
||||
return false
|
||||
@@ -0,0 +1 @@
|
||||
uid://egfuyvoj0r2s
|
||||
@@ -3,5 +3,5 @@
|
||||
name="netfox.internals"
|
||||
description="Shared internals for netfox addons"
|
||||
author="Tamas Galffy and contributors"
|
||||
version="1.35.3"
|
||||
version="1.40.2"
|
||||
script="plugin.gd"
|
||||
|
||||
@@ -10,6 +10,11 @@ static func of(items: Array) -> _Set:
|
||||
result.add(item)
|
||||
return result
|
||||
|
||||
func duplicate(deep: bool = false) -> _Set:
|
||||
var result := _Set.new()
|
||||
result._data = _data.duplicate(deep)
|
||||
return result
|
||||
|
||||
func add(value):
|
||||
_data[value] = true
|
||||
|
||||
@@ -46,6 +51,9 @@ func equals(other) -> bool:
|
||||
func _to_string():
|
||||
return "Set" + str(values())
|
||||
|
||||
func _to_vest():
|
||||
return _data.keys()
|
||||
|
||||
func _iter_init(arg) -> bool:
|
||||
_iterator_idx = 0
|
||||
return _can_iterate()
|
||||
|
||||
Reference in New Issue
Block a user