# Phantom plugin development Phantom is a desktop client for Animal Jam Classic. A plugin is a package that runs inside it and changes the game: reading what the game knows, acting as the player, drawing in the game's own screen, and reaching into the Flash client itself. This file is the whole context needed to write a correct plugin. It is written for a coding assistant. The docs for people are at https://docs.phantm.site. Every list and every figure below is read out of Phantom's own source as the docs are built: the SDK for its areas, the manifest schema for its limits, `@phantomclient/plugin/vue` for its components, and the pages themselves for the index at the foot. None of it is typed out by hand, so none of it can fall behind the code. ## The one rule that is got wrong most often A script runs beside the game and reads values as properties. A plugin runs in its own frame and has to ask Phantom for everything, so the same value is an **awaited call**. ```ts // script console.log(phantom.room.current?.name); // plugin const room = await phantom.room.current(); console.log(room?.name); ``` This matters because it fails silently. In a plugin `phantom.room.current` is a function, and `.name` exists on `Function.prototype`, so `phantom.room.current?.name` type-checks and returns the string `"current"`. Every getter in the SDK has this shape. Listeners are the exception: `onJoin` and friends are called the same way in both worlds and return their stop function straight away, not a promise. The same applies to `phantom.room.details()`, `phantom.buddies.list()`, `phantom.chat.recent()` and every other value. ## Package shape A plugin is a package whose `package.json` carries a `phantom` field. There is no separate manifest file. ```json { "name": "phantom-plugin-example", "version": "1.0.0", "private": true, "type": "module", "scripts": { "dev": "phantom-plugin dev --port 5360", "build": "phantom-plugin build", "validate": "phantom-plugin validate", "typecheck": "vue-tsc --noEmit", "test": "vitest run" }, "phantom": { "id": "author.plugin-name", "title": "Plugin Name", "description": "One sentence, at most 280 characters.", "icon": "icon.png", "author": "Author", "runs": "each-game", "requires": ["game"], "permissions": { "sdk": { "room": "read", "chat": "act" }, "packets": { "watch": ["uc"], "send": [], "modify": [] }, "flash": false, "network": ["api.example.com"], "clipboard": false, "notifications": false, "optional": ["sdk:chat:act"] }, "contributes": { "panels": [{ "id": "example", "title": "Example", "icon": "paw-print" }] } } } ``` - `id` is lower case words separated by dots or hyphens. It is also the plugin's origin, so it keeps one plugin's frames and storage apart from another's. - `runs` is `each-game` (the logic starts once for every game it is allowed on) or `single` (once). - `servers` is optional: `["private"]` makes a plugin for Phantom's private server alone (free for players, never run on an Animal Jam game), `["animal-jam"]` keeps it off the private server, and leaving it out means both. - `icon` in `contributes` is a Lucide icon name in kebab case. The top level `icon` is a file. - Limits: 10 pages, 10 panels, 6 widgets, 40 commands, 40 themes, 40 preferences. ### Permissions `packets` is a **sibling** of `sdk`, not a key inside it. Putting it inside `sdk` is a common mistake and gives `Unrecognized key: "packets"`, because the SDK areas are a fixed list and `packets` is not one of them. Traffic is its own permission and is graded by command, not by read/act. - `sdk`: an object of area to `"read"` or `"act"`. `act` implies `read`. - `packets`: `{ watch: [], send: [], modify: [] }`, each an array of commands such as `"uc"`, or `["*"]` for all. - `flash`: `true` for the whole game client. The most a plugin can ask for. - `network`: HTTPS hosts `ctx.fetch` may reach. Frames have no network of their own. - `optional`: scopes the player may untick and still install. The 51 SDK areas a plugin may ask for: player, membership, currency, achievements, newspaper, room, navigation, world, map, dens, customRooms, session, assets, content, avatars, chat, community, realtime, buddies, ecards, playerWall, trades, reports, referrals, inventory, shops, denShop, masterpieces, dailyGift, adventures, minigames, journeyBook, facts, polls, movies, artStudio, ui, gui, guiItems, hud, editor, popups, screens, settings, sound, localization, feedback, view, entities, render, input. `phantom.server` and `phantom.client` are real SDK areas you call, but they are **not** permission areas: they are covered by `packets`. `phantom.flash` and `phantom.recipes` need `"flash": true`. ### Waiting for the game `setup` would otherwise run before the game has loaded, so a plugin that touches the HUD or the item definitions finds nothing there and silently does nothing for the rest of the session. Declare what the game must have got to: ```json "requires": ["game", "hud"] ``` The names, and what the player is told the plugin is waiting for: - `game`: the game to finish loading - `room`: the player to be in a room - `language`: the game’s words to load - `hud`: the HUD to be drawn - `interface`: the game’s interface art to load - `shops`: the item definitions to arrive Each reads an SDK call, so the matching area must be in `permissions.sdk` or the manifest is refused. Phantom waits, tells the player what it is waiting for, then runs `setup`. Throw `NotReady` from `setup` for anything finer and it is run again with a backoff. `requires` cannot be used with `runs: "single"`, because there is no one game to be ready. Use `ctx.ready.wait(what, { game })` instead. ## The entry ```ts import { definePlugin, NotReady } from '@phantomclient/plugin'; import Panel from './panels/Panel.vue'; export default definePlugin({ requires: ['game'], async setup(ctx) { const phantom = ctx.phantom(); const stop = phantom.room.onJoin((room) => ctx.log('Joined', room.name)); return () => stop(); }, panels: { example: Panel }, pages: {}, widgets: {}, commands: { 'go-home': async (_input, phantom) => { await phantom.dens.joinOwn(); return { message: 'Off to your den' }; }, }, }); ``` `setup` returns what undoes it. Everything crossing a frame boundary must be plain data: functions do not cross, so a listener is registered on one side and its stop comes back. `ctx` is what a plugin is handed: its games, its storage, its preferences, and the ways it reaches out. Every member of it, with its signature, is at https://docs.phantm.site/reference/context. ## UI Components come from `@phantomclient/plugin/vue` and are Phantom's own, so a plugin matches the app in light and dark: Preferences, Avatar, Badge, Button, Callout, Checkbox, EmptyState, FieldFrame, InfoTip, Input, PageHeader, SegmentedControl, Select, Spinner, Switch, Tooltip. Composables: `usePlugin`, `usePhantom`, `useGames`, `usePreferences`, `useConfig`, `useSnags`, `useAutomation`, `useKept`, `useAutomations`, `useThemeMode`. Every one is listed with its props at https://docs.phantm.site/reference/vue/. Panels are mounted in a sandboxed frame with Phantom's theme. Tailwind classes with the `phantom-` prefix are the app's tokens (`text-phantom-text`, `bg-phantom-surface`). ## Reaching the Flash client Prefer the SDK. `phantom.flash` is the escape hatch, and it needs `"flash": true`. ```ts // What is this called, and what does it hold? const found = await phantom.flash.search('chatHist'); const held = await phantom.flash.describe('gui.ChatHistory'); // Take a method over. Options come after the handler. const stop = await phantom.flash.override( 'gui.ChatHistory.addMessage', async (call) => { const [avatarName, userName, moderated, message] = call.argList; await call.original(avatarName, userName, moderated, message); }, { onDropped: (dropped) => console.log(dropped, 'calls ran untouched') } ); ``` - `call.argList` is positional. `call.args` is a record by name and throws if destructured; `call.arg(nameOrIndex)` reads one. - `call.original(...)` runs the client's body, with the same arguments or others, or not at all. - `perFrame` defaults to 200, the most the loader allows. Calls over it run as though the override were not there. - `sequential` defaults to true, so originals reach the client in the order the calls came. - `phantom.flash.hold(place, value)` keeps a value where the client keeps putting its own back, which is what a client that rebuilds its HUD needs. - A name `phantom.flash` has not got throws rather than reading as `undefined`, and the error names the call that works: `phantom.flash.definition` is `phantom.flash.bridge.definition`, drawing is `phantom.flash.overlay`, the room's players and props `phantom.flash.entities`, the camera `phantom.flash.view`. ### Drawing over the game `phantom.ui` is Animal Jam's own look and is what to reach for first. Below it, `phantom.flash.overlay` draws `group`, `rect`, `circle`, `line`, `text`, `rich`, `image`, `marker`, and the controls `input`, `button`, `panel`, `list`, `checkbox`, `select`, `tabs`, `scroller`, `slider`. Every node is a function: calling it removes it, with everything drawn inside it. ```ts const overlay = phantom.flash.overlay; // Ask Flash how big text is rather than working it out from its length. const size = await overlay.measure('1200 gems', { font: { bold: true }, padding: 4 }); const panel = await overlay.panel({ title: 'Trade', width: 240, height: 90 }); const label = await overlay.text('Sending:', { parent: panel }); const amount = await overlay.text('1200 gems', { parent: panel, font: { bold: true } }); // Place nodes from what each takes up, rather than counting pixels. const line = await overlay.row([label, amount], { x: 8, y: 8, gap: 4 }); await panel.update({ height: line.height + 38 }); ``` - `measure(text, options)` and `node.bounds()` are the sizes; `row` and `column` are the layout. - `align: { x: 'center', y: 'middle' }` keeps a node in the middle of the stage, or of the group, panel or client object it is drawn in, through every resize. Never read the stage and do the sums. - `drag: { axis, left, right, step, live }` hands any node to the player, followed in Flash; `on('move')` is told where it was let go. - `on('hover')` and `on('wheel')` are the mouse moving and the wheel. A hover does not take the mouse from the game; every other mouse event on a node does. - `wrapWidth` wraps text over as many lines as it needs, in `measure` and in the node alike. - A composed control (`checkbox`, `select`, `tabs`, `scroller`, `slider`) hears its own value with `onChange`, not `on`, and `setValue` does not tell it. - Guides: https://docs.phantm.site/game/drawing, and https://docs.phantm.site/plugins/how-do-i for the thing you want and the call that does it. ### Read the client's own source The declarations say what a member is. Only the ActionScript says what the client does with it, and that gap is where the time goes. `bun run --cwd packages/client-types decompile` writes the whole client out, one `.as` file per class, laid out exactly like the generated declarations. Worked examples of what only the source tells you, all real: - `gui/ChatHistory.as` builds a chat line with `htmlText +=`, concatenating the speaker's name **unescaped** inside its own ``. So markup passed in the name is drawn, names are clickable because the client made them so, and writing that field yourself takes both the links and the scrollbar away. - The same field is given a `StyleSheet` for a single `a:hover` rule, and a Flash field with a stylesheet ignores `` outright. - `addMessage` draws `param3` or `param1` depending on a player setting, so dressing one of them dresses the half the player may not be looking at. - `gui/EmoticonUtility.as` keeps emotes as library linkage names built with `getDefinitionByName`. JPEXS names most parameters `param1`, `param2`, so read the source for the body and the declarations for the signature. ## Developing ```bash bun install bun run dev # serves the plugin; pick a free port ``` Turn on Developer mode in Phantom under Plugins, Developer. Each plugin needs its own port and no two may clash. `phantom-plugin validate` checks the manifest and lists the scopes a player will be asked for. `phantom-plugin build` bundles it. `phantom-plugin pack` makes the archive. ## Mistakes to avoid - Reading a plugin SDK value as a property instead of awaiting the call. - Putting `packets` inside `permissions.sdk`. - Starting work in `setup` before the game is ready, rather than declaring `requires`. - Hand-rolling settings storage and a settings UI when declared `preferences` are drawn by Phantom. - Hooking Flash for something the SDK already has. Search the SDK first. - Writing a client text field wholesale rather than changing what the client is given. - Assuming a typed array survives the bridge. It arrives as an object with numeric keys. ## Every SDK area Each one has a page with every call on it, its parameters, its types and the permission it needs. A call is awaited in a plugin and read as a property in a script. - phantom.aj.player (You and your animal), 200 calls: The player: their avatar, animals, looks, pets and belongings. https://docs.phantm.site/reference/sdk/player - phantom.aj.membership (Membership), 10 calls: Membership. https://docs.phantm.site/reference/sdk/membership - phantom.aj.currency (Gems and diamonds), 16 calls: Gems, diamonds and tickets. https://docs.phantm.site/reference/sdk/currency - phantom.aj.achievements (Achievements), 24 calls: Achievements and stats. https://docs.phantm.site/reference/sdk/achievements - phantom.aj.newspaper (The Jamaa Journal), 15 calls: The Jamaa Journal. https://docs.phantm.site/reference/sdk/newspaper - phantom.aj.room (Rooms), 63 calls: The room the player is in: its players, camera, scene, volumes, music and items. https://docs.phantm.site/reference/sdk/room - phantom.aj.navigation (Getting about), 15 calls: Walking: the room's grid, routes, walks that resolve with where the player arrived. https://docs.phantm.site/reference/sdk/navigation - phantom.aj.world (The world), 33 calls: Servers, shards, room counts and parties. https://docs.phantm.site/reference/sdk/world - phantom.aj.map (The map and the server list), 19 calls: The world map and travelling by it. https://docs.phantm.site/reference/sdk/map - phantom.aj.dens (Dens), 111 calls: Dens: visiting, rooms, items, privacy, guests. https://docs.phantm.site/reference/sdk/dens - phantom.aj.customRooms (Custom rooms), 24 calls: Building and playing custom rooms. https://docs.phantm.site/reference/sdk/customRooms - phantom.aj.session (The connection), 20 calls: The connection and login: connected, in the world, keeping active. https://docs.phantm.site/reference/sdk/session - phantom.aj.assets (How assets load), 8 calls: The client's art and media. https://docs.phantm.site/reference/sdk/assets - phantom.aj.content (What the game loads), 7 calls: Replacing the game's content. https://docs.phantm.site/reference/sdk/content - phantom.aj.avatars (Animals on screen), 59 calls: Everyone in the room, by user id: how they are drawn and how they move. https://docs.phantm.site/reference/sdk/avatars - phantom.aj.chat (Chat), 75 calls: Chat: saying things, hearing them, emotes, balloons, the chat bar. https://docs.phantm.site/reference/sdk/chat - phantom.aj.community (Phantom community), 8 calls: Phantom's community: who nearby uses Phantom. https://docs.phantm.site/reference/sdk/community - phantom.aj.realtime (Real-time channels), 33 calls: A plugin's real-time channels. https://docs.phantm.site/reference/sdk/realtime - phantom.aj.buddies (Buddies), 46 calls: The player's buddies, requests and the buddy card. https://docs.phantm.site/reference/sdk/buddies - phantom.aj.ecards (Jam-a-grams), 38 calls: Jam-a-grams. https://docs.phantm.site/reference/sdk/ecards - phantom.aj.playerWall (The player wall), 48 calls: Jammer walls: posts, likes, privacy. https://docs.phantm.site/reference/sdk/playerWall - phantom.aj.trades (Trades), 37 calls: Trading: trade lists, offers, the trade window. https://docs.phantm.site/reference/sdk/trades - phantom.aj.reports (Reporting a player), 9 calls: Reporting players. https://docs.phantm.site/reference/sdk/reports - phantom.aj.referrals (Refer a friend), 12 calls: Referrals. https://docs.phantm.site/reference/sdk/referrals - phantom.aj.inventory (Inventory), 32 calls: What the player owns: clothing and den items, wearing, recycling. https://docs.phantm.site/reference/sdk/inventory - phantom.aj.shops (Shops), 68 calls: Shops: catalogues, buying, selling, the shop window. https://docs.phantm.site/reference/sdk/shops - phantom.aj.denShop (The den shop), 13 calls: Players' den shops. https://docs.phantm.site/reference/sdk/denShop - phantom.aj.masterpieces (Masterpieces), 12 calls: Masterpieces: in dens, their creators, likes and previews. https://docs.phantm.site/reference/sdk/masterpieces - phantom.aj.dailyGift (The daily gift), 14 calls: The daily gift and the spin wheel. https://docs.phantm.site/reference/sdk/dailyGift - phantom.aj.adventures (Adventures), 181 calls: Adventures: lobby, actors, combat, treasure, rewards, routes. https://docs.phantm.site/reference/sdk/adventures - phantom.aj.minigames (Minigames), 1329 calls: Minigames: playing, lobbies, PvP, scores, and each of the 64 games by name. https://docs.phantm.site/reference/sdk/minigames - phantom.aj.journeyBook (The Journey Book), 23 calls: The journey book. https://docs.phantm.site/reference/sdk/journeyBook - phantom.aj.facts (Facts), 32 calls: Facts collected in the world. https://docs.phantm.site/reference/sdk/facts - phantom.aj.polls (Polls), 20 calls: Polls in the world. https://docs.phantm.site/reference/sdk/polls - phantom.aj.movies (The theatre), 49 calls: The theatre: the movie player, selector and chapters. https://docs.phantm.site/reference/sdk/movies - phantom.aj.artStudio (The art studio), 77 calls: The art studio: canvas, grid, painting pictures stroke by stroke, pottery, prints. https://docs.phantm.site/reference/sdk/artStudio - phantom.aj.ui (Building interfaces), 67 calls: The client's interface by name: restyle, attach, replace, dialogs. https://docs.phantm.site/reference/sdk/ui - phantom.aj.gui (On-screen elements), 22 calls: The client's display tree: find, click, type, wait for. https://docs.phantm.site/reference/sdk/gui - phantom.aj.guiItems (Item pictures in the interface), 4 calls: Item buttons across the client. https://docs.phantm.site/reference/sdk/guiItems - phantom.aj.hud (The HUD), 30 calls: The HUD: labels, buttons, parts, counters, the room label. https://docs.phantm.site/reference/sdk/hud - phantom.aj.editor (The avatar, den and pet editors), 67 calls: The avatar, den and pet editors. https://docs.phantm.site/reference/sdk/editor - phantom.aj.popups (Dialogs and popups), 19 calls: The client's plain dialogs. https://docs.phantm.site/reference/sdk/popups - phantom.aj.screens (The game’s own windows), 81 calls: The client's windows, all with the same calls: open, close, waitFor, press, text. https://docs.phantm.site/reference/sdk/screens - phantom.aj.settings (The game’s settings), 17 calls: The client's settings. https://docs.phantm.site/reference/sdk/settings - phantom.aj.sound (Sound and music), 16 calls: Sound and music volume. https://docs.phantm.site/reference/sdk/sound - phantom.aj.localization (Language and text), 16 calls: The client's text in the player's language. https://docs.phantm.site/reference/sdk/localization - phantom.aj.feedback (Feedback), 7 calls: The feedback form. https://docs.phantm.site/reference/sdk/feedback - phantom.aj.view (The view), 5 calls: What of the game is drawn: HUD parts, clean screenshots, zoom kept. https://docs.phantm.site/reference/sdk/view - phantom.aj.entities (What the room holds), 5 calls: The room's players, NPCs and objects as things to tag, highlight and click. https://docs.phantm.site/reference/sdk/entities - phantom.aj.render (How the game is drawn), 19 calls: How the game is drawn: quality, frame rate, the GPU. https://docs.phantm.site/reference/sdk/render - phantom.aj.input (Keyboard shortcuts), 13 calls: Keys and the mouse. https://docs.phantm.site/reference/sdk/input - phantom.flash (The game client), 115 calls: The game client itself, for what the SDK does not wrap: every public class, object, property and method in it, typed from the client's own declarations, and what runs inside Flash for scripts: hooks, watches, the overlay, highlights, keys and the view. https://docs.phantm.site/reference/sdk/flash - phantom.packets (Packets), 8 calls: The game's traffic, typed by `@phantom/protocol`. https://docs.phantm.site/reference/sdk/packets ## Every packet category The traffic under the SDK, both directions, field by field. - Signing in, 12 packets: https://docs.phantm.site/reference/packets/session - Rooms, 9 packets: https://docs.phantm.site/reference/packets/room - Avatars, 31 packets: https://docs.phantm.site/reference/packets/avatar - Chat, 24 packets: https://docs.phantm.site/reference/packets/chat - Buddies, 16 packets: https://docs.phantm.site/reference/packets/buddy - Jam-a-grams, 17 packets: https://docs.phantm.site/reference/packets/ecard - Refer a friend, 8 packets: https://docs.phantm.site/reference/packets/referral - Trades, 6 packets: https://docs.phantm.site/reference/packets/trade - Items, 11 packets: https://docs.phantm.site/reference/packets/item - Shops, 8 packets: https://docs.phantm.site/reference/packets/shop - Gems and diamonds, 8 packets: https://docs.phantm.site/reference/packets/currency - Pets, 16 packets: https://docs.phantm.site/reference/packets/pet - Adopt a Pet, 5 packets: https://docs.phantm.site/reference/packets/adopt-a-pet - Dens, 34 packets: https://docs.phantm.site/reference/packets/den - The world, 6 packets: https://docs.phantm.site/reference/packets/world - Parties, 18 packets: https://docs.phantm.site/reference/packets/party - Adventures, 101 packets: https://docs.phantm.site/reference/packets/quest - Minigames, 22 packets: https://docs.phantm.site/reference/packets/minigame - Achievements, 6 packets: https://docs.phantm.site/reference/packets/achievement - Likes, 4 packets: https://docs.phantm.site/reference/packets/resource-array - The player wall, 11 packets: https://docs.phantm.site/reference/packets/player-wall - World items, 2 packets: https://docs.phantm.site/reference/packets/world-item ## Every page of the docs - Introduction: What Phantom is and where to start in the docs. https://docs.phantm.site/introduction - Getting started: Install Phantom, sign in, add your Animal Jam accounts and open a game. https://docs.phantm.site/app - Where things are: The launcher, tabs, the sidebar's tools, the plugin dock, the canvas and Settings. https://docs.phantm.site/app/tour - Scripts: Write and run TypeScript against one game or all of them, with the whole SDK. https://docs.phantm.site/app/scripts - Playbooks: Automation built from steps, with a plan you check before it runs and a question before anything that can't be undone. https://docs.phantm.site/app/playbooks - Plans and your account: What each plan costs, what happens when one ends, and how Phantom works offline. https://docs.phantm.site/app/plans - Community: Shared rooms and real-time channels, the rules, hiding and reporting people, and what is kept. https://docs.phantm.site/app/community - Getting started: What a Phantom plugin is, and how to make one. https://docs.phantm.site/plugins/getting-started - The dev loop: Run a plugin inside Phantom while you write it, with hot reload and the manifest checked on every save. https://docs.phantm.site/plugins/dev-loop - llms.txt: A plain text summary of the plugin docs, for developers who use AI coding tools. https://docs.phantm.site/plugins/ai-context - What a plugin adds: Panels, pages, canvas widgets and commands, and the preferences and storage behind them. https://docs.phantm.site/plugins/contributions - Permissions: What each permission lets a plugin do, how it's checked, how risky it is, and how the player agrees to it. https://docs.phantm.site/plugins/permissions - Waiting for the game: Declare what your plugin needs before setup runs, wait for the rest inside it, and let Phantom tell the player what it's waiting for. https://docs.phantm.site/plugins/readiness - Building UI with Vue: Build a plugin's pages, panels, canvas widgets and command views with Vue, using Phantom's components and composables. https://docs.phantm.site/plugins/vue - Building interfaces in the game: Draw windows, buttons and popups in the game itself, in Animal Jam's own look or Phantom's, and add to the client's own screens. https://docs.phantm.site/plugins/interfaces - Themes: Add colour themes to Phantom with a plugin that's only a manifest. Phantom checks the contrast and reloads it while you tune it. https://docs.phantm.site/plugins/themes - Working with the game: Guides to the game side of the SDK: custom rooms, real-time channels, keys and the view, the art studio, and reaching the game client directly. https://docs.phantm.site/game - Recipes: changing the game: Worked examples of a plugin changing the running Animal Jam client: custom colours everyone sees, your own rows in one of the game's grids, the game's widgets, and a membership shown in your own game. https://docs.phantm.site/game/recipes - Custom rooms: Build a room from your own pictures, put the player in it, alone or with other Phantom users, and listen to what they do there. Animal Jam never sees it. https://docs.phantm.site/game/custom-rooms - Real-time channels: Let copies of your plugin on different players’ computers talk to each other live, in the same room, across everyone online, or in a group you name. https://docs.phantm.site/game/realtime - Shortcuts, moving and the view: Keyboard shortcuts that go quiet while the player types, the mouse as the room hears it, walking with the keys, walking through walls, zooming the room and hiding the interface for a clean screenshot. https://docs.phantm.site/game/shortcuts-and-view - Painting a masterpiece: Paint on Animal Jam’s art studio canvas from a plugin: pick tools and colours, draw strokes, turn a picture into a painting, and send it in through the studio’s own buttons. https://docs.phantm.site/game/painting - Hearing the player: Listen for what the player does in the game: an item pressed in any inventory or list, a button pressed anywhere, a window opening, and what it was at that moment. https://docs.phantm.site/game/events - Changing the game: Making a change in the game that takes effect and stays: set and keep on every area, $keep on any field, and an area for each minigame with its own state, values and actions. https://docs.phantm.site/game/changing-the-game - Reaching the game client: Use phantom.flash to read, call and hook any class, method or field in the running Animal Jam client, public or private. https://docs.phantm.site/game/flash - Watching and changing what the game does: Hook and override methods in the Animal Jam client to watch calls, change their arguments or answer in their place. Phantom undoes it all when the plugin stops. https://docs.phantm.site/game/hooks - Timing: Why something that worked in one room does nothing in the next, and the calls that fix it: waiting in frames, waiting for the client to build something, and keeping a listener when the client builds it again. https://docs.phantm.site/game/timing - Reading the client: Ask the running client what a class holds with phantom.flash.describe and search, and read Animal Jam's own ActionScript beside it for what the client actually does with a member. https://docs.phantm.site/game/reading-the-client - The client, as TypeScript: How Phantom mirrors Animal Jam's client as TypeScript declarations, one file per class, and how to write an ActionScript name that TypeScript can't take. https://docs.phantm.site/game/client-types - Debugging: Where a plugin's logs and errors go, and how to find out what the game is doing. https://docs.phantm.site/plugins/debugging - Publishing: Packing a plugin, what the store checks, and how review works. https://docs.phantm.site/plugins/publishing - Reference: The plugin contract, the Vue package, the CLI, the SDK and the packets, each checked against the code it describes. https://docs.phantm.site/reference - The manifest: Every field of a plugin's manifest, the `phantom` field of its package.json. https://docs.phantm.site/reference/manifest - definePlugin: The shape of a plugin's entry, and when each part runs. https://docs.phantm.site/reference/define - ctx: Everything a plugin gets: its games, storage, preferences, and network and clipboard access. https://docs.phantm.site/reference/context - The CLI: phantom-plugin runs, builds, checks, packs and publishes a plugin from the terminal. https://docs.phantm.site/reference/cli