t_p6_api: build plugin scripting system

- Add PluginBase (plugin_base.gd) — abstract base class with 12 virtual hooks
- Add PluginManifest (plugin_manifest.gd) — custom Resource for plugin metadata
- Add PluginManager (plugin_manager.gd) — autoload singleton: directory scan,
  hook dispatch, lifecycle (load/unload/reload/rescan), cvar integration
- Add hello_world example plugin with all hooks demonstrating usage
- Register PluginManager autoload in project.godot
- Extend rcon_command_handler.gd with 'plugin' subcommand (list/load/unload/
  reload/info/rescan) delegating to PluginManager
This commit is contained in:
2026-07-01 18:36:23 -04:00
parent ffa72a8f24
commit 159c554a86
7 changed files with 987 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
## Hello World — Example Server Plugin
##
## Demonstrates all available plugin hooks by logging each event.
## Use this as a template for creating new plugins.
extends PluginBase
class_name HelloWorldPlugin
# ---------------------------------------------------------------------------
# Plugin state
# ---------------------------------------------------------------------------
var _player_count: int = 0
var _round_count: int = 0
var _tick_count: int = 0
var _start_time: int = 0
# ---------------------------------------------------------------------------
# Initialization
# ---------------------------------------------------------------------------
func _ready() -> void:
log_info("Hello World plugin initializing...")
_start_time = Time.get_ticks_msec()
func _exit_tree() -> void:
log_info("Hello World plugin shutting down. Uptime: %.1f seconds" %
[(Time.get_ticks_msec() - _start_time) / 1000.0])
# ---------------------------------------------------------------------------
# Server lifecycle hooks
# ---------------------------------------------------------------------------
func on_server_start() -> void:
log_info("on_server_start — Server is now online")
_tick_count = 0
func on_server_stop() -> void:
log_info("on_server_stop — Server is shutting down")
# ---------------------------------------------------------------------------
# Map lifecycle hooks
# ---------------------------------------------------------------------------
func on_map_start(map_name: String) -> void:
log_info("on_map_start — Loading map: %s" % map_name)
_player_count = 0
_round_count = 0
func on_map_end(map_name: String) -> void:
log_info("on_map_end — Map ended: %s (rounds played: %d)" % [map_name, _round_count])
# ---------------------------------------------------------------------------
# Player hooks
# ---------------------------------------------------------------------------
func on_player_connect(id: int, name: String, steam_id: String) -> bool:
log_info("on_player_connect — Player connecting: id=%d, name='%s', steam_id=%s" %
[id, name, steam_id])
# Return true to allow connection
return true
func on_player_disconnect(id: int, reason: String) -> void:
log_info("on_player_disconnect — Player disconnected: id=%d, reason='%s'" %
[id, reason])
func on_player_say(id: int, msg: String) -> bool:
log_info("on_player_say — Player %d says: \"%s\"" % [id, msg])
# Return true to let the message through
return true
func on_player_spawn(id: int) -> void:
_player_count += 1
log_info("on_player_spawn — Player %d spawned (total spawns: %d)" %
[id, _player_count])
func on_player_killed(victim: int, killer: int, weapon: String) -> void:
var desc: String
if victim == killer:
desc = "suicide"
else:
desc = "killed by %d" % killer
log_info("on_player_killed — Player %d %s with '%s'" % [victim, desc, weapon])
# ---------------------------------------------------------------------------
# Round hooks
# ---------------------------------------------------------------------------
func on_round_start(number: int) -> void:
_round_count = number
log_info("on_round_start — Round %d begins" % number)
func on_round_end(winner_team: int, reason: String) -> void:
var team_name: String = "Draw"
match winner_team:
1:
team_name = "Team A"
2:
team_name = "Team B"
log_info("on_round_end — Round %d ended: %s wins (%s)" %
[_round_count, team_name, reason])
# ---------------------------------------------------------------------------
# Per-tick hook
# ---------------------------------------------------------------------------
func on_tick(delta: float) -> void:
_tick_count += 1
# Log only every ~100 ticks to avoid spam (at 128 Hz ≈ every 0.78s)
if _tick_count % 100 == 0:
log_info("on_tick — Tick %d (delta=%.4f)" % [_tick_count, delta])
+17
View File
@@ -0,0 +1,17 @@
[gd_resource type="Resource" format=3]
[ext_resource type="Script" path="res://server/scripts/plugin_api/plugin_manifest.gd" id="1_plugin_manifest"]
[resource]
script = ExtResource("1_plugin_manifest")
name = "Hello World"
version = "1.0.0"
author = "Tactical Shooter Team"
script_path = "hello_world.gd"
enabled = true
min_api_version = "1.0.0"
max_api_version = ""
metadata = {
"description": "Example plugin that logs all server events",
"url": "https://gitea.ourpad.casa/shawn/tactical-shooter"
}