Add the exp_reports plugin and move the scenario onto it - #469
Open
bbassie wants to merge 1 commit into
Open
Conversation
Reports were the last legacy control module, kept in the lua state of each instance. They are now an exp_reports plugin which stores them on the controller: a report is immutable and holds the reported player, who made it, the reason, the instance name and when it was made, and the only operations are get, list, create and delete. In game nothing is stored. The lua module sends each request over ipc, the instance plugin asks the controller and hands the answer back with rcon, and the module prints it to whoever asked if they are still online. Answers are dropped if the instance stopped meanwhile. The module raises on_player_reported with the count and reporters of every report against the player, which report_jail and discord_alerts use, and the commands and player list button call into it. The web ui gets a /reports page: a table with search on the player, reporter and reason, a filter on the instance, delete per row and a create form which reports in the name of the web user. New reports are posted to two optional webhooks configured on the controller, a Discord channel hook as an embed and a generic JSON hook. Failures are logged rather than failing the report. Tests cover the messages, the controller against a real Controller, the instance plugin against a real Instance, and the lua module through the shared harness.
bbassie
force-pushed
the
feature/reports-plugin
branch
from
September 6, 2026 16:44
a94293d to
bad98c5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reports were the last legacy control module, kept in each instance's lua state. This replaces them with an
exp_reportsplugin along the lines you described: reports are immutable and hold the player, who reported them, the reason, the instance name and when it was made; the only operations are get, list, create and delete (ReportGetRequest,ReportListRequestwith an optional player,ReportCreateRequest,ReportDeleteRequest), andReportUpdatedEventfeeds the web ui. The controller keeps them in aSubscribableDatastore(database/exp_reports/reports.json), allows one report per reporter per player, and for web created reports takes the reporter from the connection's user rather than the request.Nothing is stored in game. The lua module (
modules/exp_reports) sendsexp_reports:create|list|deleteover ipc, the instance plugin asks the controller and hands the answer back with rcon (receive_created,receive_list,receive_deleted,receive_error), and the module prints it to whoever asked if they are still online. Answers are dropped when the instance is no longer running. Because the jail check needs the other reports, the create path also fetches the reports against the player and raiseson_player_reportedwithreport_countandby_player_names;report_jail,discord_alerts, the report commands and the player list button now go through the module, and the response strings moved into the module's own locale ([exp-reports], en/zh-CN/zh-TW).The web ui gets a
/reportspage (permissionexp_reports.report.list): a table with column search on player, reporter and reason (viauseColumnSearch/useTableQueryState), an instance filter, time sort, delete per row behindexp_reports.report.delete, and a create form behindexp_reports.report.createwhich reports in the name of the web user.Two optional controller config fields,
exp_reports.discord_webhook_urlandexp_reports.json_webhook_url, receive every new report: the Discord one as an embed with player, reporter, instance and reason, the JSON one as{ "type": "report_created", "report": {...} }. Webhook failures are logged and never fail the report.Tests (
pnpm --filter @expcluster/reports test, 79 assertions): message round trips, the controller against a realControllerwith anInstanceRecordand a fake control connection (instance vs web reporter, duplicate and empty reason refusals, list/get/delete, subscription replay, webhooks posted only for new reports, failed posts logged), the instance plugin against a realInstance(each ipc path, a controller refusal reaching the reporter, answers dropped after stop), and the lua module through the shared harness (payloads sent, every receiver's printing and events, offline players skipped). Lua lint is clean.Smoke tested on my dev cluster: a report created from the web path came back with the connected user as reporter and no instance; one created from lua over rcon came back with
instance_name: "EXP"; a local node http server on the JSON webhook received the record; the lua delete path removed both. The in game printing and the web page itself need a player and a browser, which I do not have here.One thing to weigh: the reporter's own commands are still in exp_scenario with their existing permissions, only the storage and printing moved. If you would rather the commands live in the plugin too, that is a small follow up.