mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 15:54:52 +09:00
exp_reports, exp_roles and exp_groups wrapped the json they send to Lua in a fixed [=[ ]=] long bracket. A report reason, role name or group name containing ]=] closed it early and the rest ran as Lua through /sc. Any player can make a report, so any player could run code on the server. Use the next level the json does not contain, like the other plugins do. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
import type { InstancePluginContext } from "@clusterio/host";
|
|
import * as messages from "./messages.js";
|
|
|
|
/** Sent by the lua side when a player reports another. */
|
|
export type IpcReportCreate = {
|
|
player_name: string,
|
|
by_player_name: string,
|
|
reason: string,
|
|
};
|
|
|
|
/** Sent by the lua side to list the reports for a player, or for everyone. */
|
|
export type IpcReportList = {
|
|
caller: string,
|
|
player_name: string | undefined,
|
|
};
|
|
|
|
/** Sent by the lua side to delete the reports against a player, optionally only those from one player. */
|
|
export type IpcReportDelete = {
|
|
caller: string,
|
|
player_name: string,
|
|
by_player_name: string | undefined,
|
|
};
|
|
|
|
/**
|
|
* Bridges the lua side and the controller.
|
|
*
|
|
* Nothing is kept in the lua state, every request goes to the controller and
|
|
* the answer is handed back to lua once it arrives. The instance may have
|
|
* stopped by then, in which case the answer is dropped.
|
|
*/
|
|
export default async function (context: InstancePluginContext) {
|
|
const { instance } = context;
|
|
|
|
/** Hand an answer to lua, unless the instance stopped while it was being fetched. */
|
|
async function luaSend(receiver: string, json: any) {
|
|
if (instance.status !== "running") {
|
|
return;
|
|
}
|
|
const text = JSON.stringify(json);
|
|
// Reasons can contain anything, so pick a long bracket level the json does not close
|
|
let level = "=";
|
|
while (text.includes(`]${level}]`)) {
|
|
level += "=";
|
|
}
|
|
await instance.sendRcon(
|
|
`/sc exp_reports.${receiver}(helpers.json_to_table[${level}[${text}]${level}])`, true
|
|
);
|
|
}
|
|
|
|
instance.server.handle("exp_reports:create", async (event: IpcReportCreate) => {
|
|
try {
|
|
const report = await instance.sendTo("controller", new messages.ReportCreateRequest(
|
|
event.player_name, event.reason, event.by_player_name,
|
|
));
|
|
const reports = await instance.sendTo("controller", new messages.ReportListRequest(event.player_name));
|
|
await luaSend("receive_created", {
|
|
report: report.toJSON(),
|
|
reports: reports.map(other => other.toJSON()),
|
|
});
|
|
} catch (err: any) {
|
|
await luaSend("receive_error", { caller: event.by_player_name, message: err.message });
|
|
}
|
|
});
|
|
|
|
instance.server.handle("exp_reports:list", async (event: IpcReportList) => {
|
|
try {
|
|
const reports = await instance.sendTo("controller", new messages.ReportListRequest(event.player_name));
|
|
await luaSend("receive_list", {
|
|
caller: event.caller,
|
|
player_name: event.player_name,
|
|
reports: reports.map(report => report.toJSON()),
|
|
});
|
|
} catch (err: any) {
|
|
await luaSend("receive_error", { caller: event.caller, message: err.message });
|
|
}
|
|
});
|
|
|
|
instance.server.handle("exp_reports:delete", async (event: IpcReportDelete) => {
|
|
try {
|
|
let reports = await instance.sendTo("controller", new messages.ReportListRequest(event.player_name));
|
|
if (event.by_player_name !== undefined) {
|
|
reports = reports.filter(report => report.byPlayerName === event.by_player_name);
|
|
}
|
|
for (const report of reports) {
|
|
await instance.sendTo("controller", new messages.ReportDeleteRequest(report.id));
|
|
}
|
|
await luaSend("receive_deleted", {
|
|
caller: event.caller,
|
|
player_name: event.player_name,
|
|
by_player_name: event.by_player_name,
|
|
count: reports.length,
|
|
});
|
|
} catch (err: any) {
|
|
await luaSend("receive_error", { caller: event.caller, message: err.message });
|
|
}
|
|
});
|
|
}
|