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:
@@ -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])
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user