Skip to content

Architecture

Pavel Erokhin edited this page Oct 24, 2026 · 9 revisions

Architecture

How a question flows

  1. A player asks. The plugin opens a stream to the backend, POST /api/v2/chat, and shows the live status.
  2. When the model needs something from the game, the backend sends a tool call.
  3. The plugin runs the tool on the server thread and posts the result back.
  4. The answer streams into the chat, with buttons.

The protocol is docs/protocol-v2.md in Allay Server. Change both sides together. When a backend has no v2, the plugin falls back to v1.

Six modules

  • expansion is the expansion API other plugins and mods build against, chat.allay:allay-expansion on Maven Central: plain Java with no dependencies and a version of its own. Every Allay jar carries its classes as they are, never relocated. See Expansions.
  • core holds everything that doesn't touch a game platform: the whole flow of a question (Handler, the live status, buttons in answers), the commands (Commands), chat mode, languages, billing, memory, privacy and consent, quotas, the automation agents' wakes (Agents), the tools that need no world (get_allay_guide, allay_settings, the agents' own), the backend client, settings, permissions and their defaults, items as the model reads them, the game's formulas, players' state on the backend (PlayerData) and the sealed files, goals, and the chest and inventory planners. It never imports org.bukkit or net.minecraft. Here too are the catalog of things to find (Catalog, GameNames with the game's names in every language, and CatalogCache, which keeps a platform's catalog until its recipes, expansions, or names change), the server's commands as search_commands shows them (CommandCatalog: each platform builds it from its command map or tree, and the core searches it, hides what the asker can't run, and checks /askx lines) and the expansions' registry (Expansions), which checks each expansion once and offers its tools under the same rules as Allay's own.
  • spigot is the adapter for Spigot, Paper, and their forks. Its entry point Zygote builds the core's Allay from two pieces: BukkitPlatform, which wraps senders, chat lines, the scheduler, and sounds, and SpigotGame, the tools that read the world, goals' boss bars, deaths, marks, drafts, automation's work, and the chest windows. It then hands Bukkit's commands and events to the core.
  • modcore is what both mods share: the game without a loader, built against NeoForm's Minecraft (the game's own names since 26.1). Mod builds the core's Allay for each server the game starts, a dedicated one or the one under a singleplayer world, from ModPlatform and ModGame, and drops it when that server stops. Its tools, windows, and automation mirror spigot file by file, with the same names, arguments, and results. The game's events reach Allay through Hooks: the loader's events where there are some, narrow mixins where there are none, the same on both loaders. What only the asker may see, like marks and drafts, goes to their client alone as packets. The integrations sit here too, each asked only when its mod is loaded. The game tests live here as well (src/gametest), and both mods run them.
  • fabric and neoforge are the mods, thin adapters around modcore. Each sets a Loader (OnFabric, OnNeoForge): the mod list, the config folder, Allay's version, the loader's own ingredients, and its permissions API. Its AllayMod hands the loader's events to Mod and Hooks, and its client part answers on servers without Allay. Fabric adds Plan, which has no NeoForge build.

Adding a platform

A new mod loader is one more adapter next to fabric and neoforge, built from the same version. It brings:

  1. A Loader for modcore: its id (the backend's loader), its name for get_server, Allay's version, the config/ folder, whether a mod is loaded and which mods a player installed, whether an ingredient is the loader's own kind, its permissions API, and which mod's jar holds a class (owner), so search_commands can say whose a command is: Brigadier keeps no owner.
  2. Its events: server start, stop, and tick; commands; join and quit; chat before anyone else sees it; a broken block after the break, a respawn, a change of dimension, and a left click on a block, each to Mod or Hooks as fabric/…/AllayMod.kt does. modcore's mixins catch the rest.
  3. Its client: /ask, /askall, /askx, and /allay registered on the client, answered through Elsewhere while the server's command tree has none of them, and never in singleplayer.
  4. Its game tests: the shared ones with the loader's GameTest annotation, plus a singleplayer check.

A license made in a player's own game becomes Solo (Allay Server 6.12): it answers one player.

Rules the code keeps

  • Spigot API only, so one jar runs on Spigot and Paper. The exception is windows (the consent notice and the age question): Paper has its own dialog API instead of Spigot's, so spigot/src/paper/ draws them there, compiled against paper-api and loaded only on Paper.
  • Threads. Bukkit calls happen on the server thread; network calls never do.
  • Privacy. Tools see only the asker and the world around them. Other players reach the model only as the asker's locator bar shows them.
  • Operator tools are checked three times: the registry offers them only to operators, the backend client refuses them for anyone else, and the tool checks isOp() once more. Automation agents never get them.
  • Chest changes are planned in memory, checked, and written in one tick, or nothing moves.
  • Windows only show pictures: every click, drag, and hopper move on them is cancelled.
  • Secrets. The BYOK key and the license never reach the log or the chat.

The full map of files and every rule live in CLAUDE.md in the repository.

Next: Releasing

Clone this wiki locally