if EzAdmin.HasItem(player, "vip_hat") then ... end
if (EzAdminClient.IsFrozen) controller.enabled = false;
EzAdminPlugin.api().recordPurchase(uuid, "Kit", 500, "coins", id);The one rule: the server decides
Everything EzAdmin gives your game says what a player is allowed. The thing itself, paying out a reward or letting a message through, must happen on your server.
A game client can be modified. A client that is told "double rewards are on" and then asks for its own reward is a client a mod menu can make ask for a million. So:
- Unity on PlayFab: anything that pays out, unlocks or lets a player talk goes through CloudScript. The client may read the same state to show it (a "Double XP" banner, a greyed out chat box), and that is all.
SendChat, ClaimReward). The handler checks the player with the EzAdmin helpers, using currentPlayerId, and does the work with the Server API. The client never sends an amount, a player id or anything else it expects to be trusted. See CloudScript.Mutes
A member timed out in Discord is muted in your game for the same length, and unmuted when the timeout ends or is removed. Turn it on in /settings: Discord timeouts mute them in game.
- PlayFab games: the mute is written to the player as
EzAdmin_Mute. Chat that goes through CloudScript checksezadminIsMuted(currentPlayerId). Chat that does not (Photon, Vivox, your own server) checks it on that server, or at the very least greys out the chat box with the client helpers.
Live events
/event start name:Double XP multiplier:2 length:2h in Discord turns an event on in your game. Your reward code multiplies by EzAdmin's multiplier, which is 1 whenever nothing is on, so the same line of code works every day of the year. Events end by themselves.
/freeze holds a player in place while staff look into them. A Unity game reads IsFrozen and stops its own player controller.
PlayFab CloudScript
For Unity games on PlayFab, this is where anything that matters is decided. Paste EzAdmin.CloudScript.js at the bottom of your revision (Game Manager, Automation, CloudScript) and call these helpers from your own handlers.
handlers.SendChat = function (args, context) {
if (ezadminIsMuted(currentPlayerId)) {
return { ok: false, message: "Muted for " + ezadminMuteSecondsLeft(currentPlayerId) + "s." };
}
return postChat(currentPlayerId, args.text);
};
Always currentPlayerId, never a player id from args. currentPlayerId comes from the signed in session and cannot be faked; anything in args came from the client.
| Helper | Does |
|---|---|
ezadminIsMuted(playFabId) | true while timed out in Discord |
ezadminMuteSecondsLeft(playFabId) | seconds of the timeout left |
ezadminNotifyBan(playFabId, name) | after your code bans someone: Discord gets a Lift ban button |
EzAdminGetMyStatus (handler) | for clients: { muted, mutedFor }, for showing only |
EzAdminRedeemLinkCode, EzAdminGetLinkStatus (handlers) | linking; the client packages call these for you |
Unity (C#)
From EzAdminClient, after your own PlayFab login. For showing things; decide payouts in CloudScript.
using EzAdmin.Client;
EzAdminClient.EventChanged += () =>
{
eventBanner.SetActive(EzAdminClient.EventName != "");
eventLabel.text = EzAdminClient.EventName + " x" + EzAdminClient.Multiplier;
};
EzAdminClient.FreezeChanged += frozen => playerController.enabled = !frozen;
EzAdminClient.Instance.GetMuteStatus((muted, secondsLeft) =>
{
chatInput.interactable = !muted;
});
| Call | Gives |
|---|---|
EventName, Multiplier, EventEndsAt, EventChanged | the live event; Multiplier is 1 when none is on |
IsFrozen, FreezeChanged | /freeze and /unfreeze |
Announcement, LastAnnouncement | /announce; the Link Screen shows a banner by itself |
IsStaff, StaffChanged | the player may open the Staff Panel |
GetMuteStatus(Action<bool, int>) | muted, seconds left |
RedeemLinkCode(code, Action<bool, string>) | links the player; show the message |
ReportPlayer(playFabId, name, reason, ...) | a report into Discord |
ViewProfile(playFabId, Action<EzAdminProfile>) | their Discord profile, if they allow it |
Unity: your own screen, your own keyboard
The built in Link Screen is optional. EzAdminLink is one static class your code calls from anywhere, whatever keyboard or UI you have:
using EzAdmin.Client;
EzAdminLink.Redeem(myKeyboard.Text, (ok, message) => myStatusLabel.text = message);
EzAdminLink.WhenLinked(discordName =>
{
welcomeSign.text = "Welcome back, " + discordName;
discordBadge.SetActive(true);
});
if (EzAdminLink.IsLinked) { ... }
EzAdminLink.StatusChanged += message => myStatusLabel.text = message;
EzAdminLink | Does |
|---|---|
Redeem(code, onDone) | sends a code from any keyboard; onDone(ok, message) |
Check(onDone) | asks whether this account is linked; onDone(linked, discordName) |
IsLinked, DiscordName, IsBusy, Status | the current state |
WhenLinked(action) | runs now if linked, or the moment they link |
Linked, StatusChanged | events, from whichever screen did it |
Clean(raw), IsComplete(raw), CodeLength | for your own input box |
Reset() | after your game signs in a different player |
Keeping the built in screen but using your own keyboard: set its Keyboard to Custom, open your keyboard from On Keyboard Requested, and call SetCode on the EzAdminLinkScreen (it submits by itself when the code is complete). The screen also has AppendCharacter, Backspace, Clear, Submit, Open, Close and OpenSystemKeyboard.
Unity: Discord on your leaderboard
Show each player's Discord name, id and picture next to their score. One request covers up to 50 players, and results are cached, so a board that refreshes every few seconds does not ask again each time.
row.GetComponent<EzAdminProfileView>().Show(entry.PlayFabId, entry.DisplayName);
var ids = leaderboard.Select(e => e.PlayFabId).ToList();
EzAdminProfiles.Get(ids, found =>
{
foreach (var entry in leaderboard)
{
EzAdminProfile p = found[entry.PlayFabId];
string name = p.Visible ? p.Name : entry.DisplayName;
if (p.Visible) EzAdminProfiles.LoadAvatar(p.AvatarUrl, tex => rowFor(entry).Picture.texture = tex, 64);
}
});
Privacy is the player's. Another player sees someone's Discord only when the server shows profiles by default (/settings: Show linked players' Discord in game) or that player chose to show it with /profile; their own choice always wins. Staff EzAdmin trusts always see it. When it is hidden, Linked is true and Visible false: show your game's own name.
The website in your game
Linking and the Staff Panel are EzAdmin web pages. EzAdminClient.OpenLinkPage() and OpenStaffPanel() open them through EzAdminWeb: the browser by default (over the game on a Quest), or a panel inside your game if you add a web view plugin and set EzAdminWeb.Opener = url => myWebView.LoadUrl(url);
Rooms and staff tools
Rooms and staff lookup
Each Unity player reports themselves, so EzAdmin only knows which server or room they are in when your game says so. Call SetRoom when a player joins one (a Photon room, a Mirror or Netcode server, a dedicated server) and the Staff Panel groups players by server. LookupPlayer is for your own staff UI; EzAdmin checks the person asking holds Player Lookup in Discord.
EzAdminClient.Instance.SetRoom(PhotonNetwork.CurrentRoom.Name);
EzAdminClient.Instance.LookupPlayer("Kestrel", (ok, result) =>
{
if (!ok) { staffText.text = result.message; return; }
staffText.text = result.name + ": " + string.Join(", ", result.lines);
});
Staff tools
Switch on In game staff tools in /features and give the In game Staff Tools permission to your staff roles. Linked staff then get /ez vanish, /ez fly, /ez noclip, /ez tp <name>, /ez bring <name>, /ez heal and /ez god where your game supports them. Who may use them is decided in Discord, never by the game, and every use is written to the audit log.
Unity games make their own buttons, because only your game knows how its players move:
EzAdminClient.StaffToolsChanged += allowed => staffToolbar.SetActive(allowed);
void ToggleVanish()
{
if (!EzAdminClient.CanUseStaffTools) return;
vanished = !vanished;
myRenderer.enabled = !vanished;
EzAdminClient.Instance.LogStaffTool("vanish", vanished);
}
Data shapes
| Where | Name | Shape |
|---|---|---|
| PlayFab Read Only Data | EzAdmin_Mute | {"until": 1790000000} (unix seconds), removed when unmuted |
| PlayFab Read Only Data | EzAdmin_Link | {"discordId": "...", "discordName": "...", "guildId": "...", "at": ...} |
| Bridge job | mute / unmute | {"seconds": 600} / {} |
| Bridge job | announce | {"message": "..."}, for every player |
| Poll policy | event | {"name": "...", "multiplier": 2, "endsAt": ...}, or none |
Only EzAdmin writes these. PlayFab's Read Only Data can be read by the player's own client but only changed with your title's secret key, which never leaves EzAdmin and your CloudScript.
Missing something?
If your game needs a call this page does not have, open a ticket and say what you are building.