@sagl/server 0.0.0-stage → 0.1.0-next.49
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 +21 -0
- package/README.md +61 -2
- package/dist/actions.d.ts +46 -0
- package/dist/actions.js +80 -0
- package/dist/actions.js.map +1 -0
- package/dist/animations.d.ts +32 -0
- package/dist/animations.js +95 -0
- package/dist/animations.js.map +1 -0
- package/dist/audio.d.ts +180 -0
- package/dist/audio.js +353 -0
- package/dist/audio.js.map +1 -0
- package/dist/audio_effects.d.ts +219 -0
- package/dist/audio_effects.js +84 -0
- package/dist/audio_effects.js.map +1 -0
- package/dist/camera.d.ts +188 -0
- package/dist/camera.js +247 -0
- package/dist/camera.js.map +1 -0
- package/dist/claims.d.ts +27 -0
- package/dist/claims.js +48 -0
- package/dist/claims.js.map +1 -0
- package/dist/commands.d.ts +135 -0
- package/dist/commands.js +373 -0
- package/dist/commands.js.map +1 -0
- package/dist/console.d.ts +21 -0
- package/dist/console.js +100 -0
- package/dist/console.js.map +1 -0
- package/dist/constants.d.ts +654 -0
- package/dist/constants.js +657 -0
- package/dist/constants.js.map +1 -0
- package/dist/cutscene.d.ts +47 -0
- package/dist/cutscene.js +190 -0
- package/dist/cutscene.js.map +1 -0
- package/dist/draw.d.ts +106 -0
- package/dist/draw.js +132 -0
- package/dist/draw.js.map +1 -0
- package/dist/effects.d.ts +91 -0
- package/dist/effects.js +116 -0
- package/dist/effects.js.map +1 -0
- package/dist/gangzones.d.ts +47 -0
- package/dist/gangzones.js +122 -0
- package/dist/gangzones.js.map +1 -0
- package/dist/geo.d.ts +11 -0
- package/dist/geo.js +25 -0
- package/dist/geo.js.map +1 -0
- package/dist/hud.d.ts +159 -0
- package/dist/hud.js +247 -0
- package/dist/hud.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction.d.ts +81 -0
- package/dist/interaction.js +143 -0
- package/dist/interaction.js.map +1 -0
- package/dist/junctions.d.ts +1 -0
- package/dist/junctions.js +330 -0
- package/dist/junctions.js.map +1 -0
- package/dist/map.d.ts +55 -0
- package/dist/map.js +71 -0
- package/dist/map.js.map +1 -0
- package/dist/menu.d.ts +96 -0
- package/dist/menu.js +129 -0
- package/dist/menu.js.map +1 -0
- package/dist/mission.d.ts +482 -0
- package/dist/mission.js +1258 -0
- package/dist/mission.js.map +1 -0
- package/dist/models.d.ts +56 -0
- package/dist/models.js +71 -0
- package/dist/models.js.map +1 -0
- package/dist/modshop.d.ts +229 -0
- package/dist/modshop.js +904 -0
- package/dist/modshop.js.map +1 -0
- package/dist/modshop_data.d.ts +44 -0
- package/dist/modshop_data.js +287 -0
- package/dist/modshop_data.js.map +1 -0
- package/dist/native.d.ts +809 -0
- package/dist/native.js +27 -0
- package/dist/native.js.map +1 -0
- package/dist/objects.d.ts +249 -0
- package/dist/objects.js +455 -0
- package/dist/objects.js.map +1 -0
- package/dist/paint.d.ts +124 -0
- package/dist/paint.js +84 -0
- package/dist/paint.js.map +1 -0
- package/dist/paynspray.d.ts +44 -0
- package/dist/paynspray.js +177 -0
- package/dist/paynspray.js.map +1 -0
- package/dist/ped.d.ts +335 -0
- package/dist/ped.js +536 -0
- package/dist/ped.js.map +1 -0
- package/dist/player.d.ts +685 -0
- package/dist/player.js +1163 -0
- package/dist/player.js.map +1 -0
- package/dist/screeneffects.d.ts +99 -0
- package/dist/screeneffects.js +257 -0
- package/dist/screeneffects.js.map +1 -0
- package/dist/scripts.d.ts +69 -0
- package/dist/scripts.js +125 -0
- package/dist/scripts.js.map +1 -0
- package/dist/server.d.ts +713 -0
- package/dist/server.js +1080 -0
- package/dist/server.js.map +1 -0
- package/dist/shopblips.d.ts +9 -0
- package/dist/shopblips.js +30 -0
- package/dist/shopblips.js.map +1 -0
- package/dist/special.d.ts +33 -0
- package/dist/special.js +26 -0
- package/dist/special.js.map +1 -0
- package/dist/splash.d.ts +42 -0
- package/dist/splash.js +55 -0
- package/dist/splash.js.map +1 -0
- package/dist/text.d.ts +19 -0
- package/dist/text.js +25 -0
- package/dist/text.js.map +1 -0
- package/dist/ui.d.ts +141 -0
- package/dist/ui.js +243 -0
- package/dist/ui.js.map +1 -0
- package/dist/vehicle.d.ts +306 -0
- package/dist/vehicle.js +612 -0
- package/dist/vehicle.js.map +1 -0
- package/dist/vehiclecolors.d.ts +26 -0
- package/dist/vehiclecolors.js +49 -0
- package/dist/vehiclecolors.js.map +1 -0
- package/dist/world.d.ts +222 -0
- package/dist/world.js +227 -0
- package/dist/world.js.map +1 -0
- package/dist/zone.d.ts +89 -0
- package/dist/zone.js +140 -0
- package/dist/zone.js.map +1 -0
- package/package.json +33 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Burnett-Taylor
|
|
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,62 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sagl/server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The server for **SAGL**, a multiplayer mod for GTA: San Andreas, built for
|
|
4
|
+
roleplay servers. Write your gamemode in TypeScript or JavaScript on
|
|
5
|
+
Node.js: players, vehicles, peds, missions, UI, cameras, shaders and more,
|
|
6
|
+
with the server in control.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install @sagl/server
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { Server, Color } from "@sagl/server";
|
|
14
|
+
|
|
15
|
+
const server = new Server({ name: "My server", port: 7777 });
|
|
16
|
+
|
|
17
|
+
server.on("playerJoin", (player) => {
|
|
18
|
+
player.send(`Welcome, **${player.name}**! Press {key:chat} to talk.`, Color.White, { markdown: true });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
server.on("playerSpawn", (player) => {
|
|
22
|
+
player.setPosition({ x: 2495, y: -1670, z: 13.4 }, 90);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
server.listen();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Run it with `node index.ts`: Node 22.18 and newer run TypeScript directly
|
|
29
|
+
(older 22s need `--experimental-strip-types`). Players join with the SAGL
|
|
30
|
+
client.
|
|
31
|
+
|
|
32
|
+
## Documentation
|
|
33
|
+
|
|
34
|
+
**[docs.sagl.mp](https://docs.sagl.mp)**: getting started, the full API,
|
|
35
|
+
and the example gamemodes.
|
|
36
|
+
|
|
37
|
+
## Platforms
|
|
38
|
+
|
|
39
|
+
The server's core is native code, installed for your computer
|
|
40
|
+
automatically:
|
|
41
|
+
|
|
42
|
+
| Platform | Package |
|
|
43
|
+
|---|---|
|
|
44
|
+
| Linux x64 (glibc 2.28 or newer: Debian 10+, Ubuntu 20.04+, RHEL 8+) | `@sagl/server-linux-x64` |
|
|
45
|
+
| Windows x64 | `@sagl/server-win32-x64` |
|
|
46
|
+
|
|
47
|
+
Node.js 22 or newer.
|
|
48
|
+
|
|
49
|
+
## Versions
|
|
50
|
+
|
|
51
|
+
SAGL's client, server and this SDK share one version. Within the same
|
|
52
|
+
**minor** version (0.4.x) clients and servers work together; a new minor
|
|
53
|
+
version changes the network protocol, so update servers and players'
|
|
54
|
+
clients together. `Server.version` and `Server.protocolVersion` say what
|
|
55
|
+
you're running.
|
|
56
|
+
|
|
57
|
+
`npm install @sagl/server` gets the latest release; `@sagl/server@next` the
|
|
58
|
+
newest prerelease, built from every change.
|
|
59
|
+
|
|
60
|
+
## Licence
|
|
61
|
+
|
|
62
|
+
MIT
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { NativeServer } from "./native.js";
|
|
2
|
+
import type { Player } from "./player.js";
|
|
3
|
+
/**
|
|
4
|
+
* Key names for actions' default keys, by their place on a US keyboard
|
|
5
|
+
* (players see what their own layout prints there). Escape and Alt can't
|
|
6
|
+
* be bound.
|
|
7
|
+
*/
|
|
8
|
+
export declare const KEY_NAMES: readonly string[];
|
|
9
|
+
export interface ActionOptions {
|
|
10
|
+
/** What players see in Settings, Controls. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Its key until a player binds their own (`KEY_NAMES`); none if left out. */
|
|
13
|
+
key?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The server's actions (`server.actions`): things players do with a key
|
|
17
|
+
* of their choosing. Each shows in their Settings, Controls, under the
|
|
18
|
+
* server's name, with its default key; they can bind it to another (or
|
|
19
|
+
* two), kept for this server. Pressing it reaches `playerAction` (and the
|
|
20
|
+
* handler given here). Client scripts have the same: `sagl.actions`, which
|
|
21
|
+
* also hears the server's.
|
|
22
|
+
*
|
|
23
|
+
* Keys work while the game has the keyboard (not while typing in the chat
|
|
24
|
+
* or a menu), and the game still sees them: pick keys the game doesn't use
|
|
25
|
+
* on foot or driving (or let players choose).
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* server.actions.register("engine", { label: "Engine on/off", key: "N" }, (player) => {
|
|
29
|
+
* const v = player.vehicle;
|
|
30
|
+
* if (v && player.seat === 0) v.engine = !v.engine;
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare class Actions {
|
|
35
|
+
#private;
|
|
36
|
+
/** @internal */
|
|
37
|
+
constructor(native: NativeServer);
|
|
38
|
+
/** Defines (or redefines) an action: `id` up to 32 characters. */
|
|
39
|
+
register(id: string, options: ActionOptions, handler?: (player: Player) => void): void;
|
|
40
|
+
/** Removes an action (from players' Controls too). */
|
|
41
|
+
remove(id: string): void;
|
|
42
|
+
/** The actions defined. */
|
|
43
|
+
get ids(): string[];
|
|
44
|
+
/** @internal A player pressed one. */
|
|
45
|
+
pressed(player: Player, id: string): void;
|
|
46
|
+
}
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key names for actions' default keys, by their place on a US keyboard
|
|
3
|
+
* (players see what their own layout prints there). Escape and Alt can't
|
|
4
|
+
* be bound.
|
|
5
|
+
*/
|
|
6
|
+
export const KEY_NAMES = [
|
|
7
|
+
..."ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split(""),
|
|
8
|
+
...Array.from({ length: 24 }, (_, i) => `F${i + 1}`),
|
|
9
|
+
...Array.from({ length: 10 }, (_, i) => `Numpad${i}`),
|
|
10
|
+
"NumpadMultiply", "NumpadPlus", "NumpadMinus", "NumpadDecimal", "NumpadDivide",
|
|
11
|
+
"Space", "Tab", "Enter", "Backspace", "Insert", "Delete", "Home", "End", "PageUp", "PageDown",
|
|
12
|
+
"Up", "Down", "Left", "Right", "Shift", "Ctrl", "CapsLock",
|
|
13
|
+
"Comma", "Period", "Semicolon", "Slash", "Minus", "Equals", "LeftBracket", "RightBracket",
|
|
14
|
+
"Backslash", "Quote", "Backquote", "IntlBackslash",
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* The server's actions (`server.actions`): things players do with a key
|
|
18
|
+
* of their choosing. Each shows in their Settings, Controls, under the
|
|
19
|
+
* server's name, with its default key; they can bind it to another (or
|
|
20
|
+
* two), kept for this server. Pressing it reaches `playerAction` (and the
|
|
21
|
+
* handler given here). Client scripts have the same: `sagl.actions`, which
|
|
22
|
+
* also hears the server's.
|
|
23
|
+
*
|
|
24
|
+
* Keys work while the game has the keyboard (not while typing in the chat
|
|
25
|
+
* or a menu), and the game still sees them: pick keys the game doesn't use
|
|
26
|
+
* on foot or driving (or let players choose).
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* server.actions.register("engine", { label: "Engine on/off", key: "N" }, (player) => {
|
|
30
|
+
* const v = player.vehicle;
|
|
31
|
+
* if (v && player.seat === 0) v.engine = !v.engine;
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export class Actions {
|
|
36
|
+
#native;
|
|
37
|
+
#handlers = new Map();
|
|
38
|
+
#defined = new Set();
|
|
39
|
+
/** @internal */
|
|
40
|
+
constructor(native) {
|
|
41
|
+
this.#native = native;
|
|
42
|
+
}
|
|
43
|
+
/** Defines (or redefines) an action: `id` up to 32 characters. */
|
|
44
|
+
register(id, options, handler) {
|
|
45
|
+
if (!/^[A-Za-z0-9_.:-]{1,32}$/.test(id))
|
|
46
|
+
throw new RangeError(`action id "${id}": 1-32 of A-Z, 0-9, _ . : -`);
|
|
47
|
+
const key = options.key ?? "";
|
|
48
|
+
if (key && !KEY_NAMES.includes(key))
|
|
49
|
+
throw new RangeError(`not a key name: "${key}" (see KEY_NAMES)`);
|
|
50
|
+
this.#native.defineAction(id, String(options.label).slice(0, 64), key);
|
|
51
|
+
this.#defined.add(id);
|
|
52
|
+
if (handler)
|
|
53
|
+
this.#handlers.set(id, handler);
|
|
54
|
+
else
|
|
55
|
+
this.#handlers.delete(id);
|
|
56
|
+
}
|
|
57
|
+
/** Removes an action (from players' Controls too). */
|
|
58
|
+
remove(id) {
|
|
59
|
+
this.#native.removeAction(id);
|
|
60
|
+
this.#defined.delete(id);
|
|
61
|
+
this.#handlers.delete(id);
|
|
62
|
+
}
|
|
63
|
+
/** The actions defined. */
|
|
64
|
+
get ids() {
|
|
65
|
+
return [...this.#defined];
|
|
66
|
+
}
|
|
67
|
+
/** @internal A player pressed one. */
|
|
68
|
+
pressed(player, id) {
|
|
69
|
+
const handler = this.#handlers.get(id);
|
|
70
|
+
if (!handler)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
handler(player);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.error(`[sagl] action "${id}" failed:`, err);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { NativeServer } from "./native.js";
|
|
2
|
+
import type { Player } from "./player.js";
|
|
3
|
+
/** One of the server's own animation sets: an IFP client file. */
|
|
4
|
+
export interface AnimationSetInfo {
|
|
5
|
+
/** The set's name (inside the file): what `playAnim(set, name)` takes. */
|
|
6
|
+
name: string;
|
|
7
|
+
/** The client file. */
|
|
8
|
+
file: string;
|
|
9
|
+
/** How many animations it has. */
|
|
10
|
+
animations: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The server's own animations: every `.ifp` among the client files (San
|
|
14
|
+
* Andreas' format, or III / Vice City's older one) is an animation set,
|
|
15
|
+
* named by the name inside it. `player.playAnim(set, name)` and
|
|
16
|
+
* `ped.playAnim(set, name)` play them like the game's own; players' games
|
|
17
|
+
* load a set when it's first played, and unload idle ones when they need
|
|
18
|
+
* room. `server.animations`.
|
|
19
|
+
*/
|
|
20
|
+
export declare class ServerAnimations {
|
|
21
|
+
#private;
|
|
22
|
+
/** @internal */
|
|
23
|
+
constructor(native: NativeServer, players: () => Player[], dir: string | undefined);
|
|
24
|
+
/** The sets, by name. */
|
|
25
|
+
get sets(): AnimationSetInfo[];
|
|
26
|
+
/** Whether there's a set of that name (the server's own, not the game's). */
|
|
27
|
+
has(name: string): boolean;
|
|
28
|
+
/** @internal Reads the client files' IFPs again (with `server.rescanClientFiles()`). */
|
|
29
|
+
rescan(): void;
|
|
30
|
+
/** @internal A player joined: every set. */
|
|
31
|
+
replay(player: Player): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* The server's own animations: every `.ifp` among the client files (San
|
|
5
|
+
* Andreas' format, or III / Vice City's older one) is an animation set,
|
|
6
|
+
* named by the name inside it. `player.playAnim(set, name)` and
|
|
7
|
+
* `ped.playAnim(set, name)` play them like the game's own; players' games
|
|
8
|
+
* load a set when it's first played, and unload idle ones when they need
|
|
9
|
+
* room. `server.animations`.
|
|
10
|
+
*/
|
|
11
|
+
export class ServerAnimations {
|
|
12
|
+
#native;
|
|
13
|
+
#players;
|
|
14
|
+
#dir;
|
|
15
|
+
#sets = new Map();
|
|
16
|
+
/** @internal */
|
|
17
|
+
constructor(native, players, dir) {
|
|
18
|
+
this.#native = native;
|
|
19
|
+
this.#players = players;
|
|
20
|
+
this.#dir = dir;
|
|
21
|
+
this.rescan();
|
|
22
|
+
}
|
|
23
|
+
/** The sets, by name. */
|
|
24
|
+
get sets() {
|
|
25
|
+
return [...this.#sets.values()];
|
|
26
|
+
}
|
|
27
|
+
/** Whether there's a set of that name (the server's own, not the game's). */
|
|
28
|
+
has(name) {
|
|
29
|
+
return this.#sets.has(name.toUpperCase());
|
|
30
|
+
}
|
|
31
|
+
/** @internal Reads the client files' IFPs again (with `server.rescanClientFiles()`). */
|
|
32
|
+
rescan() {
|
|
33
|
+
const old = this.#sets;
|
|
34
|
+
this.#sets = new Map();
|
|
35
|
+
if (this.#dir)
|
|
36
|
+
this.#scan(this.#dir, "");
|
|
37
|
+
for (const p of this.#players()) {
|
|
38
|
+
for (const [key, set] of old)
|
|
39
|
+
if (!this.#sets.has(key))
|
|
40
|
+
this.#native.animationSet(p.id, set.name, "");
|
|
41
|
+
for (const set of this.#sets.values())
|
|
42
|
+
this.#native.animationSet(p.id, set.name, set.file);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** @internal A player joined: every set. */
|
|
46
|
+
replay(player) {
|
|
47
|
+
for (const set of this.#sets.values())
|
|
48
|
+
this.#native.animationSet(player.id, set.name, set.file);
|
|
49
|
+
}
|
|
50
|
+
#scan(dir, prefix) {
|
|
51
|
+
for (const entry of fs.readdirSync(path.join(dir, prefix), { withFileTypes: true })) {
|
|
52
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
this.#scan(dir, rel);
|
|
55
|
+
}
|
|
56
|
+
else if (entry.name.toLowerCase().endsWith(".ifp")) {
|
|
57
|
+
const info = readHeader(path.join(dir, rel));
|
|
58
|
+
if (!info) {
|
|
59
|
+
console.warn(`[sagl] animations: ${rel} isn't an animation file (IFP)`);
|
|
60
|
+
}
|
|
61
|
+
else if (info.name.length > 15 || !/^[A-Za-z0-9_]+$/.test(info.name)) {
|
|
62
|
+
console.warn(`[sagl] animations: ${rel}'s set name "${info.name}" must be 1-15 letters, digits or _`);
|
|
63
|
+
}
|
|
64
|
+
else if (this.#sets.has(info.name.toUpperCase())) {
|
|
65
|
+
console.warn(`[sagl] animations: ${rel} and ${this.#sets.get(info.name.toUpperCase()).file} are both set "${info.name}"`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.#sets.set(info.name.toUpperCase(), { name: info.name, file: rel, animations: info.animations });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// The set name and animation count from an IFP's header.
|
|
75
|
+
function readHeader(file) {
|
|
76
|
+
const fd = fs.openSync(file, "r");
|
|
77
|
+
try {
|
|
78
|
+
const b = Buffer.alloc(64);
|
|
79
|
+
fs.readSync(fd, b, 0, 64, 0);
|
|
80
|
+
const tag = b.toString("latin1", 0, 4);
|
|
81
|
+
const cstr = (at, max) => {
|
|
82
|
+
const end = b.indexOf(0, at);
|
|
83
|
+
return b.toString("latin1", at, end < 0 || end > at + max ? at + max : end);
|
|
84
|
+
};
|
|
85
|
+
if (tag === "ANP3" || tag === "ANP2")
|
|
86
|
+
return { name: cstr(8, 24), animations: b.readUInt32LE(32) };
|
|
87
|
+
if (tag === "ANPK" && b.toString("latin1", 8, 12) === "INFO")
|
|
88
|
+
return { name: cstr(20, 24), animations: b.readUInt32LE(16) };
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
fs.closeSync(fd);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=animations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animations.js","sourceRoot":"","sources":["../src/animations.ts"],"names":[],"mappings":"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"}
|
package/dist/audio.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { NativeServer } from "./native.js";
|
|
2
|
+
import type { Ped } from "./ped.js";
|
|
3
|
+
import { Player, type Vector3 } from "./player.js";
|
|
4
|
+
import { Vehicle } from "./vehicle.js";
|
|
5
|
+
import type { Zone } from "./zone.js";
|
|
6
|
+
import { type AudioEffects } from "./audio_effects.js";
|
|
7
|
+
export interface SoundOptions {
|
|
8
|
+
/** Heard from this spot, or from this ped, player or vehicle (following it). Left out: everywhere, as is. */
|
|
9
|
+
at?: Vector3 | Ped | Player | Vehicle;
|
|
10
|
+
/** How far it carries (m) when it has a place. Default 30. */
|
|
11
|
+
range?: number;
|
|
12
|
+
/** Plays until `stop()`. */
|
|
13
|
+
loop?: boolean;
|
|
14
|
+
/** 0-1, of each player's sound effects volume. Default 1. */
|
|
15
|
+
volume?: number;
|
|
16
|
+
/** Who hears it. Default everyone (players joining later too, while it loops). */
|
|
17
|
+
to?: readonly Player[];
|
|
18
|
+
/** Through audio effects: a preset (`"phone"`, `"hall"`...) or stages layered. See `AudioEffects`. */
|
|
19
|
+
effects?: AudioEffects;
|
|
20
|
+
/**
|
|
21
|
+
* Positioned: its highs fade with distance, and it's muffled with a
|
|
22
|
+
* building between it and the listener. Default `server.audio.realism`.
|
|
23
|
+
*/
|
|
24
|
+
realism?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Speech (dialogue, a phone call): the radio goes quiet while it plays
|
|
27
|
+
* and it's near enough to hear, as for the game's mission dialogue.
|
|
28
|
+
*/
|
|
29
|
+
speech?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** A sound playing (`server.audio.play()`). */
|
|
32
|
+
export declare class Sound {
|
|
33
|
+
#private;
|
|
34
|
+
readonly id: number;
|
|
35
|
+
readonly file: string;
|
|
36
|
+
/** How long the file plays (ms), read from the server's copy; undefined if it can't be read. */
|
|
37
|
+
readonly duration: number | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves when it's over: when it has played through (by its duration,
|
|
40
|
+
* from when the server sent it), or is stopped. A looping one, when
|
|
41
|
+
* stopped.
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* await server.audio.play("audio/caller.mp3", { to: [player] }).done;
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
readonly done: Promise<void>;
|
|
48
|
+
/** @internal */
|
|
49
|
+
constructor(id: number, file: string, duration: number | undefined, loop: boolean, stop: (s: Sound) => void);
|
|
50
|
+
/** False once it has played through or been stopped. */
|
|
51
|
+
get playing(): boolean;
|
|
52
|
+
stop(): void;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A line as subtitles: its sentences, short ones together, up to about
|
|
56
|
+
* `max` characters each (default 70).
|
|
57
|
+
*/
|
|
58
|
+
export declare function splitSubtitles(text: string, max?: number): string[];
|
|
59
|
+
export interface SpeakOptions extends Omit<SoundOptions, "loop"> {
|
|
60
|
+
/**
|
|
61
|
+
* What's said, shown as subtitles in step with the recording: a string
|
|
62
|
+
* (split into sentence-sized subtitles), or the subtitles one by one.
|
|
63
|
+
* Each shows for its share of the recording, by length.
|
|
64
|
+
*/
|
|
65
|
+
subtitle?: string | readonly string[];
|
|
66
|
+
/** Who sees the subtitles. Default: `to`, else everyone within `range` of `at`, else everyone. */
|
|
67
|
+
subtitleTo?: readonly Player[];
|
|
68
|
+
/** Before each subtitle: a colour code such as `"~y~"`. */
|
|
69
|
+
prefix?: string;
|
|
70
|
+
/** Through the glass of `at` (a vehicle): muffled, as someone shut inside it. */
|
|
71
|
+
muffled?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** A line being spoken (`server.audio.speak()`). */
|
|
74
|
+
export interface Speech {
|
|
75
|
+
readonly sound: Sound;
|
|
76
|
+
/** Resolves when it's been said (or stopped). */
|
|
77
|
+
readonly done: Promise<void>;
|
|
78
|
+
/** Stops it and its subtitles. */
|
|
79
|
+
stop(): void;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Sounds from client files (MP3, WAV, FLAC) on players' games:
|
|
83
|
+
* `server.audio`. Positioned ones are heard from where they are, fading
|
|
84
|
+
* with distance, as the game's own sounds.
|
|
85
|
+
*/
|
|
86
|
+
export declare class ServerAudio {
|
|
87
|
+
#private;
|
|
88
|
+
/**
|
|
89
|
+
* The default for positioned sounds' `realism` (highs fading with
|
|
90
|
+
* distance, muffled behind buildings). Default false.
|
|
91
|
+
*/
|
|
92
|
+
realism: boolean;
|
|
93
|
+
/** @internal */
|
|
94
|
+
constructor(native: NativeServer, players: () => Player[], clientFiles?: string);
|
|
95
|
+
/**
|
|
96
|
+
* How long a client file plays (ms), from the server's copy (the
|
|
97
|
+
* `clientFiles` directory); undefined if it isn't there or isn't a sound.
|
|
98
|
+
*/
|
|
99
|
+
duration(file: string): number | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* Plays a recorded line with its subtitles timed to it, as speech (the
|
|
102
|
+
* radio ducks under it). `at` a ped or vehicle: from them (`muffled`:
|
|
103
|
+
* from inside the vehicle, through the glass).
|
|
104
|
+
*
|
|
105
|
+
* ```ts
|
|
106
|
+
* await server.audio.speak("audio/benny-my-car.mp3", { at: benny, subtitle: "My car! That's my car!" }).done;
|
|
107
|
+
* await server.audio.speak("audio/caller.mp3", { to: [player], effects: "phone", subtitle: lines, prefix: "~y~" }).done;
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
speak(file: string, options?: SpeakOptions): Speech;
|
|
111
|
+
/**
|
|
112
|
+
* Plays a client file. `at` places it (a spot, or a ped, player or
|
|
113
|
+
* vehicle it follows); `to` picks who hears it.
|
|
114
|
+
*
|
|
115
|
+
* ```ts
|
|
116
|
+
* const ring = server.audio.play("audio/payphone-ring.mp3", { at: phone, loop: true, range: 25 });
|
|
117
|
+
* ring.stop();
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
play(file: string, options?: SoundOptions): Sound;
|
|
121
|
+
/**
|
|
122
|
+
* Plays one of the game's own sounds (see `GameSound`): a frontend one
|
|
123
|
+
* (the HUD's: pickups, the Pay 'n' Spray jingle), or, with `at`, one of
|
|
124
|
+
* the mission script's sounds at a spot. `to` picks who hears it
|
|
125
|
+
* (default everyone).
|
|
126
|
+
*
|
|
127
|
+
* ```ts
|
|
128
|
+
* server.audio.playGame(GameSound.CarRespray, { to: [player] });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
playGame(sound: number, options?: {
|
|
132
|
+
at?: Vector3;
|
|
133
|
+
to?: readonly Player[];
|
|
134
|
+
}): void;
|
|
135
|
+
/**
|
|
136
|
+
* Plays one of the game's mission audio, by its audio id: the recordings
|
|
137
|
+
* and effects its missions and machines load (a vending machine's clunk:
|
|
138
|
+
* 42600). From a player, ped or vehicle (following them), a spot, or
|
|
139
|
+
* (no `at`) as is. `to` picks who hears it (default everyone). Each
|
|
140
|
+
* game reads it from its own copy and plays it as the server's sounds,
|
|
141
|
+
* so `effects` work on it.
|
|
142
|
+
*
|
|
143
|
+
* ```ts
|
|
144
|
+
* server.audio.playMission(42600, { at: player });
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
playMission(id: number, options?: {
|
|
148
|
+
at?: Vector3 | Player | Ped | Vehicle;
|
|
149
|
+
range?: number;
|
|
150
|
+
to?: readonly Player[];
|
|
151
|
+
effects?: AudioEffects;
|
|
152
|
+
realism?: boolean;
|
|
153
|
+
}): void;
|
|
154
|
+
/**
|
|
155
|
+
* The effects every sound of the server's and its scripts' passes through
|
|
156
|
+
* for this player now: where they are (a hall's reverb, a tunnel's echo).
|
|
157
|
+
* `null`: none. The game's own world sounds aren't affected.
|
|
158
|
+
*
|
|
159
|
+
* ```ts
|
|
160
|
+
* server.audio.setEnvironment(player, "tunnel");
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
setEnvironment(player: Player, effects: AudioEffects | null): void;
|
|
164
|
+
/**
|
|
165
|
+
* An environment for everyone inside a zone, while they're in it (the
|
|
166
|
+
* most recently entered, where zones overlap; one set with
|
|
167
|
+
* `setEnvironment` wins over all).
|
|
168
|
+
*
|
|
169
|
+
* ```ts
|
|
170
|
+
* server.audio.setZoneEnvironment(server.createZone({ ... }), "hall");
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
setZoneEnvironment(zone: Zone, effects: AudioEffects | null): void;
|
|
174
|
+
/** @internal */
|
|
175
|
+
zoneEvent(player: Player, zone: Zone, entered: boolean): void;
|
|
176
|
+
/** @internal */
|
|
177
|
+
forget(player: Player): void;
|
|
178
|
+
/** @internal A player joined: the loops playing for everyone. */
|
|
179
|
+
replay(player: Player): void;
|
|
180
|
+
}
|