rs3-api-extended 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mark7625
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,88 @@
1
- # Temporary Holding Version
1
+ # rs3-api-extended
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Custom helpers for RuneScape 3 plugins. Not part of Jagex's API.
4
+
5
+ Everything in this package was written for this project. None of it exists in the game client, and none of it is something you can call from a plain Lua plugin. It compiles to Lua and ships inside your plugin under `lua_modules/rs3-api-extended/`. For the client's own API, which this builds on, see `rs3-api-core`.
6
+
7
+ ```ts
8
+ import { definePlugin, on, vars } from "rs3-api-extended";
9
+ ```
10
+
11
+ ## definePlugin
12
+
13
+ Wires the lifecycle globals the client looks for and runs your modules.
14
+
15
+ ```ts
16
+ definePlugin({
17
+ modules: [login, overlay],
18
+ });
19
+ ```
20
+
21
+ A module is any object with optional `init`, `tick`, `draw` and `shutdown` functions, which in practice means a file of exported functions imported with `import * as`. `definePlugin` assigns `PluginLogic` and `PluginShutdown`, adds `PluginDraw` only if something actually draws, and calls each hook through `pcall` so one module throwing does not stop the others. Errors print once per distinct message, prefixed with the plugin's name from `plugin.json`. Pass `name` to override that prefix, or `onError` to handle failures yourself.
22
+
23
+ | Option | Default | What it does |
24
+ | --- | --- | --- |
25
+ | `modules` | `[]` | Objects whose `init`, `tick`, `draw` and `shutdown` get called. |
26
+ | `autoUnsubscribe` | `true` | Detach anything still subscribed through `on` once shutdown has run. |
27
+ | `name` | `Plugin.Name` | Prefix on error messages. |
28
+ | `onError` | prints once per message | Called with each hook failure instead. |
29
+
30
+ ## autoUnsubscribe
31
+
32
+ On shutdown, after every module's own `shutdown` has run, any subscription made through `on` and not already detached is detached for you. It is on by default, so a plugin that only ever subscribes in `init` needs no `shutdown` at all:
33
+
34
+ ```ts
35
+ import { on } from "rs3-api-extended";
36
+
37
+ export function init() {
38
+ on(Event.GameStateChanged, onGameState);
39
+ }
40
+
41
+ function onGameState(event: GameStateChangedEvent) {
42
+ if (event.newState === GameState.GAME) {
43
+ Chat.AddMessage(ChatType.GENERAL, "This is a test");
44
+ }
45
+ }
46
+ ```
47
+
48
+ This only covers `on`. A handler attached with `Event.X.Subscribe(handler)` directly is invisible to it, because nothing recorded the pair, so those stay yours to unsubscribe in `shutdown`. Mixing the two is fine: each `on` subscription is forgotten as soon as its unsubscribe runs, so nothing gets detached twice.
49
+
50
+ `unsubscribeAll()` is exported if you want to drop every `on` subscription at some other moment, and `autoUnsubscribe: false` turns the shutdown sweep off entirely.
51
+
52
+ ## on
53
+
54
+ Subscribes to an event. The subscription is live as soon as the call returns, and `definePlugin` will clean it up at shutdown, so most of the time you can ignore the return value.
55
+
56
+ What it returns is the matching unsubscribe, for when you want to detach before shutdown:
57
+
58
+ ```ts
59
+ let stopListening = () => {};
60
+
61
+ export function init() {
62
+ stopListening = on(Event.SkillTransmit, onSkillTransmit);
63
+ }
64
+
65
+ function onSkillTransmit(event: SkillTransmitEvent) {
66
+ if (event.newGameplayLevel === 99) {
67
+ stopListening();
68
+ }
69
+ }
70
+ ```
71
+
72
+ Calling it twice is harmless, and so is letting shutdown collect it instead.
73
+
74
+ ## vars
75
+
76
+ The client's `var` table, under a name TypeScript allows, since `var` is a reserved word.
77
+
78
+ ```ts
79
+ const lifepoints = vars.VarPlayer.FromID(13379).value;
80
+ ```
81
+
82
+ ## Adding a helper
83
+
84
+ Worth adding if more than one plugin would use it. Keep it to functions and tables: no classes, no array helper methods, nothing that drags in TypeScriptToLua's runtime, because release builds reject output that references it. Put the source in `src/`, export it from `index.ts`, and the build type-checks it against the core declarations.
85
+
86
+ ## License
87
+
88
+ [MIT](https://github.com/Mark7625/rs3-api-ts/blob/main/LICENSE)
@@ -0,0 +1,4 @@
1
+ /** @noSelfInFile */
2
+ export { definePlugin, on, unsubscribeAll } from "./plugin";
3
+ export type { Handler, Module, PluginOptions, Subscribable, Unsubscribe } from "./plugin";
4
+ export { vars } from "./vars";
package/dist/index.lua ADDED
@@ -0,0 +1,12 @@
1
+ local ____exports = {}
2
+ do
3
+ local ____plugin = require("plugin")
4
+ ____exports.definePlugin = ____plugin.definePlugin
5
+ ____exports.on = ____plugin.on
6
+ ____exports.unsubscribeAll = ____plugin.unsubscribeAll
7
+ end
8
+ do
9
+ local ____vars = require("vars")
10
+ ____exports.vars = ____vars.vars
11
+ end
12
+ return ____exports
@@ -0,0 +1,28 @@
1
+ /** @noSelfInFile */
2
+ /** @noSelf */
3
+ export interface Module {
4
+ init?(): void;
5
+ tick?(now: number): void;
6
+ draw?(deltaSecs: number): void;
7
+ shutdown?(): void;
8
+ }
9
+ /** @noSelf */
10
+ export interface PluginOptions extends Module {
11
+ name?: string;
12
+ modules?: Module[];
13
+ autoUnsubscribe?: boolean;
14
+ onError?(message: string): void;
15
+ }
16
+ export type Unsubscribe = () => void;
17
+ export type Handler<T> = (event: T) => boolean | void;
18
+ /** @noSelf */
19
+ export interface Subscribable<T> {
20
+ Subscribe(handler: Handler<T>): void;
21
+ Unsubscribe(handler: Handler<T>): void;
22
+ }
23
+ /** @noSelf */
24
+ export declare function on<T>(source: Subscribable<T>, handler: Handler<T>): Unsubscribe;
25
+ /** @noSelf */
26
+ export declare function unsubscribeAll(): void;
27
+ /** @noSelf */
28
+ export declare function definePlugin(options: PluginOptions): void;
@@ -0,0 +1,92 @@
1
+ local ____exports = {}
2
+ local tracked = {}
3
+ ---
4
+ -- @noSelf
5
+ function ____exports.on(source, handler)
6
+ source.Subscribe(handler)
7
+ local entry = {detach = function()
8
+ pcall(source.Unsubscribe, handler)
9
+ end}
10
+ tracked[#tracked + 1] = entry
11
+ return function()
12
+ local detach = entry.detach
13
+ if detach ~= nil then
14
+ entry.detach = nil
15
+ detach()
16
+ end
17
+ end
18
+ end
19
+ ---
20
+ -- @noSelf
21
+ function ____exports.unsubscribeAll()
22
+ local pending = tracked
23
+ tracked = {}
24
+ for ____, entry in ipairs(pending) do
25
+ local detach = entry.detach
26
+ if detach ~= nil then
27
+ entry.detach = nil
28
+ detach()
29
+ end
30
+ end
31
+ end
32
+ ---
33
+ -- @noSelf
34
+ function ____exports.definePlugin(options)
35
+ local name = options.name or Plugin.Name or "plugin"
36
+ local modules = options.modules or ({})
37
+ local autoUnsubscribe = options.autoUnsubscribe ~= false
38
+ local lastError
39
+ local function report(message)
40
+ if options.onError ~= nil then
41
+ options.onError(message)
42
+ elseif message ~= lastError then
43
+ lastError = message
44
+ print((("[" .. name) .. "] ") .. message)
45
+ end
46
+ end
47
+ local function guard(fn, arg)
48
+ if fn == nil then
49
+ return
50
+ end
51
+ local ok, err = pcall(fn, arg)
52
+ if not ok then
53
+ report(tostring(err))
54
+ end
55
+ end
56
+ PluginLogic = function(event)
57
+ local now = event.logicTick
58
+ for ____, m in ipairs(modules) do
59
+ guard(m.tick, now)
60
+ end
61
+ guard(options.tick, now)
62
+ end
63
+ local hasDraw = options.draw ~= nil
64
+ for ____, m in ipairs(modules) do
65
+ if m.draw ~= nil then
66
+ hasDraw = true
67
+ end
68
+ end
69
+ if hasDraw then
70
+ PluginDraw = function(event)
71
+ local delta = event.timeDeltaSecs
72
+ for ____, m in ipairs(modules) do
73
+ guard(m.draw, delta)
74
+ end
75
+ guard(options.draw, delta)
76
+ end
77
+ end
78
+ PluginShutdown = function()
79
+ guard(options.shutdown)
80
+ for ____, m in ipairs(modules) do
81
+ guard(m.shutdown)
82
+ end
83
+ if autoUnsubscribe then
84
+ guard(____exports.unsubscribeAll)
85
+ end
86
+ end
87
+ for ____, m in ipairs(modules) do
88
+ guard(m.init)
89
+ end
90
+ guard(options.init)
91
+ end
92
+ return ____exports
package/dist/vars.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /** @noSelfInFile */
2
+ /**
3
+ * The game's `var` global (VarPlayer, VarClient, VarClan, ...), re-exported under a
4
+ * name TypeScript allows. `var` is a reserved word, so it cannot be declared directly.
5
+ *
6
+ * const lifepoints = vars.VarPlayer.FromID(13379).value;
7
+ */
8
+ export const vars: VarApi;
package/dist/vars.lua ADDED
@@ -0,0 +1 @@
1
+ return { vars = var }
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "rs3-api-extended",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Runtime helpers for RuneScape 3 plugins written in TypeScript: definePlugin, event subscription, and the vars shim. Compiled to readable Lua and bundled into your plugin by rs3-plugin.",
5
+ "license": "MIT",
6
+ "author": "Mark7625",
7
+ "homepage": "https://github.com/Mark7625/rs3-api-ts/tree/main/packages/extended#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Mark7625/rs3-api-ts.git",
11
+ "directory": "packages/extended"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Mark7625/rs3-api-ts/issues"
15
+ },
16
+ "keywords": [
17
+ "runescape",
18
+ "rs3",
19
+ "plugin",
20
+ "lua",
21
+ "typescript-to-lua"
22
+ ],
23
+ "main": "./dist/index",
24
+ "types": "./dist/index.d.ts",
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "build": "tstl -p tsconfig.json && node scripts/postbuild.js",
35
+ "prepack": "npm run build"
36
+ },
37
+ "peerDependencies": {
38
+ "rs3-api-core": "^0.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "rs3-api-core": "^0.1.0",
42
+ "typescript": "6.0.2",
43
+ "typescript-to-lua": "^1.37.1"
44
+ }
45
+ }