Appearance
1. Project Overview
What is AppelTools?
AppelTools is a coordinated Paper + Velocity plugin suite for the AppelCraft Minecraft server network. It provides moderation, economy, social (friends/parties), and utility features across a multi-server network.
The suite consists of three Gradle modules that build to two deployable plugins plus one shared library:
common(AppelTools-Common.jar) — pure-Java shared library, no Bukkit/Velocity types. Defines the database, configs, types, permissions, plugin updater, chat filter, Discord embeds, and platform abstractions.paper(AppelTools-Paper.jar) — runs on each Paper 26.3 backend server. Handles gameplay commands (teleport, gamemode, speed, fly, homes, economy, freeze, notes GUI), per-server player economy (Vault), staff mode (vanish + tool kit + invsee), and a plugin-messaging listener for chat/command moderation and staff-mode sync.velocity(AppelTools-Velocity.jar) — runs on the Velocity 4.2 proxy. Cross-server moderation (ban/mute/kick, IP-targeted variants, history, undo), friend system, party system, private messaging, chat filter, staff-chat broadcasting, reports, maintenance mode, server restart scheduler, server registration, and Discord logging of all significant events.
Paper and Velocity share the same MySQL database but do not share a JVM. They synchronize via Velocity plugin-message channels (see 08-messaging.md).
┌──────────────┐ plugin messages ┌──────────────────────┐
│ Velocity │ ─────────────────▶ │ Paper (each node) │
│ Proxy │ ◀───────────────── │ │
└──────┬───────┘ └──────────┬───────────┘
│ │
└─────────────► MySQL ◄────────────────┘Feature Catalogue
Moderation (Velocity-side)
ban,tempban,unban— permanent, temporary (withDuration), and undo.ipban,tempipban,ipunban— IP-targeted variants; kick all currently connected players with that IP.kick,ipkick— single and IP-kick.mute,tempmute,unmute— chat mute.ipmute,tempipmute,ipunmute— IP-targeted mute, notifies all players with that IP.history— list all punishments for a player (perm + temp + undone).
Economy (Paper-side)
balance(aliasbal,money) — show your per-server balance.pay— pay another player; respects theirPaymentsToggledflag, 2-decimal rounding viaBigDecimal, requires positive amount.togglepay— enable/disable receiving payments.economy(aliaseco) — admin:add|remove|set|reset <target> [amount].baltop(aliasbalancetop) — paginated top 1000 balances, 10 per page, 60-second cached.- Vault hook (
VaultEconomyProvider) — nameAppelEconomy, currency$, format#,###.00, no bank support.
Teleport (Paper-side)
teleport(tp) —/tp <target>or/tp <target> <dest>.teleporthere(tphere) — pull target to you.teleportpos(tppos) —/tppos <x> <y> <z> [yaw] [pitch] [world]with~and0shortcuts.back(return) — return to your last teleport-from location.teleporttoggle(tptoggle) — block/unblock incoming teleport requests.teleportauto(tpauto) — auto-accept all incoming teleport requests.teleportask(tpask,tpa) — send a TO teleport request, clickable accept/deny.teleportahere(tpahere) — send a HERE teleport request.teleportaccept(tpaccept) — accept a pending request.teleportdeny(tpdeny) — deny a pending request.teleportacancel(tpacancel) — cancel all or a specific pending request.
Homes (Paper-side, per-server via getServerUUID())
home(homes) — list homes; teleport to one by name; admin:/home <player>:<name>.sethome— set a home, optionally for another player.delhome— send a clickable/delhomeconfirm <name>confirmation.delhomeconfirm— actually delete.renamehome— rename an existing home.
Speed & Flight (Paper-side)
speed— set walking or flying speed 1-10 depending on whether you're currently flying.walkingspeed(walkspeed) — walking speed 1-10.flyingspeed(flyspeed) — flying speed 1-10.fly(togglefly) — toggleallowFlight; turns flying on when enabling.
Gamemode (Paper-side)
gamemode(gm) — set a player's gamemode.creative(gmc),survival(gms),adventure(gma),spectator(gmsp) — direct shortcuts.
Friends (Velocity-side, cross-server)
friends(friend,f) —add,accept,deny,remove,list(10/page),requests(sent),pending(received),notifications(toggle),toggle(toggle incoming requests).
Parties (Velocity-side, cross-server)
party(p) —invite,leave,list,promote,demote,transfer,remove(aliaskick),warp,accept,disband,chat,mute,allinvite,admin join,admin list,admin disband.pchat(pc) — party chat.
Private Messaging (Velocity-side, cross-server)
message(many aliases) — private message; storeslastMessaged, respects ignore lists, applies chat filter, notifies staff of curse words, spy broadcasts.reply— reply to the last messaged player or console.ignore/unignore— manage ignored list.messagetoggle— toggle receiving private messages.
Chat (Velocity-side, cross-server)
- Per-player
ChatChannelenum:GLOBAL, PARTY, MESSAGE, STAFF. Switch withchat <channel>. rules— print the configured rules.chat-filter— enabled by default; configurable curse words and IP/URL blocking.
Reports (Velocity-side)
report—report <player> <reason>. Rejects self-report and report cooldown. Adds to the player's report list, broadcasts to staff, posts a Discord embed, firesStaffNotificationService.sendReport.reports—listandremove <index>.
Staff (Velocity-side)
staffchat(sc) —@Join <message>to all staff with chat toggled.staffchattoggle(sctoggle) — toggle persistent staff-chat channel.staffmodetoggle(smtoggle,staffmode,sm,h) — toggle staff mode.staffnotificationstoggle(sntoggle,staffnotifications) — toggle staff notification broadcast.staffspytoggle(sstoggle,staffspy) — toggle PM-spy broadcasts.stafflist(slist) — online staff grouped by server.versionlist(vlist) — online players grouped by Minecraft protocol version.broadcast(a,alert,bc) — global alert.notes(sn,snotes,staffnotes) —add|remove|listnotes; sends Discord embed to watchlist channel.
Staff Tools (Paper-side)
freeze— toggle a player'sfrozenflag. Enforcement happens inStaffFreezeListener(cancels movement, inventory, interact, place, break, command).notesgui— opens a 54-slot chest GUI of a target player's notes; requires staff mode.- Staff tool kit (while in staff mode, given by
StaffInventoryManager): vanish (FEATHER), freeze (BLAZE_ROD), invsee (CHEST), notes (PAPER), next-player (ENDER_EYE). Inventory is serialized tostaffInventorytable on entry and restored on exit. /atp debug staffmode give <DebugStaffModeItem>— admin: hand out a specific staff item.
Maintenance (Velocity-side)
maintenance(mt) — toggle proxy-wide maintenance; kicks non-bypass players, posts Discord.- While maintenance is on,
ProxyPingListeneroverrides MOTD, server-icon, version-name, and player-list-hover. Admin.MAINTENANCE_BYPASS— permission to ignore the maintenance kick.
Server Management (Velocity-side)
servers—register <name> <ip> <port>,unregister <name>,list. Registers a backend in theserverstable.
Restart (Velocity-side)
restartserver(rs) —restartserver <duration|cancel>. Schedules broadcasts at 60/30/10/1 minutes and 5/4/3/2/1 seconds, then shuts the proxy down.cancelaborts a scheduled restart.
Plugin Updater
updater list / get <plugin> / update <plugin>(under bothatpandatv).- Six providers supported: Jenkins, SpigotMC, Modrinth, Hangar, GitHub, Geyser.
- Configured in
config.ymlunder theplugins:block. updatePlugindownloads to.tmp, validates ZIP magic0x50 0x4B, deletes the old file, renames if Spigot.
Discord (JDA)
- Paper bot: loads if
Modules.Discord.ROOTis enabled and token is non-empty. Hosts the/searchslash command. - Velocity bot: builds LiteCommands-for-JDA; hosts
StaffNotificationServicefor outbound embeds and the/searchslash command. - Embeds: green logs, yellow missing-plugins, red report/error, yellow watch-list staff note, custom curse embed.
- Staff notification embeds: 📋 report, 👤 staff join/leave, ⚖️ punishment, 🔧 maintenance, 🔄 restart.
Plugin Checker
Config.Other.REQUIRED_PLUGINSlists plugins that must be present on each Paper node.- If any are missing on startup,
setWhitelisted(true)and postEmbeds.createMissingPluginsEmbedinto the Discord log channel, pingingMOD_ROLES. Retries until the bot is connected.
Whitelist
setWhitelisted(boolean)— when true,PlayerJoinListenerkicks any joining player who lacksPermissions.Command.Staff.ROOT, with"You are not whitelisted!".
Staff Inventory Persistence
- On staff-mode entry, the player's current inventory is Base64-serialized via
ItemStack.serializeItemsAsBytesand saved tostaffInventory(Server, Uuid, Inventory). - On staff-mode exit, the inventory is restored and the row deleted.
Per-server Player UUID
- On first start, each Paper node generates a
serverUuid(random) and persists it todata.yml. Used as theServerUUIDforeign key inplayerServerDataandhomes. - This is what makes per-server balance and per-server homes work.
Concurrency and Lifecycle Notes
- Paper auto-save:
ATPPlayerManager.startAutoSaveTask()runs every 5 minutes; on shutdown the bootstrap awaitssaveAllPlayers(). - Velocity auto-save:
PunishmentManager.startAutoSaveTask()runs every 5 minutes (checkAllTempPunishments); expires temp punishments and deactivates them. - Velocity announcements:
AnnouncementManager.start()runs at the configured interval. - All DB writes use
CompletableFuture.supplyAsync; theDatabase.runUpdateretries on MySQL deadlock (SQLState == "40001") with a 1-second backoff. - The chat queue (
SignedQueue/QueuedData) auto-resolves any pending future after 50 ms withSignedResult.ALLOWEDto keep the pipeline moving.