Bans & Mutes
The punishments module provides network-wide bans and mutes with fixed reasons from reasons.yml, escalation levels, admin bans, rank protection, a player profile (/lookup), history and unban appeals in the dashboard.
License: punishments is part of the free base by default (together with core, reports, lobby). Which modules are free can change – the dashboard shows locked modules grayed out.
Enabling
- Module key:
punishments(no dependencies). See Modules & Presets. - In a network, enable it on the proxy: the proxy registers
/ban,/unban,/mute,/unmute,/lookup,/playerinfo,/history,/modifyand/resethistoryand executes them centrally. The Spigot variants are only the fallback for single servers. - The ban check on join runs on every server, even without the module.
reasons.yml
All ban and mute reasons are in one list. The reasons.yml on the proxy is authoritative: the proxy stores it in the database on startup and on /eternal reload. API/dashboard (within 30 s) and all Spigot servers (within one minute) adopt exactly this list. The file on the Spigots only applies if there is no proxy.
reasons:
- id: 1
type: ban
label: "Hacking / Cheating"
duration: permanent
- id: 5
type: ban
label: "Schwere Beleidigung"
duration: 7d
escalation:
- 7d # 1st time
- 30d # 2nd time
- permanent # 3rd time and every time after
- id: 10
type: ban
label: "Strafbare Inhalte"
duration: permanent
permission: eternal.reason.extreme
- id: 20
type: mute
label: "Chat-Spam"
duration: 1h
- id: 90
type: ban
label: "Server-Sabotage"
duration: permanent
admin: true
| Field | Required | Meaning |
|---|---|---|
id | yes | Unique number starting at 1. Gaps are allowed. |
type | yes | ban (kicks, blocks joining) or mute (blocks chat). Alone decides the kind of punishment – so /ban <Spieler> 20 imposes a mute. |
label | yes | Text on the ban screen, in the broadcast and in the history. |
duration | yes | permanent or <n>s, <n>m, <n>h, <n>d, <n>w, <n>y. |
permission | no | Permission the executor needs. Default: eternal.ban, or eternal.ban.admin with admin: true. |
groupid | no | Minimum rank level. Someone without the permission may still use it if their level is ≥ this value. 0 = off. |
admin | no | true = admin ban: only eternal.ban.admin may impose it, only eternal.unban.admin may lift it. Read-only for normal mods in the dashboard. |
escalation | no | Duration per offense. Earlier, expired or lifted punishments with the same reason are counted. If there are more offenses than levels, the last one repeats. Without escalation, duration always applies. |
reasons.yml also contains the report reasons (report-reasons, see Reports) and the shortening templates for unban appeals (appeal-shorten-templates, see below).
Reason permissions in the dashboard (eternal.ban.reason.<id>)
For every reason, Eternal automatically registers the permission eternal.ban.reason.<id> (category „reasons“ in the permission editor). It controls the web report flow: in the dialog „Bannen/Muten aus Report“ ("Ban/mute from report"), only reasons the staff member may use appear, and the API rejects a reason without a matching permission with 403.
A reason can be used in the dashboard if all three conditions are met:
- Base permission:
eternal.mute(mute reason), oreternal.banoreternal.ban.admin(depending onadmin:). eternal.ban.reason.<id>– default level Team, i.e. opt-out: anyone with the base permission may use every reason until an admin sets the reason to Aus ("Off") for a rank.- A
permission:set inreasons.ymlmust also be held.
The option Sonstiges ("Other") in the dialog (free label + duration) sends no reason and only needs the base permission.
In-game, the reason permissions do not count. There, /ban and /mute only check permission: (or the default) and groupid.
Commands
In a network, all commands run on the proxy. Player names are also resolved for offline players.
| Command | Permission | Description |
|---|---|---|
/ban <Spieler> [Grund-Nr] | eternal.ban | Without a number: list of all reasons you may use. With a number ([Grund-Nr] = reason no.): punishment according to the reason's type. |
/mute <Spieler> [Grund-Nr] | eternal.mute | Like /ban – the type comes from the reason. |
/unban <Spieler> [Grund] | eternal.unban | Lifts the active ban ([Grund] = reason). Admin bans require eternal.unban.admin. |
/unmute <Spieler> [Grund] | eternal.unmute | Lifts the active mute. |
/lookup <Spieler> | eternal.lookup | Profile: UUID, rank, active punishments, latest entries. |
/playerinfo <Spieler> (/pinfo, /whois) | eternal.lookup | Compact player info (proxy only): rank, online, last IP, active ban/mute, nick. |
/history <Spieler> (/banhistory) | eternal.lookup | Ban, mute and report history as cards. |
/modify <Bann-ID> setduration <Dauer> | eternal.modify.duration | New duration, counted from now (permanent allowed). |
/modify <Bann-ID> setreason <Grund-ID oder Text> | eternal.modify.reason | New reason; a number takes over label and ID from reasons.yml, otherwise free text. |
/resethistory <Spieler> | eternal.history.reset | Deletes the history (see history.reset-mode). |
Everyone with eternal.notify receives notifications about new bans and mutes.
config.yml
# Behavior of /resethistory
history:
reset-mode: hard # hard = physically delete entries
# soft = keep entries with hidden=1 (restorable via SQL),
# no longer count for escalation, not shown in /history
Who may punish whom (levels)
Banning and muting are rank-protected: the executor needs a strictly higher level than the target. So equal ranks cannot punish each other.
- Level = maximum of the highest held permission
eternal.tier.0…eternal.tier.100and the priority of the highest group (Eternal Perms, formerly CloudNet sortId). - If the target is offline, the last level stored in their profile counts.
eternal.bypassremoves the protection completely. The console may always.- A reason's
groupiduses the same level: level ≥groupidreplaces the missing reason permission. - In the dashboard, the
/lookupview and the history are additionally rank-protected (your own level must be higher;eternal.bypassbypasses this). In-game,/lookupand/historyare not rank-protected.
Without ranks with priority and without eternal.tier.* permissions, everyone has level 0 – then nobody can ban anyone. Either maintain priorities in the ranks or give staff e.g. eternal.tier.50.
Legacy: with cloudnet.legacy-mapping: true, the groups listed under cloudnet.groups are mapped by name to permissions and tier. With Eternal Perms this is not needed (default false).
Ban screen
The kick text on a ban and on a join attempt comes from translations/<sprache>.yml (<sprache> = language), key ban-kick-screen:
ban-kick-screen: |-
&dEternal &8»
&7Du wurdest vom Netzwerk &cgebannt&7.
&dGrund&8: &b{reason}
&dDauer&8: &7{duration}
&dBann-ID&8: &c#{id}{appealBlock}
&8Beschwerde: &f/appeal
(German default text: "You have been banned from the network. Reason / Duration / Ban ID … Appeal: /appeal".)
| Placeholder | Contents |
|---|---|
{reason} | Label of the reason |
{duration} | Remaining duration or „permanent“ |
{id} | Ban ID (the player needs it for the appeal) |
{appealBlock} | Notice line after a rejected appeal, otherwise empty |
The line „Beschwerde: /appeal“ ("Appeal: /appeal") refers to the appeal page in the dashboard (<Dashboard-URL>/appeal). Adjust it to your domain. Other texts: ban-broadcast, mute-blocked-chat, ban-perm-denied, unban-admin-only, tier-blocked-action.
Unban appeals
Banned players submit appeals in two ways:
- Without login: the
/appealpage in the dashboard – player name, ban ID and justification (at least 10 characters). Name and ID must match the active ban. - With login: Mein Konto → Entbannungsantrag stellen ("My account → Submit unban appeal"). Banned players get into the dashboard via password login (see Dashboard: login & accounts). There they also see earlier appeals and, if available, the replay of their ban.
Only one open appeal per ban is possible.
Handling (Moderation → Entbannungsanträge)
| Action | Permission | Effect |
|---|---|---|
| View list | eternal.web.dashboard | Open appeals, filter by status |
| Genehmigen ("Approve") | eternal.web.appeals.decide | The ban is lifted |
| Ablehnen ("Reject") | eternal.web.appeals.decide | The ban stays; the note appears on the ban screen ({appealBlock}) |
| Verkürzen ("Shorten") | eternal.web.dashboard | Shorten the remaining duration via a template or a custom duration |
The templates come from reasons.yml:
appeal-shorten-templates:
- id: immediate
label: "Sofort entbannen"
duration: 0s
- id: 1d
label: "Auf 1 Tag verkuerzen"
duration: 1d
- id: 7d
label: "Auf 1 Woche verkuerzen"
duration: 7d
The duration can be overridden in the dialog (1d, 6h, 30m, permanent).
Dashboard pages
| Page | Permission | Contents |
|---|---|---|
| Moderation → Aktive Strafen ("Active punishments") | eternal.web.dashboard | Active bans and mutes; lifting with eternal.unban or eternal.unban.admin |
| Moderation → Entbannungsanträge ("Unban appeals") | eternal.web.dashboard | see above |
| Spieler ("Players") → profile | eternal.web.player.view | History, sessions, appeals, chat history |
| Moderation → Reports | eternal.report.handle | Ban/mute directly from the report with eternal.ban / eternal.mute |
A web ban kicks the player immediately.
Common pitfalls
- „Für diesen Grund fehlt dir die nötige Permission“ ("You lack the required permission for this reason") – the reason has its own
permission:(e.g. reason 10 requireseternal.reason.extreme) or is an admin reason. - „Du hast nicht den Rang, um … zu bestrafen“ ("You do not have the rank to punish …") – the target has the same or a higher level. Check the ranks' priorities.
- Reason change does not arrive – edit
reasons.ymlon the proxy and run/eternal reloadthere. Changes on the Spigots are ignored in a network. /modify setdurationextends unintentionally – the new duration counts from the time of the command, not from the start of the ban.- Reusing IDs – changing the meaning of an existing
iddistorts escalation and history. Better create new reasons with a newid. - In-game unban and replays – replays for a ban are deleted when it is lifted via the dashboard. See Replays.
- The permissions
eternal.reportsystemandeternal.lookup.bypass(inplugin.ymland in the example undercloudnet.groups) come from older versions and are no longer checked in the code. Reports are handled witheternal.report.handle; rank protection is bypassed witheternal.bypass.