From 2e17be1483a497291cf6028056f1202233f78dd2 Mon Sep 17 00:00:00 2001 From: Accipiter Nisus Date: Sat, 6 Sep 2025 09:31:01 +0200 Subject: [PATCH 1/3] GH-25: Added Script.reply method. Incomplete. --- devbin/devutils/docsconverter.js | 8 +- docs/documentation.md | 358 ++++++++++++++++++++++++++++--- lib/env.ts | 12 ++ lib/host.ts | 292 ++++++++++++++++++++++--- lib/types/host/index.d.ts | 258 +++++++++++++++++++--- 5 files changed, 822 insertions(+), 106 deletions(-) diff --git a/devbin/devutils/docsconverter.js b/devbin/devutils/docsconverter.js index b1aecd2..4dfcf25 100644 --- a/devbin/devutils/docsconverter.js +++ b/devbin/devutils/docsconverter.js @@ -608,10 +608,16 @@ export default class DocsConverter { * @return {string} Converted markdown. */ _convertToNamedHeaders(text, level = 0, idPrefix, headerCallback) { + let parentIds = []; return text.replace(/^(#+)\s*(.*)/gm, (match, hashes, title) => { title = title.trim(); const l = level + hashes.length; - const id = this._stringToId(title, idPrefix); + parentIds = parentIds.slice(0, l); + let id = this._stringToId(title, idPrefix); + id = l > 3 && parentIds[l - 1] + ? parentIds[l - 1] + '-' + id + : id; + parentIds[l] = id; headerCallback?.(title, hashes.length, id); return `${escapeHtml(title)}`; }); diff --git a/docs/documentation.md b/docs/documentation.md index 89c8f6a..626b955 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -11,6 +11,8 @@     [onCharEvent](#oncharevent)     [onExitUse](#onexituse)     [onCommand](#oncommand) +    [onRequest](#onrequest) +    [onResponse](#onresponse) [API references](#api-references) Mucklet scripts are written in AssemblyScript, a strictly typed @@ -62,6 +64,8 @@ commands ([Room.addCommand](#function-room-addcommand)), or scheduled posts ([Sc with delay), will be removed, and [onActivate](#onactivate) will be called again on the new script version. +

Examples

+ ```ts // Send a describe to the room and log a message to the console on activation. export function onActivate(): void { @@ -73,14 +77,22 @@ export function onActivate(): void {

onRoomEvent

_onRoomEvent_ is called when an event occurs in the room, such as a _say_, -_arrive_, or _sleep_. It requires that [Room.listen](#function-room-listen) has been called -earlier, usually in the [onActivate](#onactivate) function. +_arrive_, or _sleep_. [Room.listen](#function-room-listen) must have been called earlier to +start listening to room events, usually in the [onActivate](#onactivate) +function. + +

Parameters

+ +* `addr` _(string)_: Address of this script instance receiving the event. +* `ev` _(string)_: Event encoded as a json string. + +

Examples

```ts // Check the event type and decode the event. export function onRoomEvent( - addr: string, // Address of this script instance receiving the event. - ev: string, // Event encoded as a json string. + addr: string, + ev: string, ): void { const eventType = Event.getType(ev); if (eventType == 'say') { @@ -93,16 +105,27 @@ export function onRoomEvent(

onMessage

_onMessage_ is called when another script sends a message to this script, -using [Script.post](#function-script-post). It requires that [Script.listen](#function-script-listen) has been -called earlier, usually in the [onActivate](#onactivate) function. +using [Script.post](#function-script-post). [Script.listen](#function-script-listen) must have been called +earlier to start listening to messages, usually in the +[onActivate](#onactivate) function. + +

Parameters

+ +* `addr` _(string)_: Address of this script instance receiving the message. +* `topic` _(string)_: Topic of the message. Determined by the sender. +* `data` _(string | null)_: JSON encoded data of the message or null. + Determined by the sender. +* `sender` _(string)_: Address of the sending script instance. + +

Examples

```ts // Receive a message from another script to change room profile export function onMessage( - addr: string, // Address of this script instance receiving the message. - topic: string, // Topic of the message. Determined by the sender. - data: string | null, // JSON encoded data of the message or null. Determined by the sender. - sender: string, // Address of the sending script instance. + addr: string, + topic: string, + data: string | null, + sender: string, ): void { if (topic == "changeProfile") { Room.setProfile(JSON.parse(data)) @@ -113,17 +136,28 @@ export function onMessage(

onCharEvent

_onCharEvent_ is called when a character enters a room, leaves a room, or -changes any of its properties while inside the room. It requires that -[Room.listenCharEvent](#function-room-listencharevent) has been called earlier, usually in the -[onActivate](#onactivate) function. +changes any of its properties while inside the room. +[Room.listenCharEvent](#function-room-listencharevent) must have been called earlier to start listening +to character events, usually in the [onActivate](#onactivate) function. + +

Parameters

+ +* `addr` _(string)_: Address of this script instance receiving the event. +* `charId` _(string)_: ID of character. +* `after` _(string | null)_: Character state after the event encoded as a + json string, or null if the character left the room. +* `before` _(string | null)_: Character state before the event encoded as a + json string, or null if the character entered the room. + +

Examples

```ts // Output to log when a character arrives or leaves export function onCharEvent( - addr: string, // Address of this script instance receiving the event. - charId: string, // ID of character. - after: string | null, // Character state after the event encoded as a json string, or null if the character left the room. - before: string | null, // Character state before the event encoded as a json string, or null if the character entered the room. + addr: string, + charId: string, + after: string | null, + before: string | null, ): void { if (after == null && before != null) { // If after is null, the character left @@ -140,18 +174,26 @@ export function onCharEvent(

onExitUse

-_onExitUse_ is called when a character tries to use an exit. It requires that -[Room.listenExit](#function-room-listenexit) has been called earlier, usually in the -[onActivate](#onactivate) function. The script should call either -[ExitAction.cancel](#method-exitaction-cancel) or [ExitAction.useExit](#method-exitaction-useexit) to determine what -should happen. If neither method is called, the action will timeout after 1 -second, automatically canceling the exit use with a default message. +_onExitUse_ is called when a character tries to use an exit. +[Room.listenExit](#function-room-listenexit) must have been called earlier to start listening to +exit use, usually in the [onActivate](#onactivate) function. The script +should call either [ExitAction.cancel](#method-exitaction-cancel) or [ExitAction.useExit](#method-exitaction-useexit) to +determine what should happen. If neither method is called, the action will +timeout after 1 second, automatically canceling the exit use with a default +message. + +

Parameters

+ +* `addr` _(string)_: Address of this script instance receiving the event. +* `cmdAction` _([ExitAction](#class-exitaction))_: Exit action object. + +

Examples

```ts // Prevent anyone from using an exit export function onExitUse( - addr: string, // Address of this script instance receiving the event. - exitAction: ExitAction, // Exit action object. + addr: string, + exitAction: ExitAction, ): void { exitAction.cancel("The door seems to be locked."); } @@ -159,13 +201,20 @@ export function onExitUse(

onCommand

-_onCommand_ is called when a character uses a custom command. It requires -that [Room.addCommand](#function-room-addcommand) has been called earlier to register the command, -usually in the [onActivate](#onactivate) function. The script may send a -response to the caller using either [CmdAction.info](#method-cmdaction-info), +_onCommand_ is called when a character uses a custom command. +[Room.addCommand](#function-room-addcommand) must have been called earlier to register the +command, usually in the [onActivate](#onactivate) function. The script may +send a response to the caller using either [CmdAction.info](#method-cmdaction-info), [CmdAction.error](#method-cmdaction-error), or [CmdAction.useExit](#method-cmdaction-useexit), but it is not required. The response must be sent within 1 second from the call. +

Parameters

+ +* `addr` _(string)_: Address of this script instance receiving the action. +* `cmdAction` _([CmdAction](#class-cmdaction))_: Command action object. + +

Examples

+ ```ts // Adding a ping command on activation export function onActivate(): void { @@ -181,6 +230,64 @@ export function onCommand( } ``` +

onRequest

+ +_onRequest_ is called when another script sends a request to this script, +using [Script.request](#function-script-request). [Script.listen](#function-script-listen) must have been called +earlier to start listening to requests, usually in the +[onActivate](#onactivate) function. + +

Parameters

+ +* `addr` _(string)_: Address of this script instance receiving the request. +* `request` _([Request](#class-request))_: Request object. + +

Examples

+ +```ts +// Receive a request from another script and send a response. +export function onRequest( + addr: string, + request: Request, +): void { + if (request.topic == "getValue") { + // Parse any data passed as arguments. + const key = request.parseData() + const value = Store.getString(key) + // Send a response to the request + request.reply(value) + } +} +``` + +

onResponse

+ +_onResponse_ is called when another script sends a response to a request by +calling [Request.reply](#method-request-reply). + +

Parameters

+ +* `addr` _(string)_: Address of the script instance receiving the response. +* `response` _([Response](#class-response))_: Response object. + +

Examples

+ +```ts +// Receive a response to an request +export function onResponse( + addr: string, + response: Response, +): void { + response obn + // Parse any data passed as arguments. + const key = request.ParseData() + const value = Store.getString(key); + // Send a response to the request + request.reply(value) + } +} +``` +

API references

[Type aliases](#type-aliases) @@ -209,6 +316,15 @@ export function onCommand(     [class ExitAction](#class-exitaction)         [method useExit](#method-exitaction-useexit)         [method cancel](#method-exitaction-cancel) +    [class Request](#class-request) +        [method parseData](#method-request-parsedata) +        [method reply](#method-request-reply) +        [method error](#method-request-error) +    [class Response](#class-response) +        [method isError](#method-response-iserror) +        [method parseData](#method-response-parsedata) +        [method parseResult](#method-response-parseresult) +        [method parseContext](#method-response-parsecontext) [Namespaces](#namespaces)     [Event namespace](#namespace-event)     [Field namespace](#namespace-field) @@ -393,6 +509,7 @@ export function onCommand(     [function Script.getChar](#function-script-getchar)     [function Script.listen](#function-script-listen)     [function Script.post](#function-script-post) +    [function Script.request](#function-script-request)     [function Script.unlisten](#function-script-unlisten) [Script classes](#script-classes)     [class Script.Char](#class-script-char) @@ -766,6 +883,142 @@ shown. * `msg` (string | null): Info message to show, or default message if null. +--- + +

class Request

+ +Request is a request sent from another script. + +

class Request properties

+ +* `actionId` (i32): Action ID +* `topic` (string): Request topic +* `data` (string): Request data encoded as JSON. + + +--- + +

method Request.parseData

+ +```ts +parseData(): T +``` + +Parses the data into a value of type T. + +

Returns

+ +* (T) + + +--- + +

method Request.reply

+ +```ts +reply(result: string | null = null): void +``` + +Responds to the request with a reply containing JSON encoded result. + +

Parameters

+ +* `result` (string | null): Reply data encoded as JSON. + + +--- + +

method Request.error

+ +```ts +error(msg: string): void +``` + +Responds to the request with an error message. + +

Parameters

+ +* `msg` (string): Error message. + + +--- + +

class Response

+ +Response is a response to a request sent by the script. + +

class Response properties

+ +* `requestId` ([ID](#type-id)): Request ID +* `topic` (string): Request topic +* `data` (string): Request data encoded as JSON. +* `ctx` (ArrayBuffer | null): Request context. +* `result` (string): Result encoded as JSON. +* `error` (string | null): Error string or null on no error. + + +--- + +

method Response.isError

+ +```ts +isError(): boolean +``` + +Returns true if it is an error response by checking that the error +property is not a null value. + +

Returns

+ +* (boolean) + + +--- + +

method Response.parseData

+ +```ts +parseData(): T +``` + +Parses the data into a value of type T. + +

Returns

+ +* (T) + + +--- + +

method Response.parseResult

+ +```ts +parseResult(): T +``` + +Parses the result into a value of type T. + +

Returns

+ +* (T) + + +--- + +

method Response.parseContext

+ +```ts +parseContext(): T +``` + +Parses the context included when creating the request, into a value of +type T. + +

Returns

+ +* (T) + +

Namespaces

Event functions

@@ -3332,12 +3585,14 @@ To get character description or image info use Room.getChar instead. Script.listen(addrs: Array | null = null): void ``` -Starts listening for posted messages from any of the given `addr` -addresses. If an address is a non-instance room, it will also listen to -posted messages from any instance of that room. +Starts listening for posted messages and requests, sent with +[Script.post](#function-script-post) and [Script.request](#function-script-request), from any of the given +`addr` addresses. If an address is a non-instance room, it will also +listen to posted messages from any instance of that room. -If no `addr` is provided, the script will listen to posts from _any_ -source, including scripts and bots controlled by other players. +If no `addr` is provided, the script will listen to posts and requests +from _any_ source, including scripts and bots controlled by other +players. Posts from the current script does not require listening. A script can always post to itself. @@ -3361,7 +3616,9 @@ help roomscript Script.post(addr: string, topic: string, data: string | null = null, delay: i64 = 0): ID | null ``` -Posts a message to another script with the address `addr`. +Posts a message to another script with the address `addr`. The receiving +script will get the message through the [onMessage](#onmessage) entry +point. To get the address of a room script, use the `roomscript` command. For more info, type: @@ -3381,6 +3638,37 @@ help roomscript * ([ID](#type-id) | null): Schedule [ID](#type-id) or null if the message was posted without delay of if the receiving script was not listening. +--- + +

function Script.request

+ +```ts +Script.request(addr: string, topic: string, data: string | null, ctx: T): ID | null +``` + +Sends a request to another script with the address `addr`. The receiving +script will get the request through the [onRequest](#onrequest) entry +point. Once replied, the requesting script will get the response together +with provided context through the [onResponse](#onresponse) entry point + +To get the address of a room script, use the `roomscript` command. For +more info, type: +``` +help roomscript +``` + +

Parameters

+ +* `addr` (string): Address of target script. If addr is "#", it will be a post to the current script instance. +* `topic` (string): Message topic. May be any kind of string. +* `data` (string | null): Additional data to be sent with the request. Must be valid JSON. +* `ctx` (T): Context data returned to the caller as part of the response. Type must be serializable to JSON. + +

Returns

+ +* ([ID](#type-id) | null): Request [ID](#type-id) or null if the receiving script was not listening. + + ---

function Script.unlisten

diff --git a/lib/env.ts b/lib/env.ts index 98da82a..cd25b61 100644 --- a/lib/env.ts +++ b/lib/env.ts @@ -77,6 +77,9 @@ export declare namespace Script { @external("env", "script.getChar") export function getChar(charId: string): string | null + + @external("env", "script.request") + export function request(addr: string, topic: string, data: string | null, ctx: ArrayBuffer | null): string | null } export declare namespace Store { @@ -134,3 +137,12 @@ export declare namespace CmdAction { @external("env", "cmdAction.useExit") export function useExit(actionId: i32, exitId: string): void } + +export declare namespace RequestAction { + @external("env", "requestAction.reply") + export function reply(actionId: i32, data: string | null): void + + @external("env", "requestAction.error") + export function error(actionId: i32, msg: string): void +} + diff --git a/lib/host.ts b/lib/host.ts index be35d6b..82ec06d 100644 --- a/lib/host.ts +++ b/lib/host.ts @@ -48,6 +48,8 @@ * with delay), will be removed, and [onActivate](#onactivate) will be called * again on the new script version. * + * ### Examples + * * ```ts * // Send a describe to the room and log a message to the console on activation. * export function onActivate(): void { @@ -59,14 +61,22 @@ * ## onRoomEvent * * _onRoomEvent_ is called when an event occurs in the room, such as a _say_, - * _arrive_, or _sleep_. It requires that {@link Room.listen} has been called - * earlier, usually in the [onActivate](#onactivate) function. + * _arrive_, or _sleep_. {@link Room.listen} must have been called earlier to + * start listening to room events, usually in the [onActivate](#onactivate) + * function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the event. + * * `ev` _(string)_: Event encoded as a json string. + * + * ### Examples * * ```ts * // Check the event type and decode the event. * export function onRoomEvent( - * addr: string, // Address of this script instance receiving the event. - * ev: string, // Event encoded as a json string. + * addr: string, + * ev: string, * ): void { * const eventType = Event.getType(ev); * if (eventType == 'say') { @@ -79,16 +89,27 @@ * ## onMessage * * _onMessage_ is called when another script sends a message to this script, - * using {@link Script.post}. It requires that {@link Script.listen} has been - * called earlier, usually in the [onActivate](#onactivate) function. + * using {@link Script.post}. {@link Script.listen} must have been called + * earlier to start listening to messages, usually in the + * [onActivate](#onactivate) function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the message. + * * `topic` _(string)_: Topic of the message. Determined by the sender. + * * `data` _(string | null)_: JSON encoded data of the message or null. + * Determined by the sender. + * * `sender` _(string)_: Address of the sending script instance. + * + * ### Examples * * ```ts * // Receive a message from another script to change room profile * export function onMessage( - * addr: string, // Address of this script instance receiving the message. - * topic: string, // Topic of the message. Determined by the sender. - * data: string | null, // JSON encoded data of the message or null. Determined by the sender. - * sender: string, // Address of the sending script instance. + * addr: string, + * topic: string, + * data: string | null, + * sender: string, * ): void { * if (topic == "changeProfile") { * Room.setProfile(JSON.parse(data)) @@ -99,17 +120,28 @@ * ## onCharEvent * * _onCharEvent_ is called when a character enters a room, leaves a room, or - * changes any of its properties while inside the room. It requires that - * {@link Room.listenCharEvent} has been called earlier, usually in the - * [onActivate](#onactivate) function. + * changes any of its properties while inside the room. + * {@link Room.listenCharEvent} must have been called earlier to start listening + * to character events, usually in the [onActivate](#onactivate) function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the event. + * * `charId` _(string)_: ID of character. + * * `after` _(string | null)_: Character state after the event encoded as a + * json string, or null if the character left the room. + * * `before` _(string | null)_: Character state before the event encoded as a + * json string, or null if the character entered the room. + * + * ### Examples * * ```ts * // Output to log when a character arrives or leaves * export function onCharEvent( - * addr: string, // Address of this script instance receiving the event. - * charId: string, // ID of character. - * after: string | null, // Character state after the event encoded as a json string, or null if the character left the room. - * before: string | null, // Character state before the event encoded as a json string, or null if the character entered the room. + * addr: string, + * charId: string, + * after: string | null, + * before: string | null, * ): void { * if (after == null && before != null) { * // If after is null, the character left @@ -126,18 +158,26 @@ * * ## onExitUse * - * _onExitUse_ is called when a character tries to use an exit. It requires that - * {@link Room.listenExit} has been called earlier, usually in the - * [onActivate](#onactivate) function. The script should call either - * {@link ExitAction.cancel} or {@link ExitAction.useExit} to determine what - * should happen. If neither method is called, the action will timeout after 1 - * second, automatically canceling the exit use with a default message. + * _onExitUse_ is called when a character tries to use an exit. + * {@link Room.listenExit} must have been called earlier to start listening to + * exit use, usually in the [onActivate](#onactivate) function. The script + * should call either {@link ExitAction.cancel} or {@link ExitAction.useExit} to + * determine what should happen. If neither method is called, the action will + * timeout after 1 second, automatically canceling the exit use with a default + * message. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the event. + * * `cmdAction` _({@link ExitAction})_: Exit action object. + * + * ### Examples * * ```ts * // Prevent anyone from using an exit * export function onExitUse( - * addr: string, // Address of this script instance receiving the event. - * exitAction: ExitAction, // Exit action object. + * addr: string, + * exitAction: ExitAction, * ): void { * exitAction.cancel("The door seems to be locked."); * } @@ -145,13 +185,20 @@ * * ## onCommand * - * _onCommand_ is called when a character uses a custom command. It requires - * that {@link Room.addCommand} has been called earlier to register the command, - * usually in the [onActivate](#onactivate) function. The script may send a - * response to the caller using either {@link CmdAction.info}, + * _onCommand_ is called when a character uses a custom command. + * {@link Room.addCommand} must have been called earlier to register the + * command, usually in the [onActivate](#onactivate) function. The script may + * send a response to the caller using either {@link CmdAction.info}, * {@link CmdAction.error}, or {@link CmdAction.useExit}, but it is not * required. The response must be sent within 1 second from the call. * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the action. + * * `cmdAction` _({@link CmdAction})_: Command action object. + * + * ### Examples + * * ```ts * // Adding a ping command on activation * export function onActivate(): void { @@ -167,6 +214,64 @@ * } * ``` * + * ## onRequest + * + * _onRequest_ is called when another script sends a request to this script, + * using {@link Script.request}. {@link Script.listen} must have been called + * earlier to start listening to requests, usually in the + * [onActivate](#onactivate) function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the request. + * * `request` _({@link Request})_: Request object. + * + * ### Examples + * + * ```ts + * // Receive a request from another script and send a response. + * export function onRequest( + * addr: string, + * request: Request, + * ): void { + * if (request.topic == "getValue") { + * // Parse any data passed as arguments. + * const key = request.parseData() + * const value = Store.getString(key) + * // Send a response to the request + * request.reply(value) + * } + * } + * ``` + * + * ## onResponse + * + * _onResponse_ is called when another script sends a response to a request by + * calling {@link Request.reply}. + * + * ### Parameters + * + * * `addr` _(string)_: Address of the script instance receiving the response. + * * `response` _({@link Response})_: Response object. + * + * ### Examples + * + * ```ts + * // Receive a response to an request + * export function onResponse( + * addr: string, + * response: Response, + * ): void { + * response obn + * // Parse any data passed as arguments. + * const key = request.ParseData() + * const value = Store.getString(key); + * // Send a response to the request + * request.reply(value) + * } + * } + * ``` + * * @packageDocumentation */ @@ -178,6 +283,7 @@ import { Iterator as iterator_binding, ExitAction as exitaction_binding, CmdAction as cmdaction_binding, + RequestAction as requestaction_binding, } from "./env"; import { JSON } from 'json-as' export { JSON }; @@ -254,6 +360,7 @@ function keyToBuffer(key: T): ArrayBuffer { * It is passed to [onExitUse](#onexituse) entry point when a character tries to * use an exit that is being listen to with {@link Room.listenExit}. */ +@json export class ExitAction { /** Action ID */ actionId: i32 = 0; @@ -287,6 +394,7 @@ export class ExitAction { /** * CmdAction is a command action triggered by a character. */ +@json export class CmdAction { /** Action ID */ actionId: i32 = 0; @@ -325,6 +433,91 @@ export class CmdAction { } } +/** + * Request is a request sent from another script. + */ +@json +export class Request { + /** Action ID */ + actionId: i32 = 0; + /** Request topic */ + topic: string = ""; + /** Request data encoded as JSON. */ + data: string = ""; + + /** + * Parses the data into a value of type T. + */ + parseData(): T { + return JSON.parse(this.data); + } + + /** + * Responds to the request with a reply containing JSON encoded result. + * @param result Reply data encoded as JSON. + */ + reply(result: string | null = null): void { + requestaction_binding.reply(this.actionId, result); + } + + /** + * Responds to the request with an error message. + * @param msg Error message. + */ + error(msg: string): void { + requestaction_binding.error(this.actionId, msg); + } +} + +/** + * Response is a response to a request sent by the script. + */ +@json +export class Response { + /** Request ID */ + requestId: ID = ""; + /** Request topic */ + topic: string = ""; + /** Request data encoded as JSON. */ + data: string = ""; + /** Request context. */ + ctx: ArrayBuffer | null = null; + /** Result encoded as JSON. */ + result: string = ""; + /** Error string or null on no error. */ + error: string | null = null; + + /** + * Returns true if it is an error response by checking that the error + * property is not a null value. + */ + isError(): boolean { + return this.error != null; + } + + /** + * Parses the data into a value of type T. + */ + parseData(): T { + return JSON.parse(this.data); + } + + /** + * Parses the result into a value of type T. + */ + parseResult(): T { + return JSON.parse(this.result); + } + + /** + * Parses the context included when creating the request, into a value of + * type T. + */ + parseContext(): T { + return JSON.parse(this.data); + } +} + /** * BaseIterator is an iterator over items with an ID. */ @@ -1367,12 +1560,14 @@ export namespace Script { } /** - * Starts listening for posted messages from any of the given `addr` - * addresses. If an address is a non-instance room, it will also listen to - * posted messages from any instance of that room. + * Starts listening for posted messages and requests, sent with + * {@link Script.post} and {@link Script.request}, from any of the given + * `addr` addresses. If an address is a non-instance room, it will also + * listen to posted messages from any instance of that room. * - * If no `addr` is provided, the script will listen to posts from _any_ - * source, including scripts and bots controlled by other players. + * If no `addr` is provided, the script will listen to posts and requests + * from _any_ source, including scripts and bots controlled by other + * players. * * Posts from the current script does not require listening. A script can * always post to itself. @@ -1406,7 +1601,9 @@ export namespace Script { } /** - * Posts a message to another script with the address `addr`. + * Posts a message to another script with the address `addr`. The receiving + * script will get the message through the [onMessage](#onmessage) entry + * point. * * To get the address of a room script, use the `roomscript` command. For * more info, type: @@ -1440,6 +1637,31 @@ export namespace Script { return script_binding.cancelPost(scheduleId); } + /** + * Sends a request to another script with the address `addr`. The receiving + * script will get the request through the [onRequest](#onrequest) entry + * point. Once replied, the requesting script will get the response together + * with provided context through the [onResponse](#onresponse) entry point + * + * To get the address of a room script, use the `roomscript` command. For + * more info, type: + * ``` + * help roomscript + * ``` + * + * @param addr - Address of target script. If addr is "#", it will be a post to the current script instance. + * @param topic - Message topic. May be any kind of string. + * @param data - Additional data to be sent with the request. Must be valid JSON. + * @param ctx - Context data returned to the caller as part of the response. Type must be serializable to JSON. + * @returns Request {@link ID} or null if the receiving script was not listening. + */ + export function request(addr: string, topic: string, data: string | null, ctx: T): ID | null { + if (ctx == null) { + return script_binding.request(addr, topic, data, null); + } + return script_binding.request(addr, topic, data, String.UTF8.encode(JSON.stringify(ctx))); + } + /** * Gets info on an existing character. * diff --git a/lib/types/host/index.d.ts b/lib/types/host/index.d.ts index 21fca03..ae22a71 100644 --- a/lib/types/host/index.d.ts +++ b/lib/types/host/index.d.ts @@ -48,6 +48,8 @@ * with delay), will be removed, and [onActivate](#onactivate) will be called * again on the new script version. * + * ### Examples + * * ```ts * // Send a describe to the room and log a message to the console on activation. * export function onActivate(): void { @@ -59,14 +61,22 @@ * ## onRoomEvent * * _onRoomEvent_ is called when an event occurs in the room, such as a _say_, - * _arrive_, or _sleep_. It requires that {@link Room.listen} has been called - * earlier, usually in the [onActivate](#onactivate) function. + * _arrive_, or _sleep_. {@link Room.listen} must have been called earlier to + * start listening to room events, usually in the [onActivate](#onactivate) + * function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the event. + * * `ev` _(string)_: Event encoded as a json string. + * + * ### Examples * * ```ts * // Check the event type and decode the event. * export function onRoomEvent( - * addr: string, // Address of this script instance receiving the event. - * ev: string, // Event encoded as a json string. + * addr: string, + * ev: string, * ): void { * const eventType = Event.getType(ev); * if (eventType == 'say') { @@ -79,16 +89,27 @@ * ## onMessage * * _onMessage_ is called when another script sends a message to this script, - * using {@link Script.post}. It requires that {@link Script.listen} has been - * called earlier, usually in the [onActivate](#onactivate) function. + * using {@link Script.post}. {@link Script.listen} must have been called + * earlier to start listening to messages, usually in the + * [onActivate](#onactivate) function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the message. + * * `topic` _(string)_: Topic of the message. Determined by the sender. + * * `data` _(string | null)_: JSON encoded data of the message or null. + * Determined by the sender. + * * `sender` _(string)_: Address of the sending script instance. + * + * ### Examples * * ```ts * // Receive a message from another script to change room profile * export function onMessage( - * addr: string, // Address of this script instance receiving the message. - * topic: string, // Topic of the message. Determined by the sender. - * data: string | null, // JSON encoded data of the message or null. Determined by the sender. - * sender: string, // Address of the sending script instance. + * addr: string, + * topic: string, + * data: string | null, + * sender: string, * ): void { * if (topic == "changeProfile") { * Room.setProfile(JSON.parse(data)) @@ -99,17 +120,28 @@ * ## onCharEvent * * _onCharEvent_ is called when a character enters a room, leaves a room, or - * changes any of its properties while inside the room. It requires that - * {@link Room.listenCharEvent} has been called earlier, usually in the - * [onActivate](#onactivate) function. + * changes any of its properties while inside the room. + * {@link Room.listenCharEvent} must have been called earlier to start listening + * to character events, usually in the [onActivate](#onactivate) function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the event. + * * `charId` _(string)_: ID of character. + * * `after` _(string | null)_: Character state after the event encoded as a + * json string, or null if the character left the room. + * * `before` _(string | null)_: Character state before the event encoded as a + * json string, or null if the character entered the room. + * + * ### Examples * * ```ts * // Output to log when a character arrives or leaves * export function onCharEvent( - * addr: string, // Address of this script instance receiving the event. - * charId: string, // ID of character. - * after: string | null, // Character state after the event encoded as a json string, or null if the character left the room. - * before: string | null, // Character state before the event encoded as a json string, or null if the character entered the room. + * addr: string, + * charId: string, + * after: string | null, + * before: string | null, * ): void { * if (after == null && before != null) { * // If after is null, the character left @@ -126,18 +158,26 @@ * * ## onExitUse * - * _onExitUse_ is called when a character tries to use an exit. It requires that - * {@link Room.listenExit} has been called earlier, usually in the - * [onActivate](#onactivate) function. The script should call either - * {@link ExitAction.cancel} or {@link ExitAction.useExit} to determine what - * should happen. If neither method is called, the action will timeout after 1 - * second, automatically canceling the exit use with a default message. + * _onExitUse_ is called when a character tries to use an exit. + * {@link Room.listenExit} must have been called earlier to start listening to + * exit use, usually in the [onActivate](#onactivate) function. The script + * should call either {@link ExitAction.cancel} or {@link ExitAction.useExit} to + * determine what should happen. If neither method is called, the action will + * timeout after 1 second, automatically canceling the exit use with a default + * message. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the event. + * * `cmdAction` _({@link ExitAction})_: Exit action object. + * + * ### Examples * * ```ts * // Prevent anyone from using an exit * export function onExitUse( - * addr: string, // Address of this script instance receiving the event. - * exitAction: ExitAction, // Exit action object. + * addr: string, + * exitAction: ExitAction, * ): void { * exitAction.cancel("The door seems to be locked."); * } @@ -145,13 +185,20 @@ * * ## onCommand * - * _onCommand_ is called when a character uses a custom command. It requires - * that {@link Room.addCommand} has been called earlier to register the command, - * usually in the [onActivate](#onactivate) function. The script may send a - * response to the caller using either {@link CmdAction.info}, + * _onCommand_ is called when a character uses a custom command. + * {@link Room.addCommand} must have been called earlier to register the + * command, usually in the [onActivate](#onactivate) function. The script may + * send a response to the caller using either {@link CmdAction.info}, * {@link CmdAction.error}, or {@link CmdAction.useExit}, but it is not * required. The response must be sent within 1 second from the call. * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the action. + * * `cmdAction` _({@link CmdAction})_: Command action object. + * + * ### Examples + * * ```ts * // Adding a ping command on activation * export function onActivate(): void { @@ -167,6 +214,64 @@ * } * ``` * + * ## onRequest + * + * _onRequest_ is called when another script sends a request to this script, + * using {@link Script.request}. {@link Script.listen} must have been called + * earlier to start listening to requests, usually in the + * [onActivate](#onactivate) function. + * + * ### Parameters + * + * * `addr` _(string)_: Address of this script instance receiving the request. + * * `request` _({@link Request})_: Request object. + * + * ### Examples + * + * ```ts + * // Receive a request from another script and send a response. + * export function onRequest( + * addr: string, + * request: Request, + * ): void { + * if (request.topic == "getValue") { + * // Parse any data passed as arguments. + * const key = request.parseData() + * const value = Store.getString(key) + * // Send a response to the request + * request.reply(value) + * } + * } + * ``` + * + * ## onResponse + * + * _onResponse_ is called when another script sends a response to a request by + * calling {@link Request.reply}. + * + * ### Parameters + * + * * `addr` _(string)_: Address of the script instance receiving the response. + * * `response` _({@link Response})_: Response object. + * + * ### Examples + * + * ```ts + * // Receive a response to an request + * export function onResponse( + * addr: string, + * response: Response, + * ): void { + * response obn + * // Parse any data passed as arguments. + * const key = request.ParseData() + * const value = Store.getString(key); + * // Send a response to the request + * request.reply(value) + * } + * } + * ``` + * * @packageDocumentation */ /** ID for in game entities such as characters, rooms, and areas. */ @@ -282,6 +387,66 @@ declare class CmdAction { */ useExit(exitId: ID): void; } +/** + * Request is a request sent from another script. + */ +declare class Request { + /** Action ID */ + actionId: i32; + /** Request topic */ + topic: string; + /** Request data encoded as JSON. */ + data: string; + /** + * Parses the data into a value of type T. + */ + parseData(): T; + /** + * Responds to the request with a reply containing JSON encoded result. + * @param result Reply data encoded as JSON. + */ + reply(result?: string | null): void; + /** + * Responds to the request with an error message. + * @param msg Error message. + */ + error(msg: string): void; +} +/** + * Response is a response to a request sent by the script. + */ +declare class Response { + /** Request ID */ + requestId: ID; + /** Request topic */ + topic: string; + /** Request data encoded as JSON. */ + data: string; + /** Request context. */ + ctx: ArrayBuffer | null; + /** Result encoded as JSON. */ + result: string; + /** Error string or null on no error. */ + error: string | null; + /** + * Returns true if it is an error response by checking that the error + * property is not a null value. + */ + isError(): boolean; + /** + * Parses the data into a value of type T. + */ + parseData(): T; + /** + * Parses the result into a value of type T. + */ + parseResult(): T; + /** + * Parses the context included when creating the request, into a value of + * type T. + */ + parseContext(): T; +} /** * BaseIterator is an iterator over items with an ID. */ @@ -950,12 +1115,14 @@ declare namespace Script { rp: RPState; } /** - * Starts listening for posted messages from any of the given `addr` - * addresses. If an address is a non-instance room, it will also listen to - * posted messages from any instance of that room. + * Starts listening for posted messages and requests, sent with + * {@link Script.post} and {@link Script.request}, from any of the given + * `addr` addresses. If an address is a non-instance room, it will also + * listen to posted messages from any instance of that room. * - * If no `addr` is provided, the script will listen to posts from _any_ - * source, including scripts and bots controlled by other players. + * If no `addr` is provided, the script will listen to posts and requests + * from _any_ source, including scripts and bots controlled by other + * players. * * Posts from the current script does not require listening. A script can * always post to itself. @@ -983,7 +1150,9 @@ declare namespace Script { */ function unlisten(addrs?: string[] | null): void; /** - * Posts a message to another script with the address `addr`. + * Posts a message to another script with the address `addr`. The receiving + * script will get the message through the [onMessage](#onmessage) entry + * point. * * To get the address of a room script, use the `roomscript` command. For * more info, type: @@ -1008,6 +1177,25 @@ declare namespace Script { * @returns True if the post was successfully canceled, otherwise false. */ function cancelPost(scheduleId: ID | null): boolean; + /** + * Sends a request to another script with the address `addr`. The receiving + * script will get the request through the [onRequest](#onrequest) entry + * point. Once replied, the requesting script will get the response together + * with provided context through the [onResponse](#onresponse) entry point + * + * To get the address of a room script, use the `roomscript` command. For + * more info, type: + * ``` + * help roomscript + * ``` + * + * @param addr - Address of target script. If addr is "#", it will be a post to the current script instance. + * @param topic - Message topic. May be any kind of string. + * @param data - Additional data to be sent with the request. Must be valid JSON. + * @param ctx - Context data returned to the caller as part of the response. Type must be serializable to JSON. + * @returns Request {@link ID} or null if the receiving script was not listening. + */ + function request(addr: string, topic: string, data: string | null, ctx: T): ID | null; /** * Gets info on an existing character. * From e860a74221ebcf08db614328bf186004eda61338 Mon Sep 17 00:00:00 2001 From: Accipiter Nisus Date: Thu, 18 Sep 2025 15:31:19 +0200 Subject: [PATCH 2/3] GH-25: Synchronous Script.request. Updates json-as. --- docs/documentation.md | 49 +++------------- lib/env.ts | 6 +- lib/host.ts | 117 +++++++++++++++----------------------- lib/types/host/index.d.ts | 84 +++++++++++---------------- package-lock.json | 8 +-- package.json | 2 +- 6 files changed, 97 insertions(+), 169 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index 626b955..b7469af 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -322,9 +322,7 @@ export function onResponse(         [method error](#method-request-error)     [class Response](#class-response)         [method isError](#method-response-iserror) -        [method parseData](#method-response-parsedata)         [method parseResult](#method-response-parseresult) -        [method parseContext](#method-response-parsecontext) [Namespaces](#namespaces)     [Event namespace](#namespace-event)     [Field namespace](#namespace-field) @@ -894,6 +892,7 @@ Request is a request sent from another script. * `actionId` (i32): Action ID * `topic` (string): Request topic * `data` (string): Request data encoded as JSON. +* `sender` (string): Request sender address. --- @@ -949,11 +948,7 @@ Response is a response to a request sent by the script.

class Response properties

-* `requestId` ([ID](#type-id)): Request ID -* `topic` (string): Request topic -* `data` (string): Request data encoded as JSON. -* `ctx` (ArrayBuffer | null): Request context. -* `result` (string): Result encoded as JSON. +* `result` (string | null): Result encoded as JSON. * `error` (string | null): Error string or null on no error. @@ -973,21 +968,6 @@ property is not a null value. * (boolean) ---- - -

method Response.parseData

- -```ts -parseData(): T -``` - -Parses the data into a value of type T. - -

Returns

- -* (T) - - ---

method Response.parseResult

@@ -1003,22 +983,6 @@ Parses the result into a value of type T. * (T) ---- - -

method Response.parseContext

- -```ts -parseContext(): T -``` - -Parses the context included when creating the request, into a value of -type T. - -

Returns

- -* (T) - -

Namespaces

Event functions

@@ -2328,6 +2292,10 @@ new JSON.Obj() ``` +

class JSON.Obj properties

+ +* `storage` (Map<string, [JSON.Value](#class-json-value)>) + --- @@ -3643,7 +3611,7 @@ help roomscript

function Script.request

```ts -Script.request(addr: string, topic: string, data: string | null, ctx: T): ID | null +Script.request(addr: string, topic: string, data: string | null = null): Response ``` Sends a request to another script with the address `addr`. The receiving @@ -3662,11 +3630,10 @@ help roomscript * `addr` (string): Address of target script. If addr is "#", it will be a post to the current script instance. * `topic` (string): Message topic. May be any kind of string. * `data` (string | null): Additional data to be sent with the request. Must be valid JSON. -* `ctx` (T): Context data returned to the caller as part of the response. Type must be serializable to JSON.

Returns

-* ([ID](#type-id) | null): Request [ID](#type-id) or null if the receiving script was not listening. +* ([Response](#class-response)): Response to the request. --- diff --git a/lib/env.ts b/lib/env.ts index cd25b61..f44bda5 100644 --- a/lib/env.ts +++ b/lib/env.ts @@ -1,3 +1,4 @@ + // @ts-nocheck export declare namespace Room { @@ -79,7 +80,7 @@ export declare namespace Script { export function getChar(charId: string): string | null @external("env", "script.request") - export function request(addr: string, topic: string, data: string | null, ctx: ArrayBuffer | null): string | null + export function request(addr: string, topic: string, data: string | null): T } export declare namespace Store { @@ -140,9 +141,8 @@ export declare namespace CmdAction { export declare namespace RequestAction { @external("env", "requestAction.reply") - export function reply(actionId: i32, data: string | null): void + export function reply(actionId: i32, result: string | null): void @external("env", "requestAction.error") export function error(actionId: i32, msg: string): void } - diff --git a/lib/host.ts b/lib/host.ts index 82ec06d..abed4ce 100644 --- a/lib/host.ts +++ b/lib/host.ts @@ -354,43 +354,6 @@ function keyToBuffer(key: T): ArrayBuffer { return key; } -/** - * ExitAction is an action representing an intercepted use of an exit. - * - * It is passed to [onExitUse](#onexituse) entry point when a character tries to - * use an exit that is being listen to with {@link Room.listenExit}. - */ -@json -export class ExitAction { - /** Action ID */ - actionId: i32 = 0; - /** Character ID */ - charId: ID = ""; - /** Exit ID */ - exitId: ID = ""; - - /** - * Makes the character use an exit. If exitId is null, the character is sent - * through the exit that they originally tried to use. - * - * The exit may be hidden or inactive. - * @param exitId Exit ID or null for the originally used exit. - */ - useExit(exitId: ID | null = null): void { - exitaction_binding.useExit(this.actionId, exitId); - } - - /** - * Cancels a character's attempt to use an exit and shows them an info - * message instead. If msg is null, the default exit timeout message will be - * shown. - * @param msg Info message to show, or default message if null. - */ - cancel(msg: string | null = null): void { - exitaction_binding.cancel(this.actionId, msg); - } -} - /** * CmdAction is a command action triggered by a character. */ @@ -433,6 +396,43 @@ export class CmdAction { } } +/** + * ExitAction is an action representing an intercepted use of an exit. + * + * It is passed to [onExitUse](#onexituse) entry point when a character tries to + * use an exit that is being listen to with {@link Room.listenExit}. + */ +@json +export class ExitAction { + /** Action ID */ + actionId: i32 = 0; + /** Character ID */ + charId: ID = ""; + /** Exit ID */ + exitId: ID = ""; + + /** + * Makes the character use an exit. If exitId is null, the character is sent + * through the exit that they originally tried to use. + * + * The exit may be hidden or inactive. + * @param exitId Exit ID or null for the originally used exit. + */ + useExit(exitId: ID | null = null): void { + exitaction_binding.useExit(this.actionId, exitId); + } + + /** + * Cancels a character's attempt to use an exit and shows them an info + * message instead. If msg is null, the default exit timeout message will be + * shown. + * @param msg Info message to show, or default message if null. + */ + cancel(msg: string | null = null): void { + exitaction_binding.cancel(this.actionId, msg); + } +} + /** * Request is a request sent from another script. */ @@ -444,6 +444,8 @@ export class Request { topic: string = ""; /** Request data encoded as JSON. */ data: string = ""; + /** Request sender address. */ + sender: string = ""; /** * Parses the data into a value of type T. @@ -472,18 +474,9 @@ export class Request { /** * Response is a response to a request sent by the script. */ -@json export class Response { - /** Request ID */ - requestId: ID = ""; - /** Request topic */ - topic: string = ""; - /** Request data encoded as JSON. */ - data: string = ""; - /** Request context. */ - ctx: ArrayBuffer | null = null; /** Result encoded as JSON. */ - result: string = ""; + result: string | null = null; /** Error string or null on no error. */ error: string | null = null; @@ -495,26 +488,14 @@ export class Response { return this.error != null; } - /** - * Parses the data into a value of type T. - */ - parseData(): T { - return JSON.parse(this.data); - } - /** * Parses the result into a value of type T. */ parseResult(): T { - return JSON.parse(this.result); - } - - /** - * Parses the context included when creating the request, into a value of - * type T. - */ - parseContext(): T { - return JSON.parse(this.data); + if (this.result == null) { + abort("no result to parse") + } + return JSON.parse(this.result!); } } @@ -1652,14 +1633,10 @@ export namespace Script { * @param addr - Address of target script. If addr is "#", it will be a post to the current script instance. * @param topic - Message topic. May be any kind of string. * @param data - Additional data to be sent with the request. Must be valid JSON. - * @param ctx - Context data returned to the caller as part of the response. Type must be serializable to JSON. - * @returns Request {@link ID} or null if the receiving script was not listening. + * @returns Response to the request. */ - export function request(addr: string, topic: string, data: string | null, ctx: T): ID | null { - if (ctx == null) { - return script_binding.request(addr, topic, data, null); - } - return script_binding.request(addr, topic, data, String.UTF8.encode(JSON.stringify(ctx))); + export function request(addr: string, topic: string, data: string | null = null): Response { + return script_binding.request(addr, topic, data); } /** diff --git a/lib/types/host/index.d.ts b/lib/types/host/index.d.ts index ae22a71..56e04f2 100644 --- a/lib/types/host/index.d.ts +++ b/lib/types/host/index.d.ts @@ -327,35 +327,6 @@ declare const enum ExitIcon { In = 11, Out = 12 } -/** - * ExitAction is an action representing an intercepted use of an exit. - * - * It is passed to [onExitUse](#onexituse) entry point when a character tries to - * use an exit that is being listen to with {@link Room.listenExit}. - */ -declare class ExitAction { - /** Action ID */ - actionId: i32; - /** Character ID */ - charId: ID; - /** Exit ID */ - exitId: ID; - /** - * Makes the character use an exit. If exitId is null, the character is sent - * through the exit that they originally tried to use. - * - * The exit may be hidden or inactive. - * @param exitId Exit ID or null for the originally used exit. - */ - useExit(exitId?: ID | null): void; - /** - * Cancels a character's attempt to use an exit and shows them an info - * message instead. If msg is null, the default exit timeout message will be - * shown. - * @param msg Info message to show, or default message if null. - */ - cancel(msg?: string | null): void; -} /** * CmdAction is a command action triggered by a character. */ @@ -387,6 +358,35 @@ declare class CmdAction { */ useExit(exitId: ID): void; } +/** + * ExitAction is an action representing an intercepted use of an exit. + * + * It is passed to [onExitUse](#onexituse) entry point when a character tries to + * use an exit that is being listen to with {@link Room.listenExit}. + */ +declare class ExitAction { + /** Action ID */ + actionId: i32; + /** Character ID */ + charId: ID; + /** Exit ID */ + exitId: ID; + /** + * Makes the character use an exit. If exitId is null, the character is sent + * through the exit that they originally tried to use. + * + * The exit may be hidden or inactive. + * @param exitId Exit ID or null for the originally used exit. + */ + useExit(exitId?: ID | null): void; + /** + * Cancels a character's attempt to use an exit and shows them an info + * message instead. If msg is null, the default exit timeout message will be + * shown. + * @param msg Info message to show, or default message if null. + */ + cancel(msg?: string | null): void; +} /** * Request is a request sent from another script. */ @@ -397,6 +397,8 @@ declare class Request { topic: string; /** Request data encoded as JSON. */ data: string; + /** Request sender address. */ + sender: string; /** * Parses the data into a value of type T. */ @@ -416,16 +418,8 @@ declare class Request { * Response is a response to a request sent by the script. */ declare class Response { - /** Request ID */ - requestId: ID; - /** Request topic */ - topic: string; - /** Request data encoded as JSON. */ - data: string; - /** Request context. */ - ctx: ArrayBuffer | null; /** Result encoded as JSON. */ - result: string; + result: string | null; /** Error string or null on no error. */ error: string | null; /** @@ -433,19 +427,10 @@ declare class Response { * property is not a null value. */ isError(): boolean; - /** - * Parses the data into a value of type T. - */ - parseData(): T; /** * Parses the result into a value of type T. */ parseResult(): T; - /** - * Parses the context included when creating the request, into a value of - * type T. - */ - parseContext(): T; } /** * BaseIterator is an iterator over items with an ID. @@ -1192,10 +1177,9 @@ declare namespace Script { * @param addr - Address of target script. If addr is "#", it will be a post to the current script instance. * @param topic - Message topic. May be any kind of string. * @param data - Additional data to be sent with the request. Must be valid JSON. - * @param ctx - Context data returned to the caller as part of the response. Type must be serializable to JSON. - * @returns Request {@link ID} or null if the receiving script was not listening. + * @returns Response to the request. */ - function request(addr: string, topic: string, data: string | null, ctx: T): ID | null; + function request(addr: string, topic: string, data?: string | null): Response; /** * Gets info on an existing character. * diff --git a/package-lock.json b/package-lock.json index 5cac5d5..253fc17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "assemblyscript": "^0.28.2", "isomorphic-ws": "^5.0.0", - "json-as": "github:anisus/json-as#863f37b8c6666e79c219f9e36b01efb5a49a9a19", + "json-as": "1.1.21", "resclient": "^2.5.0", "tinyargs": "^0.1.4", "visitor-as": "^0.11.4" @@ -1085,9 +1085,9 @@ } }, "node_modules/json-as": { - "version": "1.1.17", - "resolved": "git+ssh://git@github.com/anisus/json-as.git#863f37b8c6666e79c219f9e36b01efb5a49a9a19", - "integrity": "sha512-pIdamF7Vsr0sSPwIJqyy0EKtfab+1hwtVynO4jFqQf1Mt6Y1U9w8vxocGODyfJk8wThQDOWNlh3xkU/RlVlxKg==" + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/json-as/-/json-as-1.1.21.tgz", + "integrity": "sha512-4WlxVqLY6sZHqGyFiKE+ulmbnxhiBlOPXb2KELjNpj1LXh0ZYhaPJq7Ml2Jnt2E5Zmn6AXPmyerdkR0w9GbXRg==" }, "node_modules/json-buffer": { "version": "3.0.1", diff --git a/package.json b/package.json index d2d69ba..c55448b 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "dependencies": { "assemblyscript": "^0.28.2", "isomorphic-ws": "^5.0.0", - "json-as": "github:anisus/json-as#863f37b8c6666e79c219f9e36b01efb5a49a9a19", + "json-as": "1.1.21", "resclient": "^2.5.0", "tinyargs": "^0.1.4", "visitor-as": "^0.11.4" From 6dd2a58e547a199fb81ed0bfcea4e62f3293ca5c Mon Sep 17 00:00:00 2001 From: Accipiter Nisus Date: Fri, 19 Sep 2025 11:41:56 +0200 Subject: [PATCH 3/3] GH-25: Added vip_guard.ts and vip_list.ts examples. --- README.md | 4 +- docs/documentation.md | 8 ++- examples/intercom_inside.ts | 2 +- examples/vip_guard.ts | 47 +++++++++++++ examples/vip_list.ts | 130 ++++++++++++++++++++++++++++++++++++ lib/host.ts | 8 ++- lib/types/host/index.d.ts | 8 ++- 7 files changed, 196 insertions(+), 11 deletions(-) create mode 100644 examples/vip_guard.ts create mode 100644 examples/vip_list.ts diff --git a/README.md b/README.md index f642b9b..66b1533 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,10 @@ Script file | Description [intercom_inside.ts](./examples/intercom_inside.ts) | An intercom script allowing communication with another room running the [intercom_outside.ts](./examples/intercom_outside.ts) script. [intercom_outside.ts](./examples/intercom_outside.ts) | An intercom script allowing communication with another room running the [intercom_inside.ts](./examples/intercom_inside.ts) script. [lock_inside.ts](./examples/lock_inside.ts) | A script that locks a door preventing others from using an exit in the room running the [lock_outside.ts](./examples/lock_outside.ts) script. -[lock_outside.ts](./examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the script running the [lock_inside.ts](./examples/lock_inside.ts) script. +[lock_outside.ts](./examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the [lock_inside.ts](./examples/lock_inside.ts) script. [secret_exit.ts](./examples/secret_exit.ts) | A script that reveals a secret passage when the password "tapeworm" is spoken. +[vip_list.ts](./examples/vip_list.ts) | A script that manages a list of VIP characters, requested by another room running the [vip_guard.ts](./examples/vip_guard.ts) script. +[vip_guard.ts](./examples/vip_guard.ts) | A script that prevents characters from using an exit unless they are listed as VIP character by the [vip_list.ts](./examples/vip_list.ts) script. ## About diff --git a/docs/documentation.md b/docs/documentation.md index b7469af..3ec8482 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -40,8 +40,10 @@ Script file | Description [intercom_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_inside.ts) | An intercom script allowing communication with another room running the [intercom_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_outside.ts) script. [intercom_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_outside.ts) | An intercom script allowing communication with another room running the [intercom_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_inside.ts) script. [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) | A script that locks a door preventing others from using an exit in the room running the [lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) script. -[lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the script running the [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) script. +[lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) script. [secret_exit.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/secret_exit.ts) | A script that reveals a secret passage when the password "tapeworm" is spoken. +[vip_list.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_list.ts) | A script that manages a list of VIP characters, requested by another room running the [vip_guard.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_guard.ts) script. +[vip_guard.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_guard.ts) | A script that prevents characters from using an exit unless they are listed as VIP character by the [vip_list.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_list.ts) script.

Entry points

@@ -732,7 +734,7 @@ Responds to the command action with an info message.

Parameters

-* `msg` (string): Info message. +* `msg` (string): Info message. It may be formatted with info formatting and span multiple paragraphs. --- @@ -747,7 +749,7 @@ Responds to the command action with an error message.

Parameters

-* `msg` (string): Error message. +* `msg` (string): Error message. It may be formatted and span multiple paragraphs. --- diff --git a/examples/intercom_inside.ts b/examples/intercom_inside.ts index cca5e9f..1a56a8d 100644 --- a/examples/intercom_inside.ts +++ b/examples/intercom_inside.ts @@ -57,7 +57,7 @@ export function onRoomEvent(addr: string, ev: string): void { } } -// onCommand is called when a characters uses a script command. +// onCommand is called when a character uses a script command. export function onCommand(addr: string, cmdAction: CmdAction): void { // Get the current active state of the intercom. const active = isActive() diff --git a/examples/vip_guard.ts b/examples/vip_guard.ts new file mode 100644 index 0000000..c5140bc --- /dev/null +++ b/examples/vip_guard.ts @@ -0,0 +1,47 @@ +/** + * The vip_guard.ts is a script that prevents characters from using an exit + * unless they are added to the VIP list that is kept by the vip_list.ts script. + * + * When a character tries to use the exit, a request is sent to vip_list.ts to + * check whether or not they are VIP characters. The vip_guard.ts script and + * vip_list.ts may (and should) be running in different rooms. + */ + +// Post address to the vipList script. Replace this. +// To get a room script's address, type: roomscript +const vipListAddr = "room.aaaaaaaaaaaaaaaaaaaa#bbbbbbbbbbbbbbbbbbbb" + +// ID of the exit to guard. Replace this. +// To get a room script's address, type: get exit +// The leading # should not be included. +const exitId = "aaaaaaaaaaaaaaaaaaaa" + +export function onActivate(): void { + // Always listen and intercept any use of the exit. + Room.listenExit(exitId) +} + +// onExitUse is called when someone tries to use the exit. +export function onExitUse(addr: string, exitAction: ExitAction): void { + // Send a request to the vip_list.ts script. It waits until we receive a response. + const response = Script.request(vipListAddr, "isVip", JSON.stringify(exitAction.charId)) + + // Assert we didn't get an error response. + if (response.isError()) { + // We look the error for debugging purpose + console.debug("Request error: " + response.error!) + // We cancel the exit use attempt with a message. + exitAction.cancel("The guard can't seem to find the VIP list.") + return + } + + // Parse the response. The vip_list.ts should have sent a boolean value. + const isVip = response.parseResult() + if (isVip) { + // We let the character use the exit. + exitAction.useExit() + } else { + // We cancel the exit use attempt with a message. + exitAction.cancel("The guard blocks your way as they cannot find you in the VIP list.") + } +} diff --git a/examples/vip_list.ts b/examples/vip_list.ts new file mode 100644 index 0000000..34471be --- /dev/null +++ b/examples/vip_list.ts @@ -0,0 +1,130 @@ +/** + * The vip_list.ts is a script that keeps a list of VIP characters, with + * commands to add, remove, and list them. Other scripts, such as vip_guard.ts, + * may send requests to ask whether a character is a VIP, using + * `Script.request`: + * ``` + * const response = Script.request(vipListAddr, "isVip", JSON.stringify(charId)) + * ``` + * + * The vip_guard.ts script sends requests to this scripts to only let VIP + * characters use an exit. + * + * The script adds three commands to the room: + * ``` + * add vip + * remove vip + * list vip + * ``` + */ + +// onActivate is called when the script is activated. +export function onActivate(): void { + // Add commands for managing the VIP list + Room.addCommand( + "add", + new Command("add vip ", "Add a character to the VIP list.") + .field("Character", new Field.Char("Character to add.")), + ) + Room.addCommand( + "remove", + new Command("remove vip ", "Remove a character from the VIP list.") + .field("Character", new Field.Char("Character to remove.")), + ) + Room.addCommand( + "list", + new Command("list vip", "List all VIP characters."), + ) + // Listen for script requests. + Script.listen() +} + +// Command arguments class. Used by both the "add" and "remove" command. +@json +class Args { + @alias("Character") + char: FieldValue.Char = new FieldValue.Char(); +} + +// onCommand is called when a character uses a script command. +export function onCommand(addr: string, cmdAction: CmdAction): void { + // Handle add command + if (cmdAction.keyword == "add") { + const args = JSON.parse(cmdAction.data) + // Check if the character is already added. + if (Store.getString(args.char.id) == null) { + // Add the character to the store. As value, we store the timestamp + // when the character was added. This value currently not used in + // this script. + Store.setString(args.char.id, Date.now().toString()) + cmdAction.info(`**${args.char.name} ${args.char.surname}** was added to the VIP list.`) + } else { + cmdAction.info(`**${args.char.name} ${args.char.surname}** was already in the VIP list.`) + } + return + } + + // Handle remove command + if (cmdAction.keyword == "remove") { + const args = JSON.parse(cmdAction.data) + // Check if the character is not already removed. + if (Store.getString(args.char.id) != null) { + // Remove the character from the store. + Store.deleteKey(args.char.id) + cmdAction.info(`**${args.char.name} ${args.char.surname}** was removed from the VIP list.`) + } else { + cmdAction.info(`**${args.char.name} ${args.char.surname}** was not found in the VIP list.`) + } + return + } + + // Handle list command + if (cmdAction.keyword == "list") { + let chars = new Array() + // Iterate over all characters stored in the list + for (const iter = new Store.Iterator(); iter.isValid(); iter.next()) { + // Get the store key which is the character ID. + const charId = iter.getKeyString() + // Get character info. + const char = Script.getChar(charId) + // Verify that the character is found. + if (char != null) { + chars.push(char) + } else { + // The character is probably deleted. + // We prune our VIP list by deleting them there as well. + console.debug("Pruned " + charId) + Store.deleteKey(charId) + } + } + + // Check if the list is empty. If so, show a simple message. + if (chars.length == 0) { + cmdAction.info("_List is empty_") + return + } + + // Sort the characters by name and surname. + chars.sort((a: Script.Char, b: Script.Char) => a.name.localeCompare(b.name) || a.surname.localeCompare(b.surname)) + // Concatenate the names into a string with line breaks between each character. + const list = chars + .map((char: Script.Char, i: i32, chars: Script.Char[]) => char.name + " " + char.surname) + .join("\n") + // Show the list with a header "VIP list". For help on how to format + // text, see: `help format info` + cmdAction.info("## VIP list\n" + list) + return + } +} + +// onRequest is called when another script sends a request to this script. +export function onRequest(addr: string, request: Request): void { + if (request.topic == "isVip") { + // Parse the data passed as arguments. It should be the charId. + const charId = request.parseData() + // If the charId exists in the store, the character is a VIP. + const isVip = Store.getString(charId) != null + // Send a response to the request. It must be JSON encoded. + request.reply(JSON.stringify(isVip)) + } +} diff --git a/lib/host.ts b/lib/host.ts index abed4ce..cea687c 100644 --- a/lib/host.ts +++ b/lib/host.ts @@ -24,8 +24,10 @@ * [intercom_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_inside.ts) | An intercom script allowing communication with another room running the [intercom_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_outside.ts) script. * [intercom_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_outside.ts) | An intercom script allowing communication with another room running the [intercom_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_inside.ts) script. * [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) | A script that locks a door preventing others from using an exit in the room running the [lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) script. - * [lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the script running the [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) script. + * [lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) script. * [secret_exit.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/secret_exit.ts) | A script that reveals a secret passage when the password "tapeworm" is spoken. + * [vip_list.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_list.ts) | A script that manages a list of VIP characters, requested by another room running the [vip_guard.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_guard.ts) script. + * [vip_guard.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_guard.ts) | A script that prevents characters from using an exit unless they are listed as VIP character by the [vip_list.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_list.ts) script. * * # Entry points * @@ -370,7 +372,7 @@ export class CmdAction { /** * Responds to the command action with an info message. - * @param msg Info message. + * @param msg Info message. It may be formatted with info formatting and span multiple paragraphs. */ info(msg: string): void { cmdaction_binding.info(this.actionId, msg); @@ -378,7 +380,7 @@ export class CmdAction { /** * Responds to the command action with an error message. - * @param msg Error message. + * @param msg Error message. It may be formatted and span multiple paragraphs. */ error(msg: string): void { cmdaction_binding.error(this.actionId, msg); diff --git a/lib/types/host/index.d.ts b/lib/types/host/index.d.ts index 56e04f2..dae90d3 100644 --- a/lib/types/host/index.d.ts +++ b/lib/types/host/index.d.ts @@ -24,8 +24,10 @@ * [intercom_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_inside.ts) | An intercom script allowing communication with another room running the [intercom_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_outside.ts) script. * [intercom_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_outside.ts) | An intercom script allowing communication with another room running the [intercom_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/intercom_inside.ts) script. * [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) | A script that locks a door preventing others from using an exit in the room running the [lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) script. - * [lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the script running the [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) script. + * [lock_outside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_outside.ts) | A script that prevents characters from using an exit locked by the [lock_inside.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/lock_inside.ts) script. * [secret_exit.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/secret_exit.ts) | A script that reveals a secret passage when the password "tapeworm" is spoken. + * [vip_list.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_list.ts) | A script that manages a list of VIP characters, requested by another room running the [vip_guard.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_guard.ts) script. + * [vip_guard.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_guard.ts) | A script that prevents characters from using an exit unless they are listed as VIP character by the [vip_list.ts](https://github.com/mucklet/mucklet-script/blob/master/examples/vip_list.ts) script. * * # Entry points * @@ -341,12 +343,12 @@ declare class CmdAction { data: string; /** * Responds to the command action with an info message. - * @param msg Info message. + * @param msg Info message. It may be formatted with info formatting and span multiple paragraphs. */ info(msg: string): void; /** * Responds to the command action with an error message. - * @param msg Error message. + * @param msg Error message. It may be formatted and span multiple paragraphs. */ error(msg: string): void; /**