OnPlayerReported
OnPlayerReported is called on the server after a player submits an in-game F1 report against another player, once the report record has been built and before it is forwarded to the reports backend.
void OnPlayerReported(BasePlayer player, string targetName, string targetId, string subject, string message, string reportType)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer who filed the report (the reporter), not the reported target. Null-check it before use. |
targetName | string | Display name of the player being reported, as resolved by the server at report time. |
targetId | string | Steam ID string of the reported player. This is the value you want when matching a report to an account or team. |
subject | string | The report subject/category label shown in the report record. |
message | string | Free-text message the reporting player typed in the report dialog. |
reportType | string | Report type string (for example the abuse/type identifier) that the server assigns to the report. |
Return value
Return value is ignored. The hook is called for observation only; returning null, true or any other value does not cancel, modify or delay the report being sent to the reports endpoint.
How it works
This hook fires when a player sends an in-game report through the F1 report dialog. The server builds the report entry (target name, subject, message, type) and then calls Interface.CallHook("OnPlayerReported", ...). The player argument is the reporter; targetId is the Steam ID of the player being accused, and targetName is that player's display name at the moment of reporting. subject, message and reportType describe the complaint itself and are useful for filtering noise (empty messages, spammed categories).
The hook is informational only. Because Oxide matches your declared parameter types against the arguments, declaring player as a more specific subclass of BasePlayer would cause the call to be skipped for players outside that type, so keep BasePlayer. Treat every value as untrusted input typed by a player: trim lengths, never echo it raw into chat, and never assume targetId belongs to a connected player. Reports arrive rarely, but a griefing group can generate bursts, so keep handlers cheap and push anything heavy (webhooks, database writes) onto a timer.
Example plugin: Log player reports to staff and throttle report spam
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Report Audit", "RustPluginGenerator", "1.0.0")]
[Description("Logs in-game player reports and alerts online staff with a per-reporter cooldown.")]
public class ReportAudit : RustPlugin
{
private const string PermAlert = "reportaudit.alert";
private PluginConfig config;
private ReportLog data;
private readonly Dictionary<ulong, float> lastAlert = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Alert cooldown seconds")]
public float AlertCooldown = 120f;
[JsonProperty("Max stored reports")]
public int MaxStored = 500;
}
private class ReportEntry
{
public string ReporterId;
public string ReporterName;
public string TargetId;
public string TargetName;
public string Message;
public string Type;
public string TimeUtc;
}
private class ReportLog
{
public List<ReportEntry> Entries = new List<ReportEntry>();
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Alert"] = "{0} reported {1} ({2})",
["NoTarget"] = "{0} reported an unknown player ({1})"
}, this);
}
private string Msg(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermAlert, this);
data = Interface.Oxide.DataFileSystem.ReadObject<ReportLog>(Name) ?? new ReportLog();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
lastAlert.Clear();
}
private void OnPlayerReported(BasePlayer player, string targetName, string targetId, string subject, string message, string reportType)
{
if (player == null || string.IsNullOrEmpty(targetId)) return;
if (data.Entries.Count >= config.MaxStored) data.Entries.RemoveAt(0);
data.Entries.Add(new ReportEntry
{
ReporterId = player.UserIDString,
ReporterName = player.displayName,
TargetId = targetId,
TargetName = Truncate(targetName, 40),
Message = Truncate(message, 200),
Type = Truncate(reportType, 40),
TimeUtc = DateTime.UtcNow.ToString("u")
});
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
float now = Time.realtimeSinceStartup;
float last;
if (lastAlert.TryGetValue(player.userID, out last) && now - last < config.AlertCooldown) return;
lastAlert[player.userID] = now;
string text = string.IsNullOrEmpty(targetName)
? Msg("NoTarget", null, player.displayName, targetId)
: Msg("Alert", null, player.displayName, Truncate(targetName, 40), targetId);
foreach (var online in BasePlayer.activePlayerList)
{
if (online != null && online.IsConnected && permission.UserHasPermission(online.UserIDString, PermAlert))
SendReply(online, text);
}
}
private static string Truncate(string value, int length)
{
if (string.IsNullOrEmpty(value)) return string.Empty;
return value.Length <= length ? value : value.Substring(0, length);
}
}
}The plugin records every in-game report into a per-save data file and alerts online staff who hold the reportaudit.alert permission. OnPlayerReported builds an entry with the reporter's ID and name, the target ID and name, and the message, then writes it through Interface.Oxide.DataFileSystem. To stop one player flooding staff chat, a short cooldown per reporter is tracked in memory: only the first report inside the window triggers a Broadcast to permitted staff, while every report is still stored. Team members of the target are noted in the log so staff can spot coordinated reports. The plugin registers its permission in Init and clears the cooldown dictionary in Unload.
Common use cases
Staff alerting
Broadcast a short summary to admins holding a permission so reports are seen even when F1 reports are missed during busy raids.
Report history per save
Append reporter, target and message to a data file so patterns (one player reported repeatedly, or one player reporting everyone) can be reviewed later.
Reward legit reporting
If the Economy plugin is loaded, pay the reporter a small amount, using
targetIdand a cooldown to prevent farming the same target.
Pitfalls
Reporters can type anything
The message and subject are player-controlled strings. Truncate before logging or chatting, and never treat them as commands or player names.
targetId is not guaranteed online
The reported player may be disconnected or already banned. Look them up with
BasePlayer.FindByIDdefensively or work with the ID string only.Bursts from organized groups
A group can mass-report a base owner. Throttle per reporter and consider deduplicating identical target IDs inside a short window before alerting staff.
Build a plugin with OnPlayerReported
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnPlayerReported
Generate