Files
tactical-shooter/addons/vest/vest-mixins.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

137 lines
4.4 KiB
GDScript

extends Object
class_name VestMixins
## Manages mixins for [VestTest].
##
## Instead of having to implement all the functionality in one huge class,
## the functionality can be separated into mixins.
## [br][br]
## Mixins are classes whose functionality is combined into a single class, to
## produce the [VestTest] base class. This is done by generating scripts that
## Godot can then load.
## [br][br]
## When working with custom mixins, [b]make sure to refresh[/b] when a change
## is applied!
## List all active mixins.
static func list() -> Array[Script]:
var result: Array[Script] = []
result.assign((VestTest.new()).__get_vest_mixins())
return result
## Add a custom script to the list of mixins.
## [br][br]
## Make sure to call [method refresh], otherwise the generated classes are not
## updated.
static func add(mixin: Script):
var active_mixins := list()
if not active_mixins.has(mixin):
active_mixins.append(mixin)
refresh(active_mixins)
## Remove a custom script from the list of mixins.
## [br][br]
## Make sure to call [method refresh], otherwise the generated classes are not
## updated.
static func remove(mixin: Script):
var active_mixins := list()
if active_mixins.has(mixin):
active_mixins.erase(mixin)
refresh(active_mixins)
## Refresh the generated classes, making sure that all mixins used are up to
## date.
static func refresh(mixins: Array[Script] = []):
if mixins.is_empty():
mixins = list()
# Generate mixin chain
var mixin_chain: Array[Script] = []
mixin_chain.append(_get_test_base())
mixin_chain.append_array(_get_builtin_mixins())
var active_mixins: Array[Script] = []
for mixin_script in mixins:
if not mixin_script:
# Not script?
continue
if mixin_chain.has(mixin_script):
# Don't include the same mixin twice
continue
mixin_chain.append(mixin_script)
active_mixins.append(mixin_script)
mixin_chain.append(VestTest)
# Generate scripts
_clean_mixin_directory()
DirAccess.make_dir_recursive_absolute(_get_mixin_directory())
var extends_pattern := RegEx.create_from_string("^extends.*")
for i in range(1, mixin_chain.size()):
var parent_script := mixin_chain[i - 1]
var target_script := mixin_chain[i]
var parent_path := parent_script.resource_path
var target_path := target_script.resource_path
if i != 1:
parent_path = _get_generated_mixin_path(parent_script, i - 1)
if i != mixin_chain.size() - 1:
target_path = _get_generated_mixin_path(target_script, i)
# Generate source
var target_source := target_script.source_code
var script_header := (
"# This file is generated by Vest!\n" +
"# Do not modify!\n" +
"# source: %s\n" % [target_script.resource_path] +
"extends \"%s\"\n\n" % [parent_path])
target_source = extends_pattern.sub(target_source, "", true)
if target_script != VestTest:
target_source = script_header + target_source
else:
target_source = script_header
target_source += "class_name VestTest\n\n"
var mixin_fragments = active_mixins\
.map(func(it): return it.resource_path)\
.map(func(it): return "preload(\"%s\")" % it)
mixin_fragments = ", ".join(mixin_fragments)
target_source += "func __get_vest_mixins() -> Array:\n\treturn [%s]" % [mixin_fragments]
var target_file := FileAccess.open(target_path, FileAccess.WRITE)
target_file.store_string(target_source)
target_file.close()
static func _get_generated_mixin_name(script: Script, idx: int) -> String:
return "%d-%x.gd" % [idx, hash(script.resource_path.get_file())]
static func _get_generated_mixin_path(script: Script, idx: int) -> String:
return _get_mixin_directory() + _get_generated_mixin_name(script, idx)
static func _get_builtin_mixins() -> Array[Script]:
return [
preload("res://addons/vest/test/mixins/gather-suite-mixin.gd"),
preload("res://addons/vest/test/mixins/expect-mixin.gd"),
preload("res://addons/vest/test/mixins/assert-that-mixin.gd"),
preload("res://addons/vest/test/mixins/mock-mixin.gd"),
preload("res://addons/vest/test/mixins/capture-signal-mixin.gd"),
preload("res://addons/vest/test/mixins/logging-mixin.gd")
]
static func _get_mixin_directory() -> String:
return "res://addons/vest/_generated-mixins/"
static func _clean_mixin_directory():
for file in DirAccess.get_files_at(_get_mixin_directory()):
DirAccess.remove_absolute(_get_mixin_directory() + file)
static func _get_test_base() -> Script:
return preload("res://addons/vest/test/vest-test-base.gd") as Script