Admin tools
A kitchen-sink admin plugin: the /gamemode family, /give, a three-level control panel, freeze and mute. 314 lines of Skript, 445 of SwoftLang; exercises commands, GUIs, pagination, persistence, world weather, the cancellable PlayerCommand event, schedules and the raw-packet escape hatch.
options:
Prefix: &x&F&F&9&3&F&8&lᴀᴅᴍɪɴ+ &7»&r
CommandPrefix: admin+
gamemodePermission: minecraft.command.gamemode
weatherPermission: minecraft.command.weather
givePermission: minecraft.command.give
freezePermission: freeze.use
mutePermission: mute.use
menuPermission: op
unfrozenMessage: &dYou have been unfrozen!
freezeMessage: &dYou have been frozen!
PermissionMessage: &cʏᴏᴜ ᴀʀᴇ ɴᴏᴛ ᴀʟʟᴏᴡᴇᴅ ᴛᴏ ᴜѕᴇ ᴛʜɪѕ ᴄᴏᴍᴍᴀɴᴅ!// admin.sk port — /gamemode family, /give, a paginated admin panel,
// freeze and mute. Skript's options block becomes module-level vars.
var prefix = "<#FF93F8><bold>ᴀᴅᴍɪɴ+</bold> <gray>»</gray>"
var permission_message = "<red>ʏᴏᴜ ᴀʀᴇ ɴᴏᴛ ᴀʟʟᴏᴡᴇᴅ ᴛᴏ ᴜѕᴇ ᴛʜɪѕ ᴄᴏᴍᴍᴀɴᴅ!"
// gui handlers run against gui scope (player/state/slot), not module vars —
// prefixed chat always goes through this helper
function tell(target: Player, msg: String) {
send "${prefix} ${msg}" to target
}Why it maps this way
{@X} is textual substitution at parse time; var is a real binding. gui handlers run against gui scope (player, state, slot) and don't see module vars, so every screen routes prefixed chat through the tell() helper. Two differences from the .sk: permission: takes a literal string, and a denied command shows the runtime's message instead of a per-command permission message:.
command /gamemode <gamemode> [<player>]:
permission: {@gamemodePermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
set {_p} to name of player
if arg-2 is set:
set {_p} to name of arg-2
if arg-1 is a gamemode:
send "{@Prefix} &dGamemode set to %arg-1% for %{_p} in proper case%."
set the gamemode of {_p} parsed as player to arg-1
else:
send "{@Prefix} &d%arg-1% is not a gamemode!"// one function replaces five copy-pasted Skript triggers (and the gma
// copy-paste bug in the .sk, which set survival, has nowhere to live)
function apply_gamemode(actor: Player, target: Player, mode: String) {
set target.gamemode to mode
send "${prefix} <light_purple>Gamemode set to ${mode} for ${target.name}." to actor
}
command "gamemode" {
permission: "minecraft.command.gamemode"
description: "Set your or another player's gamemode"
arguments {
mode: String
target: Player = sender
}
execute {
if args.mode is "survival" or args.mode is "creative"
or args.mode is "adventure" or args.mode is "spectator" {
apply_gamemode(sender, args.target, args.mode)
} else {
send "${prefix} <light_purple>${args.mode} is not a gamemode!" to sender
}
}
}Why it maps this way
arg-2 is set + parsed as player becomes a typed argument with a default: target: Player = sender. A name that isn't online never reaches the handler. Literal gamemode strings are enum-checked at compile time; this dynamic one is validated in the handler, like the .sk.
command gmc [<player>]:
permission: {@gamemodePermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
set {_p} to name of player
if arg-1 is set:
set {_p} to name of arg-1
execute player command "gamemode creative %{_p}%"
command gms [<player>]:
permission: {@gamemodePermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
set {_p} to name of player
if arg-1 is set:
set {_p} to name of arg-1
execute player command "gamemode survival %{_p}%"
command gmsp [<player>]:
permission: {@gamemodePermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
set {_p} to name of player
if arg-1 is set:
set {_p} to name of arg-1
execute player command "gamemode spectator %{_p}%"
command gma [<player>]:
permission: {@gamemodePermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
set {_p} to name of player
if arg-1 is set:
set {_p} to name of arg-1
execute player command "gamemode survival %{_p}%"command "gmc" {
permission: "minecraft.command.gamemode"
arguments {
target: Player = sender
}
execute {
apply_gamemode(sender, args.target, "creative")
}
}
command "gms" {
permission: "minecraft.command.gamemode"
arguments {
target: Player = sender
}
execute {
apply_gamemode(sender, args.target, "survival")
}
}
command "gmsp" {
permission: "minecraft.command.gamemode"
arguments {
target: Player = sender
}
execute {
apply_gamemode(sender, args.target, "spectator")
}
}
command "gma" {
permission: "minecraft.command.gamemode"
arguments {
target: Player = sender
}
execute {
apply_gamemode(sender, args.target, "adventure")
}
}Why it maps this way
The .sk re-enters itself with execute player command "gamemode ..." — four times, copy-pasted. There is no execute-a-command-string statement; the port calls a shared function. Which is also where the .sk's bug shows: its gma sets survival (paste slip). A shared function has no copy to paste wrong.
command /weather <text>:
permission: {@weatherPermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
if arg-1 is "sun" or "clear" or "rain" or "storm":
set weather to arg-1 parsed as weather
send "{@Prefix} &dWeather set to %arg-1%." to player
else:
send "{@Prefix} &d%arg-1 in proper case% is not a type of weather!" to player// worlds carry a writable weather enum (clear|rain|thunder); the command maps
// the .sk's vocabulary onto it and flips the sky for real
command "weather" {
permission: "minecraft.command.weather"
arguments {
kind: String
}
execute {
set w to sender.world
if args.kind is "sun" or args.kind is "clear" {
set w.weather to "clear"
send "${prefix} <light_purple>Weather set to ${args.kind}." to sender
} else if args.kind is "rain" {
set w.weather to "rain"
send "${prefix} <light_purple>Weather set to ${args.kind}." to sender
} else if args.kind is "storm" {
set w.weather to "thunder"
send "${prefix} <light_purple>Weather set to ${args.kind}." to sender
} else {
send "${prefix} <light_purple>${args.kind} is not a type of weather!" to sender
}
}
}Why it maps this way
Worlds expose a writable weather row — a clear/rain/thunder enum validated at compile time (alongside read-only raining/thundering booleans). The port maps the .sk's sun/clear/rain/storm vocabulary onto it and actually changes the sky; only an unrecognized word falls through to the "not a type of weather" reply.
command /give <text> <item> <integer=1>:
permission: {@givePermission}
permission message: {@PermissionMessage}
trigger:
set {_item} to arg-2
set {_amount} to arg-3
if arg-1 is "@a":
give {_amount} of {_item} to all players
broadcast "{@Prefix} &d%player% gave %{_amount}% %{_item}%(s) to everyone!"
else if arg-1 is "@s" or player's name:
give {_amount} of {_item} to player
send "{@Prefix} &dYou've been given %{_amount}% %{_item}%(s)!" to player
else:
set {_target} to arg-1 parsed as player
if {_target} is online:
give {_amount} of {_item} to {_target}
send "{@Prefix} &dYou've been given %{_amount}% %{_item}%(s) by %player%!" to {_target}
send "{@Prefix} &dGave %{_amount}% %{_item}%(s) to %{_target}%." to player
else:
send "{@Prefix} &cPlayer '%arg-1%' not found." to playercommand "give" {
permission: "minecraft.command.give"
description: "Give an item to a player, everyone, or yourself"
arguments {
target: String
material: String
amount: Integer = 1
}
execute {
if args.target is "@a" {
loop all players as p {
set p.held_item to item(args.material, args.amount)
}
broadcast "${prefix} <light_purple>${sender.name} gave ${args.amount} ${args.material}(s) to everyone!"
} else if args.target is "@s" or args.target is sender.name {
set sender.held_item to item(args.material, args.amount)
send "${prefix} <light_purple>You've been given ${args.amount} ${args.material}(s)!" to sender
} else {
set found to player(args.target)
if found exists {
set found.held_item to item(args.material, args.amount)
send "${prefix} <light_purple>You've been given ${args.amount} ${args.material}(s) by ${sender.name}!" to found
send "${prefix} <light_purple>Gave ${args.amount} ${args.material}(s) to ${found.name}." to sender
} else {
send "${prefix} <red>Player '${args.target}' not found." to sender
}
}
}
}Why it maps this way
@a/@s are matched as plain strings, exactly like the .sk. Skript's give adds to the inventory; here the port writes held_item, which replaces the held stack, because give item is reserved for declared custom items. Same items delivered, different slot.
on tab complete of "/give":
set tab completions for position 1 to all players, "@a", and "@s"
set tab completions for position 2 to all items
set tab completions for position 3 to 1, 2, 3, 4, 5, 6, 7, 8, and 9
on tab complete of "/weather":
set tab completions for position 1 to "sun", "rain", and "storm"
on tab complete of "/gamemode":
set tab completions for position 1 to all gamemodes
set tab completions for position 2 to all players// No 'on tab complete' sections: completion falls out of the typed
// arguments blocks above (Player args complete to online players, defaults
// make trailing args optional).Why it maps this way
Three handler blocks become zero lines: completion derives from the typed arguments blocks — Player args complete to online players, defaults make trailing args optional.
function Main(p: player):
set {_gui} to chest inventory with 3 rows named "&bᴀᴅᴍɪɴ ᴘᴀɴᴇʟ"
loop 27 times:
set slot (loop-number - 1) of {_gui} to gray stained glass pane named " "
set slot 12 of {_gui} to skull of {_p} named "&eᴘʟᴀʏᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
set slot 13 of {_gui} to clock named "&eᴛɪᴍᴇ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
set slot 14 of {_gui} to sunflower named "&eᴡᴇᴀᴛʜᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
open {_gui} to {_p}gui "admin_panel" {
rows: 3
title: "<aqua>ᴀᴅᴍɪɴ ᴘᴀɴᴇʟ"
fill: item("GRAY_STAINED_GLASS_PANE", name: " ")
slot 12 {
item {
skull: player
name: "<yellow>ᴘʟᴀʏᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
}
on_click {
open gui "player_management" to player
}
}
slot 13 {
item {
material: "CLOCK"
name: "<yellow>ᴛɪᴍᴇ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
}
on_click {
open gui "time_management" to player
}
}
slot 14 {
item {
material: "SUNFLOWER"
name: "<yellow>ᴡᴇᴀᴛʜᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
}
on_click {
open gui "weather_management" to player
}
}
}Why it maps this way
skull of {_p} → skull: player — item expressions re-evaluate per viewer, so the head is always the opener's.
function PManagement(p: player):
set {_gui} to chest inventory with 3 rows named "&bᴘʟᴀʏᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
loop 27 times:
set slot (loop-number - 1) of {_gui} to gray stained glass pane named " "
set {_slot} to 0
loop all players:
set slot {_slot} of {_gui} to skull of loop-value named "&e%loop-value%"
add 1 to {_slot}
set slot 26 of {_gui} to arrow named "&cʙᴀᴄᴋ"
open {_gui} to {_p}gui "player_management" {
rows: 3
title: "<aqua>ᴘʟᴀʏᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
fill: item("GRAY_STAINED_GLASS_PANE", name: " ")
// the .sk packs heads by hand into a 27-slot grid with no page math;
// paginate owns the layout, arrows and clamping
paginate {
source: all_players()
slots: [9..17]
render {
skull: item
name: "<yellow>${item.name}"
}
on_click {
open gui "player_actions" to player with { target: item }
}
prev_slot: 18
next_slot: 26
}
slot 22 {
item { material: "ARROW", name: "<red>ʙᴀᴄᴋ" }
on_click {
go back for player
}
}
}Why it maps this way
The .sk packs heads into slots by hand with a manual counter and no page math. paginate owns layout, prev/next arrows and clamping; source: all_players() re-evaluates on render.
function DManagment(p: player):
set {_gui} to chest inventory with 3 rows named "&bᴛɪᴍᴇ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
loop 27 times:
set slot (loop-number - 1) of {_gui} to gray stained glass pane named " "
set slot 10 of {_gui} to clock named "&eѕᴜɴʀɪѕᴇ"
set slot 11 of {_gui} to clock named "&eᴅᴀʏ"
set slot 12 of {_gui} to clock named "&eɴᴏᴏɴ"
set slot 13 of {_gui} to clock named "&eᴀꜰᴛᴇʀɴᴏᴏɴ"
set slot 14 of {_gui} to clock named "&eꜱᴜɴꜱᴇᴛ"
set slot 15 of {_gui} to clock named "&eɴɪɢʜᴛ"
set slot 16 of {_gui} to clock named "&eᴍɪᴅɴɪɢʜᴛ"
set slot 22 of {_gui} to arrow named "&cʙᴀᴄᴋ"
open {_gui} to {_p}gui "time_management" {
rows: 3
title: "<aqua>ᴛɪᴍᴇ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
fill: item("GRAY_STAINED_GLASS_PANE", name: " ")
// the .sk routes every clock through console 'time set N' commands
// matched by DISPLAY NAME; each slot here just writes world.time
slot 10 {
item { material: "CLOCK", name: "<yellow>ѕᴜɴʀɪѕᴇ" }
on_click { set player.world.time to 0 }
}
slot 11 {
item { material: "CLOCK", name: "<yellow>ᴅᴀʏ" }
on_click { set player.world.time to 1000 }
}
slot 12 {
item { material: "CLOCK", name: "<yellow>ɴᴏᴏɴ" }
on_click { set player.world.time to 6000 }
}
slot 13 {
item { material: "CLOCK", name: "<yellow>ᴀꜰᴛᴇʀɴᴏᴏɴ" }
on_click { set player.world.time to 9000 }
}
slot 14 {
item { material: "CLOCK", name: "<yellow>ꜱᴜɴꜱᴇᴛ" }
on_click { set player.world.time to 12000 }
}
slot 15 {
item { material: "CLOCK", name: "<yellow>ɴɪɢʜᴛ" }
on_click { set player.world.time to 13000 }
}
slot 16 {
item { material: "CLOCK", name: "<yellow>ᴍɪᴅɴɪɢʜᴛ" }
on_click { set player.world.time to 18000 }
}
slot 22 {
item { material: "ARROW", name: "<red>ʙᴀᴄᴋ" }
on_click {
go back for player
}
}
}Why it maps this way
The .sk builds clocks here and, 60 lines later, matches them back by display name to run console time set N commands. Each slot here owns its click and writes world.time — renaming a button can't silently break it.
function WManagement(p: player):
set {_gui} to chest inventory with 3 rows named "&bᴡᴇᴀᴛʜᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
loop 27 times:
set slot (loop-number - 1) of {_gui} to gray stained glass pane named " "
set slot 12 of {_gui} to sunflower named "&eꜱᴜɴɴʏ"
set slot 13 of {_gui} to water bucket named "&eʀᴀɪɴʏ"
set slot 14 of {_gui} to breeze rod named "&eꜱᴛᴏʀᴍʏ"
set slot 22 of {_gui} to arrow named "&cʙᴀᴄᴋ"
open {_gui} to {_p}gui "weather_management" {
rows: 3
title: "<aqua>ᴡᴇᴀᴛʜᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ"
fill: item("GRAY_STAINED_GLASS_PANE", name: " ")
slot 12 {
item { material: "SUNFLOWER", name: "<yellow>ꜱᴜɴɴʏ" }
on_click {
set player.world.weather to "clear"
tell(player, "<light_purple>Weather set to clear.")
}
}
slot 13 {
item { material: "WATER_BUCKET", name: "<yellow>ʀᴀɪɴʏ" }
on_click {
set player.world.weather to "rain"
tell(player, "<light_purple>Weather set to rain.")
}
}
slot 14 {
item { material: "BREEZE_ROD", name: "<yellow>ꜱᴛᴏʀᴍʏ" }
on_click {
set player.world.weather to "thunder"
tell(player, "<light_purple>Weather set to thunder.")
}
}
slot 22 {
item { material: "ARROW", name: "<red>ʙᴀᴄᴋ" }
on_click {
go back for player
}
}
}Why it maps this way
The .sk's three weather items map straight onto the weather enum: each button writes player.world.weather and the sky changes for the opener. set player.world.weather to ... is the same chained property write the time menu uses for world.time.
command /adminmenu:
aliases: /amenu, /adminpanel, /apanel, /panel, /ap, am
permission: {@menuPermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
Main(player)command "adminmenu",
command "amenu",
command "adminpanel",
command "apanel",
command "panel",
command "ap",
command "am" {
permission: "admin.menu"
description: "Open the admin panel"
execute {
open gui "admin_panel" to sender
}
}Why it maps this way
aliases: → alias-group declarations sharing one body.
on inventory click:
if name of event-inventory is "&bᴀᴅᴍɪɴ ᴘᴀɴᴇʟ":
cancel event
if event-item is player head:
PManagement(player)
if event-item is clock:
DManagment(player)
if event-item is sunflower:
WManagement(player)
if name of event-inventory is "&bᴘʟᴀʏᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ":
cancel event
if event-item is player head:
set {_target} to uncolored name of event-item
set {_gui} to chest inventory with 3 rows named "&b%{_target}%"
loop 27 times:
set slot (loop-number - 1) of {_gui} to gray stained glass pane named " "
set slot 12 of {_gui} to iron sword named "&cᴋɪᴄᴋ"
set slot 13 of {_gui} to clock named "&cᴛᴇᴍᴘ-ʙᴀɴ"
set slot 14 of {_gui} to barrier named "&cʙᴀɴ"
set slot 11 of {_gui} to glass bottle named "&cᴍᴜᴛᴇ"
set slot 15 of {_gui} to packed ice named "&cꜰʀᴇᴇᴢᴇ"
set slot 22 of {_gui} to arrow named "&cʙᴀᴄᴋ"
open {_gui} to player
if event-item is arrow:
Main(player)
if name of event-inventory is "&bᴛɪᴍᴇ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ":
cancel event
if event-item is clock:
set {_time} to name of event-item
if {_time} is "&eѕᴜɴʀɪѕᴇ":
execute console command "time set 0"
if {_time} is "&eᴅᴀʏ":
execute console command "time set 1000"
if {_time} is "&eɴᴏᴏɴ":
execute console command "time set 6000"
if {_time} is "&eᴀꜰᴛᴇʀɴᴏᴏɴ":
execute console command "time set 9000"
if {_time} is "&eꜱᴜɴꜱᴇᴛ":
execute console command "time set 12000"
if {_time} is "&eɴɪɢʜᴛ":
execute console command "time set 13000"
if {_time} is "&eᴍɪᴅɴɪɢʜᴛ":
execute console command "time set 18000"
if event-item is arrow:
Main(player)
if name of event-inventory is "&bᴡᴇᴀᴛʜᴇʀ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ":
cancel event
if event-item is sunflower:
set the weather to sun
if event-item is water bucket:
set the weather to rain
if event-item is breeze rod:
set the weather to thunder
if event-item is arrow:
Main(player)
else:
loop all players:
if name of event-inventory is "&b%loop-value%":
cancel event
if event-item is iron sword:
kick loop-value
if event-item is clock:
ban loop-value's ip for 12 hours
if event-item is barrier:
ban loop-value's ip
if event-item is glass bottle:
execute console command "mute %loop-value%"
if event-item is packed ice:
execute player command "freeze %loop-value%"
if event-item is arrow:
PManagement(player)// The .sk's 70-line 'on inventory click' router — matching inventories by
// display name, finding the target player back by parsing the TITLE — is
// the on_click handlers inside the gui blocks above. The one screen it
// builds inline (per-player actions) is a gui like any other; the target
// rides along as state instead of being encoded in the window title.
gui "player_actions" {
rows: 3
title: "<aqua>${state.target.name}"
fill: item("GRAY_STAINED_GLASS_PANE", name: " ")
slot 12 {
item { material: "IRON_SWORD", name: "<red>ᴋɪᴄᴋ" }
on_click {
kick_player(player, state.target)
}
}
slot 13 {
item { material: "CLOCK", name: "<red>ᴛᴇᴍᴘ-ʙᴀɴ" }
on_click {
tell(player, "<red>There is no ban registry yet — see the pair note.")
}
}
slot 14 {
item { material: "BARRIER", name: "<red>ʙᴀɴ" }
on_click {
tell(player, "<red>There is no ban registry yet — see the pair note.")
}
}
slot 11 {
item { material: "GLASS_BOTTLE", name: "<red>ᴍᴜᴛᴇ" }
on_click {
toggle_mute(player, state.target)
}
}
slot 15 {
item { material: "PACKED_ICE", name: "<red>ꜰʀᴇᴇᴢᴇ" }
on_click {
toggle_freeze(player, state.target)
}
}
slot 22 {
item { material: "ARROW", name: "<red>ʙᴀᴄᴋ" }
on_click {
go back for player
}
}
}
// no kick statement yet — the raw packet escape hatch closes the connection
function kick_player(actor: Player, target: Player) {
send packet "DisconnectPacket" { message: "${prefix} kicked" } to target
send "${prefix} <light_purple>Kicked ${target.name}." to actor
}Why it maps this way
Eighty lines of matching inventories by title — including finding the target player by looping everyone online and comparing the window name — collapse into the on_click handlers already declared per slot, plus one gui the .sk built inline: the per-player actions screen, whose target rides in state instead of the title bar. Kick closes the connection through the raw DisconnectPacket. Temp-ban and ban each want a persistent ban registry, so those two buttons carry a placeholder message.
command freeze <player> [<text>]:
permission: {@freezePermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
if {Freezed::%uuid of arg-1%} is true:
set {Freezed::%uuid of arg-1%} to false
send "{@Prefix} {@unfrozenMessage}" to arg-1
send "{@Prefix} &dYou have unfrozen %arg-1%!" to player
else:
set {Freezed::%uuid of arg-1%} to true
if arg-2 is set:
send "{@Prefix} &dYou have been frozen by %player% for %arg-2%!" to arg-1
send "{@Prefix} &dYou have frozen %arg-1%!" to player
else:
send "{@Prefix} &dYou have been frozen by %player%!" to arg-1
send "{@Prefix} &dYou have frozen %arg-1%!" to playerpersistent frozen for Player: Boolean = false
persistent freeze_x for Player: Double = 0.0
persistent freeze_y for Player: Double = 0.0
persistent freeze_z for Player: Double = 0.0
function toggle_freeze(actor: Player, target: Player) {
if frozen for target {
set frozen for target to false
send "${prefix} <light_purple>You have been unfrozen!" to target
send "${prefix} <light_purple>You have unfrozen ${target.name}!" to actor
} else {
set frozen for target to true
set freeze_x for target to target.location.x
set freeze_y for target to target.location.y
set freeze_z for target to target.location.z
send "${prefix} <light_purple>You have been frozen by ${actor.name}!" to target
send "${prefix} <light_purple>You have frozen ${target.name}!" to actor
}
}
command "freeze" {
permission: "freeze.use"
arguments {
target: Player
reason: optional<String>
}
execute {
if args.reason exists {
if not (frozen for args.target) {
send "${prefix} <light_purple>Reason: ${args.reason}" to args.target
}
}
toggle_freeze(sender, args.target)
}
}Why it maps this way
{Freezed::%uuid of arg-1%} → frozen for Player, plus three Doubles for the freeze spot (Locations aren't persistable — scalars only). All of it survives restarts, which the .sk's did too — but its freeze location was never stored at all.
on player move:
if {Freezed::%uuid of player%} is true:
cancel event
send action bar "{@Prefix} &dYou're frozen, there for you cannot move!"
else:
stop// 'on player move: cancel event' — the port corrects instead of cancels:
// a boot-time schedule snaps frozen players back to where they were frozen
// (their location survives restarts too)
every 2 ticks {
loop all players as p {
if frozen for p {
teleport p to location(freeze_x for p, freeze_y for p, freeze_z for p)
actionbar "${prefix} <light_purple>You're frozen, therefore you cannot move!" to p
}
}
}Why it maps this way
The .sk cancels every move event; the port corrects instead of cancels — a boot-time every 2 ticks schedule snaps frozen players back to their stored spot. One loop covers everyone, and because the spot is persistent it survives restarts.
on command:
if {Freezed::%uuid of player%} is true:
cancel event
send action bar "{@Prefix} &dCommands can not be executed while you're frozen!"// 'on command: cancel event' while frozen — PlayerCommand fires before a
// command dispatches and is cancellable, so the freeze veto is a direct port
event PlayerCommand {
execute {
if frozen for event.player {
cancel event
actionbar "${prefix} <light_purple>Commands can not be executed while you're frozen!" to event.player
}
}
}Why it maps this way
PlayerCommand fires before a typed command dispatches; it is cancellable and its command field is even writable (a handler can rewrite what runs). Here the freeze check just cancels — a one-to-one port of the .sk's on command: cancel event, keyed off the same persistent frozen flag.
command mute <player>:
permission: {@mutePermission}
permission message: {@PermissionMessage}
prefix: {@CommandPrefix}
trigger:
if {Muted::%uuid of player%} is true:
set {Muted::%uuid of player%} to false
send "{@Prefix} &dYou have been unmuted!" to arg-1
send "{@Prefix} &dYou have unmuted %arg-1%!" to player
else:
set {Muted::%uuid of player%} to true
send "{@Prefix} &dYou have been muted!" to arg-1
send "{@Prefix} &dYou have muted %arg-1%!" to playerpersistent muted for Player: Boolean = false
function toggle_mute(actor: Player, target: Player) {
if muted for target {
set muted for target to false
send "${prefix} <light_purple>You have been unmuted!" to target
send "${prefix} <light_purple>You have unmuted ${target.name}!" to actor
} else {
set muted for target to true
send "${prefix} <light_purple>You have been muted!" to target
send "${prefix} <light_purple>You have muted ${target.name}!" to actor
}
}
command "mute" {
permission: "mute.use"
arguments {
target: Player
}
execute {
// the .sk reads {Muted::%uuid of player%} — the SENDER's flag — in
// its toggle check; keying off the target is what it meant
toggle_mute(sender, args.target)
}
}Why it maps this way
The .sk toggles {Muted::%uuid of player%} — the sender's flag, so muting anyone mutes yourself. The port keys off the target, which is what it meant.
on chat:
if {Muted::%uuid of player%} is true:
cancel event
send action bar "{@Prefix} &dYou cannot talk while being muted!" to playerevent PlayerChat {
execute {
if muted for event.player {
cancel event
actionbar "${prefix} <light_purple>You cannot talk while being muted!" to event.player
}
}
}Why it maps this way
send action bar → the actionbar statement; the cancel is legal because PlayerChat is cancellable.