Files
tactical-shooter/addons/vest/ui/results-panel.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

293 lines
7.8 KiB
GDScript

@tool
extends Panel
var visibility_popup: VestUI.VisibilityPopup
@onready var _tree := %Tree as Tree
@onready var _spinner := %Spinner as Control
@onready var _spinner_icon := %"Spinner Icon" as TextureRect
@onready var _spinner_label := %"Spinner Label" as Label
@onready var _animation_player := $PanelContainer/Spinner/AnimationPlayer as AnimationPlayer
var _results: VestResult.Suite = null
var _render_results: VestResult.Suite = null
var _search_string: String = ""
signal on_collapse_changed()
func get_results() -> VestResult.Suite:
return _results
func set_results(results: VestResult.Suite) -> void:
if _results != results:
_results = results
_render()
func get_search_string() -> String:
return _search_string
func set_search_string(search_string: String) -> void:
if _search_string != search_string:
_search_string = search_string
_render()
func clear() -> void:
set_results(null)
func set_spinner(text: String, icon: Texture2D = null, animated: bool = true) -> void:
clear()
_spinner_icon.texture = icon
_spinner_label.text = text
_spinner.show()
if animated:
_animation_player.play("spin")
else:
_animation_player.stop()
func collapse() -> void:
var root := _tree.get_root()
if not root: return
for item in root.get_children():
item.set_collapsed_recursive(true)
func expand() -> void:
var root := _tree.get_root()
if not root: return
for item in root.get_children():
item.set_collapsed_recursive(false)
func toggle_collapsed() -> void:
if is_any_collapsed():
expand()
else:
collapse()
func is_any_collapsed() -> bool:
var at := _tree.get_root()
var queue := [] as Array[TreeItem]
while at:
queue.append_array(at.get_children())
if at.collapsed:
return true
at = queue.pop_back()
return false
func _ready():
_tree.item_collapsed.connect(func(_item):
on_collapse_changed.emit()
)
func _clear():
_tree.clear()
for connection in _tree.item_activated.get_connections():
connection["signal"].disconnect(connection["callable"])
_spinner.hide()
func _render():
_clear()
if _results != null:
_render_results = VestResult.Suite._from_wire(_results._to_wire()) # HACK: Duplicate
_filter_visibility(_render_results)
_filter_search(_render_results, _search_string)
_render_result(_render_results, _tree)
func _filter_visibility(results: VestResult.Suite) -> void:
var cases_to_remove := []
var suites_to_remove := []
for case in results.cases:
if not _check_visibility(case):
cases_to_remove.append(case)
for subsuite in results.subsuites:
if not _check_visibility(subsuite):
suites_to_remove.append(subsuite)
else:
_filter_visibility(subsuite)
for case in cases_to_remove:
results.cases.erase(case)
for subsuite in suites_to_remove:
results.subsuites.erase(subsuite)
func _filter_search(results: VestResult.Suite, needle: String) -> void:
if not needle:
# Search string empty, do nothing
return
var scores := {}
var parents := {}
var at := results
var queue: Array[VestResult.Suite] = []
var leaves: Array[VestResult.Suite] = []
# Calculate scores
while at:
for subsuite in at.subsuites:
queue.append(subsuite)
parents[subsuite] = at
if at.subsuites.is_empty():
leaves.append(at)
scores[at] = VestUI.fuzzy_score(needle, at.suite.name)
for case in at.cases:
scores[case] = maxf(
VestUI.fuzzy_score(needle, case.case.description),
VestUI.fuzzy_score(needle, case.case.method_name + "()")
)
at = queue.pop_back() as VestResult.Suite
# Propagate best scores from leaves
for leaf in leaves:
at = leaf
# Calculate best score for leaf
var best_score := scores[at] as float
for case in at.cases:
best_score = maxf(best_score, scores[case])
# Propagate upwards in tree
while at:
scores[at] = maxf(scores[at], best_score)
best_score = maxf(best_score, scores[at])
at = parents.get(at, null)
# Remove results that don't match the search string
at = results
queue.clear()
while at:
at.cases.sort_custom(func(a, b): return scores[a] > scores[b])
at.subsuites.sort_custom(func(a, b): return scores[a] > scores[b])
queue.append_array(at.subsuites)
at = queue.pop_back()
func _check_visibility(what: Variant) -> bool:
if what is VestResult.Case:
var case := what as VestResult.Case
return visibility_popup.get_visibility_for(case.status)
elif what is VestResult.Suite:
var suite := what as VestResult.Suite
for status in suite.get_unique_statuses():
if visibility_popup.get_visibility_for(status):
return true
return false
else:
push_warning("Checking visibility for unknown item: %s" % [what])
return true
func _render_result(what: Object, tree: Tree, parent: TreeItem = null):
if what is VestResult.Suite:
var item := tree.create_item(parent)
item.set_text(0, what.suite.name)
item.set_text(1, what.get_aggregate_status_string().capitalize())
item.set_icon(0, VestUI.get_status_icon(what))
item.set_icon_max_width(0, VestUI.get_icon_size())
tree.item_activated.connect(func():
if tree.get_selected() == item:
_navigate(what.suite.definition_file, what.suite.definition_line)
)
for subsuite in what.subsuites:
_render_result(subsuite, tree, item)
for case in what.cases:
_render_result(case, tree, item)
elif what is VestResult.Case:
var item := tree.create_item(parent)
item.set_text(0, what.case.description)
item.set_text(1, what.get_status_string().capitalize())
item.collapsed = what.status == VestResult.TEST_PASS
item.set_icon(0, VestUI.get_status_icon(what))
item.set_icon_max_width(0, VestUI.get_icon_size())
_render_data(what, tree, item)
tree.item_activated.connect(func():
if tree.get_selected() == item:
_navigate(what.case.definition_file, what.case.definition_line)
)
else:
push_error("Rendering unknown object: %s" % [what])
func _render_data(case: VestResult.Case, tree: Tree, parent: TreeItem):
var data := case.data.duplicate()
for message in case.messages:
var item := tree.create_item(parent)
item.set_text(0, message)
tree.item_activated.connect(func():
if tree.get_selected() == item:
# TODO: popup_dialog()
add_child(VestMessagePopup.of(message).window)
)
if data == null or data.is_empty():
return
if data.has("messages"):
var header_item := tree.create_item(parent)
header_item.set_text(0, "Messages")
for message in data["messages"]:
tree.create_item(header_item).set_text(0, message)
data.erase("messages")
if data.has("benchmarks"):
var header_item := tree.create_item(parent)
header_item.set_text(0, "Benchmarks")
for benchmark in data["benchmarks"]:
var benchmark_item = tree.create_item(header_item)
benchmark_item.set_text(0, benchmark["name"])
if benchmark.has("duration"): benchmark_item.set_text(1, benchmark["duration"])
for measurement in benchmark.keys():
if measurement == "name": continue
var measurement_item := tree.create_item(benchmark_item)
measurement_item.set_text(0, str(measurement).capitalize())
measurement_item.set_text(1, str(benchmark[measurement]))
data.erase("benchmarks")
if data.has("expect") and data.has("got"):
var header_item := tree.create_item(parent)
header_item.set_text(0, "Got:")
header_item.set_text(1, "Expected:")
var got_string := JSON.stringify(data["got"], " ")
var expect_string := JSON.stringify(data["expect"], " ")
var comparison_item := tree.create_item(header_item)
comparison_item.set_text(0, got_string)
comparison_item.set_text(1, expect_string)
tree.item_activated.connect(func():
# TODO: popup_dialog()
if tree.get_selected() in [header_item, comparison_item]:
add_child(VestComparisonPopup.of(expect_string, got_string).window)
)
data.erase("got")
data.erase("expect")
for key in data:
var item := tree.create_item(parent)
item.set_text(0, var_to_str(key))
item.set_text(1, var_to_str(data[key]))
func _navigate(file: String, line: int):
Vest._get_editor_interface().edit_script(load(file), line)