What the game loads
phantom.aj.contentHow assets load
Replacing the game's content.
Scripts and plugins
A script has all of it, as phantom.aj.content, and so does a plugin, from ctx.phantom(): the SDK runs in the plugin's own frame. Each call is marked with what it needs: game:read for what the area knows and hears, which "game": "read" in the manifest grants, and game:act for doing things in it as the player, which needs "game": "act" (see Permissions).
Overview
| Member | What it does | Plugins |
|---|---|---|
all() | Every replacement in force for this game (owner names it), held bytes with a null match among them. | game:read |
isAvailable() | Whether the host gave a content origin, without which nothing can be replaced. | game:read |
load() | Loads bytes into the client as one of its own assets (a SWF, a picture), in its own application domain, and resolves once Flash has, with the display object made: calling the result unloads it. | game:act |
replace() | Serves source for every request matching match, in place of Animal Jam's own, and resolves with the replacement: calling it puts the original back. | game:act |
replaceOnScreen() | Draws a picture over one of the client's display objects, in its place and at its size, with the object hidden, until the handle is called. | game:act |
restore() | Puts Animal Jam's own back for a match this game replaced, written as it was replaced, and resolves with whether anything was being served for it. | game:act |
onRequest() | Hears each asset the game asks for once it is answered, replaced or not: watch, do the thing that loads what you want, and its path is heard. | game:read |
Methods
all
all() => Promise<ContentReplacementInfo[]>Every replacement in force for this game (owner names it), held bytes with a null match among them. Only this game's own: one origin serves every game, but nothing another game holds is ever handed back here. The bytes themselves are never handed back either.
Returns
Promise<ContentReplacementInfo[]>
Types
isAvailable
isAvailable() => booleanWhether the host gave a content origin, without which nothing can be replaced.
Returns
boolean
load
load(source: LoadSource, options?: LoadOptions) => Promise<LoadedContent>Loads bytes into the client as one of its own assets (a SWF, a picture), in its own application domain, and resolves once Flash has, with the display object made: calling the result unloads it. A base64 data: URL is decoded in the game; { address } is loaded from an address the client's network reaches; bytes and files are held by Phantom and loaded from its address. REFUSED when Flash cannot read or will not take them; 20 seconds by default.
Parameters
| Name | Type | |
|---|---|---|
source | LoadSource | |
options optional | LoadOptions |
Returns
Promise<LoadedContent>
Types
LoadSource · LoadOptions · LoadedContent · TypedFlash · FlashRead · FlashWrite · HoldOptions · FlashArgumentLike · FlashPlace · FlashValue · FlashArgument · FlashData · AnyFlash · FlashMethod · FlashConstructor
Example
const badge = await phantom.aj.content.load('/home/me/badge.swf');
console.log(await badge.content.width.$get());
await badge();replace
replace(match: ContentMatchLike, source: ContentSource, options?: ActOptions) => Promise<ContentReplacement>Serves source for every request matching match, in place of Animal Jam's own, and resolves with the replacement: calling it puts the original back. The same match again replaces what it served. Only what loads after changes: what is drawn already is replaceOnScreen's.
Parameters
| Name | Type | |
|---|---|---|
match | ContentMatchLike | |
source | ContentSource | |
options optional | ActOptions |
Returns
Promise<ContentReplacement>
Types
ContentMatchLike · ContentSource · ActOptions · ContentReplacement · ContentMechanism · ContentReplacementInfo
Example
const art = await phantom.aj.content.replace(
{ kind: 'prefix', value: '/content/1829/sound/' },
{ file: 'C:/sounds/silence.mp3', contentType: 'audio/mpeg' },
);
console.log('in force by', art.how.join(' and '));replaceOnScreen
replaceOnScreen(place: FlashPlaceLike, source: ContentSource, options?: ContentSwapOptions) => Promise<ContentSwap>Draws a picture over one of the client's display objects, in its place and at its size, with the object hidden, until the handle is called. Nothing reloads, so this changes what is on screen now. A data: URL is drawn as it is; other bytes are held by Phantom.
Parameters
| Name | Type | |
|---|---|---|
place | FlashPlaceLike | |
source | ContentSource | |
options optional | ContentSwapOptions |
Returns
Promise<ContentSwap>
Types
FlashPlaceLike · ContentSwapOptions · ContentSwap · ContentSource
Example
const hud = phantom.flash.use('gui.GuiManager').mainHud;
const swap = await phantom.aj.content.replaceOnScreen(hud.$at('money'), '/home/me/purse.png');restore
restore(match: ContentMatchLike, options?: ActOptions) => Promise<boolean>Puts Animal Jam's own back for a match this game replaced, written as it was replaced, and resolves with whether anything was being served for it. What is drawn stays until it loads again.
Parameters
| Name | Type | |
|---|---|---|
match | ContentMatchLike | |
options optional | ActOptions |
Returns
Promise<boolean>
Types
Events
onRequest
onRequest(listener: (request: ContentRequest) => void) => () => voidHears each asset the game asks for once it is answered, replaced or not: watch, do the thing that loads what you want, and its path is heard. An asset the client was pointed at from inside is heard under Phantom's address.
Parameters
| Name | Type | |
|---|---|---|
listener | (request: ContentRequest) => void |
Returns
() => void. Call it to stop listening.
Types
Types
The types the calls above take and give, with every field. Import any of them in a plugin from @phantomclient/plugin/sdk, or use them as they are in a script.
ContentMatchLike
A match as it may be written: plain text is the exact path.
type ContentMatchLike = ContentMatch | stringContentSource
Where bytes come from: the bytes themselves; a data: URL; or a file on this computer (plain text that is not a data: URL), read by Phantom itself, typed by its name.
type ContentSource = | Uint8Array
| ArrayBuffer
| string
| { bytes: Uint8Array | ArrayBuffer; contentType?: string }
| { dataUrl: string; contentType?: string }
| { file: string; contentType?: string }ActOptions
How long something that acts may take, and what may end it first.
| Field | Type | |
|---|---|---|
timeoutMs optional | number | undefined | 10 seconds by default. |
signal optional | AbortSignal | undefined |
ContentReplacement
A replacement in force. Calling it puts Animal Jam's own back, as restore does.
type ContentReplacement = (() => Promise<void>) &
ContentReplacementInfo & {
/** The mechanisms in force, the client's own first when it could be used. */
readonly how: readonly ContentMechanism[];
}ContentMechanism
How a replacement is in force: client, a hook on the client's address builder (LoaderCache.fetchCDNURL) that hands back Phantom's address, so Animal Jam's is never asked for; proxy, the content origin answering the request itself, which catches what loads before the hook and what does not pass through the builder.
type ContentMechanism = 'client' | 'proxy'ContentReplacementInfo
Bytes Phantom holds for a game: what they answer, as what, and where they are served.
| Field | Type | |
|---|---|---|
id | string | Phantom's own name for these bytes. |
match | ContentMatch | null | Which requests they answer; null for bytes held only to be loaded by address. |
contentType | string | |
bytes | number | How many bytes are served. The bytes themselves are never handed back. |
owner | string | Which game they belong to: one origin serves every game open. |
url | string | Where Phantom serves them. |
ContentRequest
One request the game made for an asset, as it was answered.
| Field | Type | |
|---|---|---|
path | string | The path on Phantom's content origin, such as /content/1829/assets/room.swf. |
status | number | |
bytes | number | |
replaced | boolean | Whether Phantom answered it with a replacement. |
milliseconds | number |
FlashPlaceLike
Somewhere in Flash, however a script holds it: a typed place (avatars.playerAvatarWorldView), an object read before (held by handle), or an untyped reference.
type FlashPlaceLike = { readonly $reference: FlashReference } | FlashReference | FlashHandleContentSwapOptions
type ContentSwapOptions = ActOptions & {
x?: number;
y?: number;
width?: number;
height?: number;
alpha?: number;
/** How often the object is looked up again, in frames, for a HUD the client rebuilds. */
refreshFrames?: number;
}ContentSwap
A picture drawn over one of the client's objects. Calling it puts the object back.
type ContentSwap = (() => Promise<void>) & {
/** Where the picture came from: Phantom's address, or the `data:` URL given. */
readonly url: string;
}LoadSource
What load loads: a source, or an address the client's network reaches.
type LoadSource = ContentSource | { address: string }LoadOptions
type LoadOptions = ActOptions & {
/** Let a SWF bring its own code. Off by default. */
allowCode?: boolean;
}LoadedContent
Bytes loaded into the client as one of its own assets. Calling it unloads them.
type LoadedContent = (() => Promise<void>) & {
/**
* The display object made, to add, place or read. For a picture from another domain with no
* policy file Flash will not hand it over, and this is the loader, which draws it the same.
*/
readonly content: TypedFlash<ClientInstances['flash.display.DisplayObject']>;
/** The `flash.display.Loader` holding it. */
readonly loader: TypedFlash<ClientInstances['flash.display.Loader']>;
}TypedFlash
A place in Flash whose declared type is T.
type TypedFlash<T> = FlashPlace<T> &
FlashConstructor<T> &
(unknown extends T
? unknown
: {
readonly [Key in keyof T as Key extends `$${string}` ? never : Key]: T[Key] extends (
...args: infer Args
) => infer Result
? FlashMethod<Args, Result>
: TypedFlash<T[Key]>;
})FlashRead
What reading a place of type T resolves with.
type FlashRead<T> = unknown extends T
? FlashData
: T extends Primitive
? T
: T extends readonly (infer Item)[]
? FlashRead<Item>[]
: T extends (...args: never[]) => unknown
? TypedFlash<unknown>
: TypedFlash<T>FlashWrite
What may be given where Flash expects T.
type FlashWrite<T> = unknown extends T
? FlashArgumentLike
: T extends Primitive
? T
: T extends readonly (infer Item)[]
? FlashWrite<Item>[]
: T extends (...args: never[]) => unknown
? ((...args: FlashData[]) => void) | FlashCallback
: TypedFlash<T> | FlashHandle | FlashArgumentLikeHoldOptions
How a hold behaves.
| Field | Type | |
|---|---|---|
everyFrames optional | number | undefined | Write on every nth frame. Every frame by default, which is what a value the client resets needs. |
when optional | "enter" | "exit" | undefined | Which half of the frame to write in. 'enter' by default, before the frame is read. Use 'exit' for a member the game writes every frame of its own, such as a thing it pins to the pointer. Two objects' enter-frame listeners have no order between them, so an ordinary hold on one of those stands only on the frames the loader happens to run second, and the value flickers or never takes at all. Exit-frame work runs after every enter-frame listener, so what is held is what is drawn and what the next frame reads. |
while optional | HookWhen | undefined | Only while this holds, in the same rules as a hook's when, read between calls. |
once optional | boolean | undefined | Write once, on the first frame the condition holds, then stop. |
restore optional | boolean | undefined | Put back what was there before the first write when the hold ends. On by default. |
onFailed optional | ((error: string) => void) | undefined | Called if the game turns the hold off because a write failed. |
FlashArgumentLike
Anything the bridge carries, typed places included.
type FlashArgumentLike = FlashArgument | TypedFlash<unknown> | FlashArgumentLike[] | { [key: string]: FlashArgumentLike }FlashPlace
The operations every place has, whatever its type.
| Field | Type | |
|---|---|---|
$get | () => Promise<FlashRead<T>> | Reads the value here: plain data as it is, an object as a typed place held by handle. |
$set | (value: FlashWrite<T>) => Promise<void> | Writes the value here, once. The client may put its own back a frame later: a member it sets every frame, or on every redraw, or an object it builds again for the next room. For a change that has to stay, use $keep. |
$keep | (value: FlashWrite<T>, options?: HoldOptions | undefined) => Promise<() => Promise<void>> | Writes the value here and keeps it here, until the function it gives back is called, which puts back what was there. It is $set for a change that has to last. The game writes it itself, every frame, so a value the client puts back is overwritten before Flash draws it. The place is followed rather than pinned: nothing is lost when the object is not there yet (it is written as soon as it is) or when the client builds it again (the new one is written too). It is phantom.flash.hold on this place, and takes the same options. ts const hud = phantom.flash.use('gui.GuiManager').mainHud; const faded = await hud.alpha.$keep(0.5); |
$call | (...args: FlashArgumentLike[]) => Promise<FlashData> | Calls what is here, whatever the declarations say is at this place. A place is callable in its own right (avatars.createAvatar(id, x, y, name)), which is how to write a call the declarations describe: the arguments and the answer are both checked. This is the same call for the places they cannot describe, and it saves casting one to reach it: - a class whose instance side declares nothing, because every member of it is static (gui.EmoticonUtility), so $as and a hook's self give a place with no members; - an object the client typed as Object or *, or one reached with $at; - a member holding a function, such as a callback the client keeps in a field. It answers FlashData, since there is no declared return to read it as. ts const emotes = phantom.flash.use('gui.EmoticonUtility'); console.log(await emotes.stringForId(3)); console.log(await emotes.$at('stringForId').$call(3)); |
$listen | (event: string, listener: (event: FlashData, keep: <Kept>(value: Kept) => Kept) => void, options?: { read?: readonly (readonly string[])[] | undefined; capture?: boolean | undefined; } | undefined) => Promise<() => Promise<void>> | Listens for an event on the object here. Resolves with what stops listening. Without read, the event is its readable members, objects among them as typed places held by handle, and every one of them is released once the listener returns, except those passed to keep before then (release those with $release when done). read names the paths to read as the event is dispatched instead: the event is then { className, type, read } with the values in that order, and nothing is held. A listener that ignores the event passes read: []. capture listens on the way down. Both are FlashBridge.listen's options. ts await stage.$listen('mouseMove', (event) => console.log((event as { read: number[] }).read), { read: [['stageX'], ['stageY']], }); |
$describe | () => Promise<FlashValue> | The public members of what is here, as Flash's describeType lists them. |
$at | (...path: string[]) => AnyFlash | Further along, by names the types do not know: untyped from here on. |
$as | { <Name extends keyof ClientInstances & string>(name: Name): TypedFlash<ClientInstances[Name]>; (name: string): AnyFlash; } | The same place, read as the client's name from here on. Nothing is sent and nothing is checked in the game: it says what the object here is, so the editor completes and checks its members again after a step the declarations could not follow ($at, a hook's capture, anything the client typed as Object or *). Naming the wrong class is not caught here; the read or call that follows is what fails. ts const grid = phantom.flash .use('gui.AvatarEditor') .$at('_colorTableColor1') .$as('gui.ColorTable'); console.log(await grid._numCols.$get(), 'by', await grid._numRows.$get()); |
$release | () => Promise<void> | Releases the object this place is held by, when it is one; nothing otherwise. |
$reference | FlashReference | The untyped reference underneath. |
FlashValue
A value on the TypeScript side: plain data, undefined, or a handle to an object in Flash.
type FlashValue = | null
| undefined
| boolean
| number
| string
| FlashValue[]
| { [key: string]: FlashValue }
| FlashHandleFlashArgument
What may be passed to Flash: a value, a function Flash may call, or an object to make there.
type FlashArgument = | FlashValue
| FlashArgument[]
| { [key: string]: FlashArgument }
| ((...args: FlashValue[]) => void)
| FlashNewFlashData
Plain data read from Flash, with objects that live there as typed places.
type FlashData = | null
| undefined
| boolean
| number
| string
| FlashData[]
| { [key: string]: FlashData }
| TypedFlash<unknown>AnyFlash
A place whose class the declarations do not describe: phantom.flash.use('a.Class') for a class the client has no declaration for, $at, $as with a name, or anything read back from one.
Every member is another place of the same kind, and every place can be called, which is what reflection allows and what a place does at run time. Nothing is checked, because there is nothing to check it against: a name the game does not have fails when it is used, not here. then is left out, so awaiting a place is still the mistake it always was.
type AnyFlash = FlashPlace<unknown> &
FlashMethod<unknown[], unknown> & {
readonly [member: string]: AnyFlash;
} & { then?: undefined }FlashMethod
A method of the client, called with checked arguments.
type FlashMethod<Args extends readonly unknown[], Result> = (
...args: { [Index in keyof Args]: FlashWrite<Args[Index]> }
) => Promise<FlashRead<Result>>FlashConstructor
A class's constructor, from its declared $new.
type FlashConstructor<T> = T extends { $new(...args: infer Args): infer Instance }
? { $new: FlashMethod<Args, Instance> }
: unknown