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:
2026-07-02 20:55:20 -04:00
parent ce39b237c3
commit b0c83af092
4416 changed files with 57418 additions and 902676 deletions
+100
View File
@@ -0,0 +1,100 @@
# This file is generated by Vest!
# Do not modify!
# source: res://addons/vest/test/mixins/gather-suite-mixin.gd
extends "res://addons/vest/test/vest-test-base.gd"
# Automatically gathers tests defined in the test class
func _get_ignored_methods() -> Array[String]:
return [
"get_suite_name",
"before_suite", "before_case", "before_benchmark", "before_each",
"after_suite", "after_case", "after_benchmark", "after_each"
]
func _get_suite_name() -> String:
# Check if callback is implemented
if has_method("get_suite_name"):
return call("get_suite_name")
# Check if class_name is set
var script := get_script() as Script
var pattern := RegEx.create_from_string("class_name\\s+([^\n\\s]+)")
var hit := pattern.search(script.source_code)
if hit:
return hit.get_string(1)
# Fall back to script path
return script.resource_path
func _get_suite() -> VestDefs.Suite:
var script := get_script() as Script
var ignored_methods := _get_ignored_methods()
var inherited_methods := (script.get_base_script()
.get_script_method_list()
.map(func(it): return it["name"])
)
var methods := (script.get_script_method_list()
.filter(func(it): return not it["name"].begins_with("_"))
.filter(func(it): return not inherited_methods.has(it["name"]))
.filter(func(it): return not ignored_methods.has(it["name"]))
)
var define_methods: Array[Dictionary] = []
var case_methods: Array[Dictionary] = []
var parametric_methods: Array[Dictionary] = []
for method in methods:
if method["name"].begins_with("suite"):
define_methods.append(method)
elif method["name"].begins_with("test") and not method["default_args"].is_empty() and method["default_args"][0] is String:
parametric_methods.append(method)
elif method["name"].begins_with("test"):
case_methods.append(method)
return await define(_get_suite_name(), func():
for method in define_methods:
await call(method["name"])
for method in case_methods:
var method_name := method["name"] as String
var test_name := method_name.trim_prefix("test").trim_suffix("__only").capitalize()
var callback := func(): await call(method["name"])
var is_only := _is_only(method_name)
test(test_name, callback, is_only, method_name)
for method in parametric_methods:
var method_name := method["name"] as String
var test_name := method_name.trim_prefix("test").trim_suffix("__only").capitalize()
var is_only := _is_only(method_name)
var param_provider_name := method["default_args"][0] as String
if not has_method(param_provider_name):
push_warning(
"Can't run parametrized test \"%s\", provider method \"%s\" is missing!" % \
[method["name"], param_provider_name]
)
var params = await call(param_provider_name)
if not params is Array or not params.all(func(it): return it is Array):
push_warning(
"Can't run parametrized test \"%s\", provider \"%s\" didn't return array or arrays: %s" % \
[method["name"], param_provider_name, params]
)
for i in range(params.size()):
test(
"%s#%d %s" % [test_name, i+1, params[i]],
func(): await callv(method["name"], params[i]),
is_only,
method_name
)
)
func _is_only(name: String) -> bool:
return name.ends_with("__only")
@@ -0,0 +1 @@
uid://mm20mdmsc5ii
+129
View File
@@ -0,0 +1,129 @@
# This file is generated by Vest!
# Do not modify!
# source: res://addons/vest/test/mixins/expect-mixin.gd
extends "res://addons/vest/_generated-mixins/1-8182042.gd"
## Mixin for asserting test requirement
##
## @tutorial(Assertions): https://foxssake.github.io/vest/latest/user-guide/assertions/
## Expect a [param condition] to be true.
func expect(condition: bool, p_message: String = "") -> void:
if condition:
ok()
else:
fail(p_message)
## Expect a [param condition] to be false.
func expect_not(condition: bool, p_message: String = "") -> void:
if not condition:
ok()
else:
fail(p_message)
## Expect two values to be equal.
## [br][br]
## If [param actual] has an [code]equals()[/code] method, it will be used.
func expect_equal(actual: Variant, expected: Variant, p_message: String = "Actual value differs from expected!") -> void:
if Vest.__.Matchers.is_equal(actual, expected):
ok()
else:
fail(p_message, { "expect": expected, "got": actual })
## Expect two values not to be equal.
## [br][br]
## If [param actual] has an [code]equals()[/code] method, it will be used.
func expect_not_equal(actual: Variant, expected: Variant, p_message: String = "Actual value equals expected!") -> void:
if Vest.__.Matchers.is_equal(actual, expected):
fail(p_message, { "expect": expected, "got": actual })
else:
ok()
## Expect a [param condition] to be true.
## [br][br]
## Synonim of [method expect], aimed at better readability for asserting bools.
func expect_true(condition: bool, p_message: String = "") -> void:
expect(condition, p_message)
## Expect a [param condition] to be false.
## [br][br]
## Synonim of [method expect_not], aimed at better readability for asserting
## bools.
func expect_false(condition: bool, p_message: String = "") -> void:
expect_not(condition, p_message)
## Expect an [param object] to be empty.
## [br][br]
## If it's a custom type implementing [code]is_empty()[/code], that method will
## be used.
func expect_empty(object: Variant, p_message: String = "Object was not empty!") -> void:
match Vest.__.Matchers.is_empty(object):
true:
ok()
false:
fail(p_message)
ERR_METHOD_NOT_FOUND:
fail("Object has no is_empty() method!", { "object": object })
ERR_CANT_RESOLVE:
fail("Unknown object, can't be checked for emptiness!", { "object": object })
## Expect an [param object] to not be empty.
## [br][br]
## If it's a custom type implementing [code]is_empty()[/code], that method will
## be used.
func expect_not_empty(object: Variant, p_message: String = "Object was empty!") -> void:
match Vest.__.Matchers.is_empty(object):
true:
fail(p_message)
false:
ok()
ERR_METHOD_NOT_FOUND:
fail("Object has no is_empty() method!", { "object": object })
ERR_CANT_RESOLVE:
fail("Unknown object, can't be checked for emptiness!", { "object": object })
## Expect an [param object] to contain [param item].
## [br][br]
## If it's a custom type implementing [code]has()[/code], that method will be
## used.
func expect_contains(object: Variant, item: Variant, p_message: String = "Item is missing from collection!") -> void:
match Vest.__.Matchers.contains(object, item):
true:
ok()
false:
fail(p_message, { "got": object, "missing": item })
ERR_METHOD_NOT_FOUND:
fail("Object has no has() method!", { "object": object })
ERR_CANT_RESOLVE:
fail("Unknown object, can't be checked if it contains item!", { "object": object })
## Expect an [param object] to not contain [param item].
## [br][br]
## If it's a custom type implementing [code]has()[/code], that method will be
## used.
func expect_does_not_contain(object: Variant, item: Variant, p_message: String = "Item is in collection!") -> void:
match Vest.__.Matchers.contains(object, item):
true:
fail(p_message, { "got": object, "excess": item })
false:
ok()
ERR_METHOD_NOT_FOUND:
fail("Object has no has() method!", { "object": object })
ERR_CANT_RESOLVE:
fail("Unknown object, can't be checked if it contains item!", { "object": object })
## Expect a [param value] to be null.
func expect_null(value: Variant, p_message: String = "Item is not null!") -> void:
if value == null:
ok()
else:
fail(p_message, { "got": value })
## Expect a [param value] to not be null.
func expect_not_null(value: Variant, p_message: String = "Item is null!") -> void:
if value != null:
ok()
else:
fail(p_message)
@@ -0,0 +1 @@
uid://caf7owv25i7op
@@ -0,0 +1,85 @@
# This file is generated by Vest!
# Do not modify!
# source: res://addons/vest/test/mixins/assert-that-mixin.gd
extends "res://addons/vest/_generated-mixins/2-ffc90559.gd"
func assert_that(value: Variant) -> _Assertion:
return _Assertion.new(self, value)
class _Assertion:
var _test: VestTest
var _value: Variant
func _init(p_test: Variant, p_value: Variant):
assert(p_test is VestTest, "assert_that mixin used from outside of VestTest!")
_test = p_test
_value = p_value
## Expect a [param condition] about the asserted value to be true.
func passes(condition: Callable, p_message: String = "") -> _Assertion:
_test.expect(condition.call(_value), p_message)
return self
## Expect a [param condition] about the asserted value to be false.
func fails(condition: Callable, p_message: String = "") -> _Assertion:
_test.expect_not(condition.call(_value), p_message)
return self
## Expect the asserted value to be equal to [param expected].
## [br][br]
## If [param actual] has an [code]equals()[/code] method, it will be used.
func is_equal_to(expected: Variant, p_message: String = "Actual value differs from expected!") -> _Assertion:
_test.expect_equal(_value, expected, p_message)
return self
## Expect the asserted value to not be equal to [param expected].
## [br][br]
## If [param actual] has an [code]equals()[/code] method, it will be used.
func is_not_equal_to(expected: Variant, p_message: String = "Actual value equals expected!") -> _Assertion:
_test.expect_not_equal(_value, expected, p_message)
return self
## Expect the asserted value to be empty.
## [br][br]
## If it's a custom type implementing [code]is_empty()[/code], that method will
## be used.
func is_empty(p_message: String = "Object was not empty!") -> _Assertion:
_test.expect_empty(_value, p_message)
return self
## Expect the asserted value to not be empty.
## [br][br]
## If it's a custom type implementing [code]is_empty()[/code], that method will
## be used.
func is_not_empty(p_message: String = "Object was empty!") -> _Assertion:
_test.expect_not_empty(_value, p_message)
return self
## Expect the asserted value to contain [param item].
## [br][br]
## If it's a custom type implementing [code]has()[/code], that method will be
## used.
func contains(item: Variant, p_message: String = "Item is missing from collection!") -> _Assertion:
_test.expect_contains(_value, item, p_message)
return self
## Expect the asserted value to not contain [param item].
## [br][br]
## If it's a custom type implementing [code]has()[/code], that method will be
## used.
func does_not_contain(item: Variant, p_message: String = "Item is in collection!") -> _Assertion:
_test.expect_does_not_contain(_value, item, p_message)
return self
## Expect the asserted value to be null.
func is_null(p_message: String = "Item is not null!") -> _Assertion:
_test.expect_null(_value, p_message)
return self
## Expect the asserted value to not be null.
func is_not_null(p_message: String = "Item is null!") -> _Assertion:
_test.expect_not_null(_value, p_message)
return self
@@ -0,0 +1 @@
uid://d3mmot6r5ycm6
@@ -0,0 +1,86 @@
# This file is generated by Vest!
# Do not modify!
# source: res://addons/vest/test/mixins/mock-mixin.gd
extends "res://addons/vest/_generated-mixins/3-27f38ba0.gd"
## Provides mocking for tests
##
## @tutorial(Mocks): https://foxssake.github.io/vest/latest/user-guide/mocks/
# TODO: Fail test case if there's unhandled calls
var _mock_generator := VestMockGenerator.new()
var _mock_handler := VestMockHandler.new()
# Maps Scripts to their mocked counterparts
var _mock_script_cache := {}
## Create a mocked instance of a given script
func mock(script: Script):
var mocked_script := _get_mock_script(script)
var mocked_object = mocked_script.new()
_mock_handler.take_over(mocked_object)
return mocked_object
## Get calls of a mock object's method
func get_calls_of(method: Callable) -> Array[Array]:
var result: Array[Array] = []
for call_data in _mock_handler.get_calls():
if call_data.method != method:
continue
result.append(call_data.args)
return result
## Start specifying an answer for a mocked method call
func when(method: Callable) -> AnswerBuilder:
return AnswerBuilder._of(method, self)
func _get_mock_script(script: Script) -> Script:
if _mock_script_cache.has(script):
return _mock_script_cache.get(script)
else:
var mocked_script := _mock_generator.generate_mock_script(script)
_mock_script_cache[script] = mocked_script
return mocked_script
## Builder for specifying [VestMockDefs.Answer] objects
class AnswerBuilder:
var _test
var _args: Array = []
var _method: Callable
## Set expected arguments
func with_args(p_args: Array) -> AnswerBuilder:
_args = p_args
return self
## Answer by calling a custom method
## [br][br]
## The method will received the passed arguments as an array.
func then_answer(p_answer_method: Callable) -> void:
var answer := VestMockDefs.Answer.new()
answer.expected_method = _method
answer.expected_args = _args
answer._answer_method = p_answer_method
_test._mock_handler.add_answer(answer)
## Answer with a fixed value
func then_return(p_answer_value: Variant) -> void:
var answer := VestMockDefs.Answer.new()
answer.expected_method = _method
answer.expected_args = _args
answer._answer_value = p_answer_value
_test._mock_handler.add_answer(answer)
static func _of(p_method: Callable, p_test):
var builder := AnswerBuilder.new()
builder._method = p_method
builder._test = p_test
return builder
@@ -0,0 +1 @@
uid://v3npn3goh4em
@@ -0,0 +1,84 @@
# This file is generated by Vest!
# Do not modify!
# source: res://addons/vest/test/mixins/capture-signal-mixin.gd
extends "res://addons/vest/_generated-mixins/4-795b469a.gd"
## Mixin for capturing and asserting signals
##
## @tutorial(Capturing signals): https://foxssake.github.io/vest/latest/user-guide/capturing-signals/
# Maps signals to an array of recorded emissions
# Each array item is an individual array containing the emission params
var _signal_captures := {}
# Array of [signal, recorder, is_persistent] tuples
var _signal_recorders: Array[Array] = []
## Capture all emissions of a signal.
## [br][br]
## Mark the capture as [param persistent] if you want the capture to persist
## between test cases.
func capture_signal(what: Signal, arg_count: int = 0, persistent: bool = false):
# Reset captures
_signal_captures[what] = []
# Add listener
var recorder = _get_signal_recorder(what, arg_count)
if not recorder:
push_warning("Can't capture signal with %d arguments!" % arg_count)
return
what.connect(recorder)
_signal_recorders.append([what, recorder, persistent])
## Get the captured signal emissions for a given signal.
## [br][br]
## Note that the captured emissions are reset between test cases. [br]
## Returns an array of signal emission parameters for each captured emission.
func get_signal_emissions(what: Signal) -> Array:
return _signal_captures.get(what, [])
func _init():
super()
on_case_begin.connect(func(__):
# Remove non-persistent recorders
_cleanup_recorders()
# Clear captures
_signal_captures.clear()
)
func _cleanup_recorders():
var filtered_recorders: Array[Array] = []
for recorder_tuple in _signal_recorders:
var recorded_signal := recorder_tuple[0] as Signal
var recorder := recorder_tuple[1] as Callable
var persistent := recorder_tuple[2] as bool
if persistent:
filtered_recorders.append(recorder_tuple)
else:
recorded_signal.disconnect(recorder)
_signal_recorders = filtered_recorders
func _get_signal_recorder(what: Signal, arg_count: int):
match(arg_count):
0: return func(): _record_emission(what, [])
1: return func(a1): _record_emission(what, [a1])
2: return func(a1, a2): _record_emission(what, [a1, a2])
3: return func(a1, a2, a3): _record_emission(what, [a1, a2, a3])
4: return func(a1, a2, a3, a4): _record_emission(what, [a1, a2, a3, a4])
5: return func(a1, a2, a3, a4, a5): _record_emission(what, [a1, a2, a3, a4, a5])
6: return func(a1, a2, a3, a4, a5, a6): _record_emission(what, [a1, a2, a3, a4, a5, a6])
7: return func(a1, a2, a3, a4, a5, a6, a7): _record_emission(what, [a1, a2, a3, a4, a5, a6, a7])
8: return func(a1, a2, a3, a4, a5, a6, a7, a8): _record_emission(what, [a1, a2, a3, a4, a5, a6, a7, a8])
_: return null
func _record_emission(what: Signal, args: Array):
if not _signal_captures.has(what):
_signal_captures[what] = []
_signal_captures[what].append(args)
@@ -0,0 +1 @@
uid://c8oa5ooaqwi11
@@ -0,0 +1,18 @@
# This file is generated by Vest!
# Do not modify!
# source: res://addons/vest/test/mixins/logging-mixin.gd
extends "res://addons/vest/_generated-mixins/5-96ae432f.gd"
# Manages logs added using [method Vest.message]
func _init():
super()
on_case_finish.connect(func(_case):
var messages := Vest._get_messages()
if not messages.is_empty():
_result.data["messages"] = messages
Vest._clear_messages()
)
@@ -0,0 +1 @@
uid://dqqt1ecooeqdd