@rezless/vm 0.7.0 → 0.8.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/dist/builtins/agents.d.ts +14 -0
- package/dist/builtins/agents.d.ts.map +1 -0
- package/dist/builtins/agents.js +117 -0
- package/dist/builtins/agents.js.map +1 -0
- package/dist/builtins/animation.d.ts +46 -0
- package/dist/builtins/animation.d.ts.map +1 -0
- package/dist/builtins/animation.js +316 -0
- package/dist/builtins/animation.js.map +1 -0
- package/dist/builtins/attachments.d.ts +9 -0
- package/dist/builtins/attachments.d.ts.map +1 -0
- package/dist/builtins/attachments.js +127 -0
- package/dist/builtins/attachments.js.map +1 -0
- package/dist/builtins/base64.d.ts +8 -0
- package/dist/builtins/base64.d.ts.map +1 -1
- package/dist/builtins/base64.js +1 -1
- package/dist/builtins/base64.js.map +1 -1
- package/dist/builtins/camera.d.ts +35 -0
- package/dist/builtins/camera.d.ts.map +1 -0
- package/dist/builtins/camera.js +104 -0
- package/dist/builtins/camera.js.map +1 -0
- package/dist/builtins/collision.d.ts +21 -0
- package/dist/builtins/collision.d.ts.map +1 -0
- package/dist/builtins/collision.js +44 -0
- package/dist/builtins/collision.js.map +1 -0
- package/dist/builtins/controls.d.ts +19 -0
- package/dist/builtins/controls.d.ts.map +1 -0
- package/dist/builtins/controls.js +27 -0
- package/dist/builtins/controls.js.map +1 -0
- package/dist/builtins/damage.d.ts +24 -0
- package/dist/builtins/damage.d.ts.map +1 -0
- package/dist/builtins/damage.js +166 -0
- package/dist/builtins/damage.js.map +1 -0
- package/dist/builtins/detected.d.ts +22 -1
- package/dist/builtins/detected.d.ts.map +1 -1
- package/dist/builtins/detected.js +27 -1
- package/dist/builtins/detected.js.map +1 -1
- package/dist/builtins/dialog.d.ts +23 -0
- package/dist/builtins/dialog.d.ts.map +1 -0
- package/dist/builtins/dialog.js +69 -0
- package/dist/builtins/dialog.js.map +1 -0
- package/dist/builtins/email.d.ts +61 -0
- package/dist/builtins/email.d.ts.map +1 -0
- package/dist/builtins/email.js +121 -0
- package/dist/builtins/email.js.map +1 -0
- package/dist/builtins/environment.d.ts +27 -0
- package/dist/builtins/environment.d.ts.map +1 -0
- package/dist/builtins/environment.js +126 -0
- package/dist/builtins/environment.js.map +1 -0
- package/dist/builtins/experience.d.ts +51 -0
- package/dist/builtins/experience.d.ts.map +1 -0
- package/dist/builtins/experience.js +202 -0
- package/dist/builtins/experience.js.map +1 -0
- package/dist/builtins/game-control.d.ts +17 -0
- package/dist/builtins/game-control.d.ts.map +1 -0
- package/dist/builtins/game-control.js +21 -0
- package/dist/builtins/game-control.js.map +1 -0
- package/dist/builtins/give.d.ts +16 -0
- package/dist/builtins/give.d.ts.map +1 -0
- package/dist/builtins/give.js +137 -0
- package/dist/builtins/give.js.map +1 -0
- package/dist/builtins/ground.d.ts +18 -0
- package/dist/builtins/ground.d.ts.map +1 -0
- package/dist/builtins/ground.js +83 -0
- package/dist/builtins/ground.js.map +1 -0
- package/dist/builtins/http.d.ts +99 -4
- package/dist/builtins/http.d.ts.map +1 -1
- package/dist/builtins/http.js +216 -6
- package/dist/builtins/http.js.map +1 -1
- package/dist/builtins/index.d.ts.map +1 -1
- package/dist/builtins/index.js +235 -10
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/inventory.d.ts +1 -0
- package/dist/builtins/inventory.d.ts.map +1 -1
- package/dist/builtins/inventory.js +1 -1
- package/dist/builtins/inventory.js.map +1 -1
- package/dist/builtins/land.d.ts +29 -0
- package/dist/builtins/land.d.ts.map +1 -0
- package/dist/builtins/land.js +339 -0
- package/dist/builtins/land.js.map +1 -0
- package/dist/builtins/links.d.ts +20 -0
- package/dist/builtins/links.d.ts.map +1 -0
- package/dist/builtins/links.js +53 -0
- package/dist/builtins/links.js.map +1 -0
- package/dist/builtins/linksetdata.d.ts +5 -0
- package/dist/builtins/linksetdata.d.ts.map +1 -1
- package/dist/builtins/linksetdata.js +16 -0
- package/dist/builtins/linksetdata.js.map +1 -1
- package/dist/builtins/lists.d.ts +6 -0
- package/dist/builtins/lists.d.ts.map +1 -1
- package/dist/builtins/lists.js +19 -6
- package/dist/builtins/lists.js.map +1 -1
- package/dist/builtins/math.d.ts +16 -0
- package/dist/builtins/math.d.ts.map +1 -1
- package/dist/builtins/math.js +74 -0
- package/dist/builtins/math.js.map +1 -1
- package/dist/builtins/media.d.ts +8 -0
- package/dist/builtins/media.d.ts.map +1 -0
- package/dist/builtins/media.js +167 -0
- package/dist/builtins/media.js.map +1 -0
- package/dist/builtins/motion.d.ts +79 -0
- package/dist/builtins/motion.d.ts.map +1 -0
- package/dist/builtins/motion.js +396 -0
- package/dist/builtins/motion.js.map +1 -0
- package/dist/builtins/notecard-search.d.ts +15 -0
- package/dist/builtins/notecard-search.d.ts.map +1 -0
- package/dist/builtins/notecard-search.js +374 -0
- package/dist/builtins/notecard-search.js.map +1 -0
- package/dist/builtins/object-animation.d.ts +21 -0
- package/dist/builtins/object-animation.d.ts.map +1 -0
- package/dist/builtins/object-animation.js +193 -0
- package/dist/builtins/object-animation.js.map +1 -0
- package/dist/builtins/object-reads.d.ts +39 -0
- package/dist/builtins/object-reads.d.ts.map +1 -0
- package/dist/builtins/object-reads.js +332 -0
- package/dist/builtins/object-reads.js.map +1 -0
- package/dist/builtins/object.d.ts +6 -0
- package/dist/builtins/object.d.ts.map +1 -1
- package/dist/builtins/object.js +19 -3
- package/dist/builtins/object.js.map +1 -1
- package/dist/builtins/particles.d.ts +4 -0
- package/dist/builtins/particles.d.ts.map +1 -0
- package/dist/builtins/particles.js +65 -0
- package/dist/builtins/particles.js.map +1 -0
- package/dist/builtins/pathfinding.d.ts +39 -0
- package/dist/builtins/pathfinding.d.ts.map +1 -0
- package/dist/builtins/pathfinding.js +83 -0
- package/dist/builtins/pathfinding.js.map +1 -0
- package/dist/builtins/permissions.d.ts.map +1 -1
- package/dist/builtins/permissions.js +6 -0
- package/dist/builtins/permissions.js.map +1 -1
- package/dist/builtins/physics.d.ts +49 -0
- package/dist/builtins/physics.d.ts.map +1 -0
- package/dist/builtins/physics.js +47 -0
- package/dist/builtins/physics.js.map +1 -0
- package/dist/builtins/prim-accessors.d.ts +3 -0
- package/dist/builtins/prim-accessors.d.ts.map +1 -1
- package/dist/builtins/prim-accessors.js +27 -7
- package/dist/builtins/prim-accessors.js.map +1 -1
- package/dist/builtins/raycast.d.ts +3 -0
- package/dist/builtins/raycast.d.ts.map +1 -0
- package/dist/builtins/raycast.js +65 -0
- package/dist/builtins/raycast.js.map +1 -0
- package/dist/builtins/region.d.ts +31 -0
- package/dist/builtins/region.d.ts.map +1 -0
- package/dist/builtins/region.js +125 -0
- package/dist/builtins/region.js.map +1 -0
- package/dist/builtins/remote-load.d.ts +3 -0
- package/dist/builtins/remote-load.d.ts.map +1 -0
- package/dist/builtins/remote-load.js +53 -0
- package/dist/builtins/remote-load.js.map +1 -0
- package/dist/builtins/rez.d.ts +10 -0
- package/dist/builtins/rez.d.ts.map +1 -0
- package/dist/builtins/rez.js +255 -0
- package/dist/builtins/rez.js.map +1 -0
- package/dist/builtins/rsa.d.ts +4 -0
- package/dist/builtins/rsa.d.ts.map +1 -0
- package/dist/builtins/rsa.js +44 -0
- package/dist/builtins/rsa.js.map +1 -0
- package/dist/builtins/script-control.d.ts +6 -2
- package/dist/builtins/script-control.d.ts.map +1 -1
- package/dist/builtins/script-control.js +9 -4
- package/dist/builtins/script-control.js.map +1 -1
- package/dist/builtins/sensor.d.ts +11 -0
- package/dist/builtins/sensor.d.ts.map +1 -0
- package/dist/builtins/sensor.js +145 -0
- package/dist/builtins/sensor.js.map +1 -0
- package/dist/builtins/sit.d.ts +23 -0
- package/dist/builtins/sit.d.ts.map +1 -0
- package/dist/builtins/sit.js +150 -0
- package/dist/builtins/sit.js.map +1 -0
- package/dist/builtins/sound.d.ts +19 -0
- package/dist/builtins/sound.d.ts.map +1 -0
- package/dist/builtins/sound.js +113 -0
- package/dist/builtins/sound.js.map +1 -0
- package/dist/builtins/targets.d.ts +12 -0
- package/dist/builtins/targets.d.ts.map +1 -0
- package/dist/builtins/targets.js +121 -0
- package/dist/builtins/targets.js.map +1 -0
- package/dist/builtins/time.d.ts +12 -0
- package/dist/builtins/time.d.ts.map +1 -1
- package/dist/builtins/time.js +32 -4
- package/dist/builtins/time.js.map +1 -1
- package/dist/builtins/vehicles.d.ts +22 -0
- package/dist/builtins/vehicles.d.ts.map +1 -0
- package/dist/builtins/vehicles.js +78 -0
- package/dist/builtins/vehicles.js.map +1 -0
- package/dist/builtins/viewer.d.ts +39 -0
- package/dist/builtins/viewer.d.ts.map +1 -0
- package/dist/builtins/viewer.js +74 -0
- package/dist/builtins/viewer.js.map +1 -0
- package/dist/clock.d.ts +29 -1
- package/dist/clock.d.ts.map +1 -1
- package/dist/clock.js +89 -5
- package/dist/clock.js.map +1 -1
- package/dist/experience.d.ts +39 -0
- package/dist/experience.d.ts.map +1 -0
- package/dist/experience.js +38 -0
- package/dist/experience.js.map +1 -0
- package/dist/index.d.ts +20 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interpreter.d.ts +0 -1
- package/dist/interpreter.d.ts.map +1 -1
- package/dist/interpreter.js.map +1 -1
- package/dist/inventory.d.ts +2 -0
- package/dist/inventory.d.ts.map +1 -1
- package/dist/inventory.js +2 -0
- package/dist/inventory.js.map +1 -1
- package/dist/linkset.d.ts +139 -11
- package/dist/linkset.d.ts.map +1 -1
- package/dist/linkset.js +376 -24
- package/dist/linkset.js.map +1 -1
- package/dist/prim-params.d.ts.map +1 -1
- package/dist/prim-params.js +11 -3
- package/dist/prim-params.js.map +1 -1
- package/dist/prim.d.ts +94 -4
- package/dist/prim.d.ts.map +1 -1
- package/dist/prim.js +94 -5
- package/dist/prim.js.map +1 -1
- package/dist/runtime.d.ts +41 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/script.d.ts +147 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +373 -19
- package/dist/script.js.map +1 -1
- package/dist/values/ops.d.ts +3 -0
- package/dist/values/ops.d.ts.map +1 -1
- package/dist/values/ops.js +2 -2
- package/dist/values/ops.js.map +1 -1
- package/dist/world.d.ts +247 -1
- package/dist/world.d.ts.map +1 -1
- package/dist/world.js +318 -6
- package/dist/world.js.map +1 -1
- package/package.json +2 -2
package/dist/script.js
CHANGED
|
@@ -1,23 +1,37 @@
|
|
|
1
|
+
import { clearTargetMotion } from './builtins/motion.js';
|
|
2
|
+
import { copyGameControl, gameControlAvatars } from './builtins/game-control.js';
|
|
3
|
+
import { PERMISSION_GAME_CONTROL } from './generated/constants.js';
|
|
4
|
+
import { clearTargets } from './builtins/targets.js';
|
|
5
|
+
import { withCollisionFilter } from './builtins/collision.js';
|
|
6
|
+
import { stopSensors } from './builtins/sensor.js';
|
|
7
|
+
import { enterDamageEvent, leaveDamageEvent, combatAvatars, } from './builtins/damage.js';
|
|
8
|
+
import { sendIncomingHttp } from './builtins/http.js';
|
|
9
|
+
import { deliverEmail, emailQueue } from './builtins/email.js';
|
|
10
|
+
import { characterFor, pathUpdate, setNavigationResults } from './builtins/pathfinding.js';
|
|
1
11
|
import { truncateUtf8 } from './values/utf8.js';
|
|
12
|
+
import { agentPresence } from './builtins/viewer.js';
|
|
13
|
+
import { defaultCamera } from './builtins/camera.js';
|
|
14
|
+
import { animationAvatars, playingAnimations, setAnimationState } from './builtins/animation.js';
|
|
2
15
|
import { HaltSignal, ResetScriptSignal } from './builtins/object.js';
|
|
3
16
|
import { Mulberry32 } from './random.js';
|
|
4
17
|
import { inventoryItemName } from './inventory.js';
|
|
18
|
+
import { typedList, listElementTypes } from './values/list-types.js';
|
|
5
19
|
import { NULL_KEY } from './values/types.js';
|
|
6
20
|
import { execHandler, StateChangeSignal } from './interpreter.js';
|
|
7
21
|
import { buildCoveragePlan, CoverageCollector } from './coverage.js';
|
|
8
22
|
import { defaultEvalFor, rot, toFloat32Value, vec } from './values/types.js';
|
|
9
|
-
import { typedList } from './values/list-types.js';
|
|
10
23
|
import { Env } from './env.js';
|
|
11
|
-
import { ScriptClockView } from './clock.js';
|
|
24
|
+
import { DELAYED_EVENTS, ScriptClockView } from './clock.js';
|
|
12
25
|
import { Linkset } from './linkset.js';
|
|
13
26
|
import { Prim } from './prim.js';
|
|
14
27
|
import { EVENT_SPECS } from './generated/events.js';
|
|
15
28
|
import { CONSTANT_TABLE } from './generated/constants_table.js';
|
|
16
|
-
import { DEBUG_CHANNEL, HTTP_BODY_TRUNCATED } from './generated/constants.js';
|
|
29
|
+
import { DEBUG_CHANNEL, HTTP_BODY_TRUNCATED, PERMISSION_TAKE_CONTROLS, XP_ERROR_NOT_PERMITTED, } from './generated/constants.js';
|
|
17
30
|
import { own } from './own.js';
|
|
18
31
|
import { deterministicKey } from './keys.js';
|
|
19
32
|
import { Memory, RuntimeError, codeBytes } from './memory.js';
|
|
20
33
|
import { invalidPrograms } from './typecheck.js';
|
|
34
|
+
import { grantExperience } from './builtins/experience.js';
|
|
21
35
|
/**
|
|
22
36
|
* Public handle for a loaded LSL script. Tests interact almost entirely
|
|
23
37
|
* with this surface: drive events via fire(), inspect chat / calls,
|
|
@@ -25,6 +39,7 @@ import { invalidPrograms } from './typecheck.js';
|
|
|
25
39
|
*/
|
|
26
40
|
export class Script {
|
|
27
41
|
ast;
|
|
42
|
+
options;
|
|
28
43
|
state;
|
|
29
44
|
mocks = Object.create(null);
|
|
30
45
|
globals;
|
|
@@ -38,22 +53,28 @@ export class Script {
|
|
|
38
53
|
/** Per-script clock facade (timer state + time reference). */
|
|
39
54
|
clockView;
|
|
40
55
|
host;
|
|
41
|
-
linkset;
|
|
42
56
|
scriptName;
|
|
43
57
|
/** When false, drainQueue parks events instead of delivering them. */
|
|
44
58
|
running = true;
|
|
45
59
|
/** Events parked while `running === false`; replayed when re-enabled. */
|
|
46
60
|
parkedEvents = [];
|
|
61
|
+
/** llGetStartParameter: the linkset's start parameter until the script resets, then 0. */
|
|
62
|
+
startParameter;
|
|
47
63
|
/** The handler chain suspended in llSleep or a builtin delay, if any. */
|
|
48
64
|
task = null;
|
|
65
|
+
/** Virtual time this script last started a handler or woke from a sleep. */
|
|
66
|
+
resumedAt = 0;
|
|
49
67
|
constructor(ast, options = {}) {
|
|
50
68
|
this.ast = ast;
|
|
69
|
+
this.options = options;
|
|
51
70
|
// Auto-wrap: if no host prim was provided, create a default 1-prim linkset.
|
|
52
71
|
let host = options.host;
|
|
53
72
|
if (!host) {
|
|
54
73
|
const linkset = new Linkset({
|
|
55
74
|
owner: options.owner ?? NULL_KEY,
|
|
56
75
|
startUnixTime: options.startUnixTime ?? 0,
|
|
76
|
+
startParameter: options.startParameter ?? 0,
|
|
77
|
+
startString: options.startString ?? '',
|
|
57
78
|
});
|
|
58
79
|
const prim = new Prim({
|
|
59
80
|
key: options.objectKey ??
|
|
@@ -67,15 +88,14 @@ export class Script {
|
|
|
67
88
|
if (!host.linkset) {
|
|
68
89
|
throw new Error('Script host prim must belong to a Linkset');
|
|
69
90
|
}
|
|
70
|
-
this.
|
|
91
|
+
this.startParameter = this.linkset.startParameter;
|
|
71
92
|
this.scriptName = inventoryItemName(options.scriptName ?? deriveScriptName(options.filename));
|
|
72
93
|
this.clockView = new ScriptClockView(this.linkset.clock, () => this);
|
|
73
|
-
const linkset = this.linkset;
|
|
74
94
|
const prim = host;
|
|
75
95
|
const scriptName = this.scriptName;
|
|
76
96
|
const identity = {
|
|
77
97
|
get owner() {
|
|
78
|
-
return linkset.owner;
|
|
98
|
+
return prim.linkset.owner;
|
|
79
99
|
},
|
|
80
100
|
get objectKey() {
|
|
81
101
|
return prim.key;
|
|
@@ -102,6 +122,13 @@ export class Script {
|
|
|
102
122
|
clock: this.clockView,
|
|
103
123
|
httpRequests: [],
|
|
104
124
|
httpKeyCounter: 0,
|
|
125
|
+
urls: [],
|
|
126
|
+
urlPolicy: 'grant',
|
|
127
|
+
urlCounter: 0,
|
|
128
|
+
incomingHttp: [],
|
|
129
|
+
httpInCounter: 0,
|
|
130
|
+
emails: [],
|
|
131
|
+
targetedEmails: [],
|
|
105
132
|
listens: [],
|
|
106
133
|
listenHandleCounter: 0,
|
|
107
134
|
random: new Mulberry32(options.randomSeed ?? 1),
|
|
@@ -110,9 +137,25 @@ export class Script {
|
|
|
110
137
|
dataserverRequests: [],
|
|
111
138
|
dataserverKeyCounter: 0,
|
|
112
139
|
detectedStack: [],
|
|
113
|
-
linksetData
|
|
140
|
+
get linksetData() {
|
|
141
|
+
return prim.linkset.linksetData;
|
|
142
|
+
},
|
|
114
143
|
appearance: this.host.appearance,
|
|
144
|
+
gameControlSnapshot: null,
|
|
115
145
|
permissions: { granted: 0, agent: NULL_KEY, requests: [] },
|
|
146
|
+
experience: options.experience ?? null,
|
|
147
|
+
experienceRequests: [],
|
|
148
|
+
controls: { taken: null, level: 0 },
|
|
149
|
+
camera: defaultCamera(),
|
|
150
|
+
cameraParams: new Map(),
|
|
151
|
+
get animations() {
|
|
152
|
+
return animationAvatars(prim.linkset);
|
|
153
|
+
},
|
|
154
|
+
inventoryOffers: [],
|
|
155
|
+
viewerRequests: [],
|
|
156
|
+
teleportRequests: [],
|
|
157
|
+
agentPresence: agentPresence(prim.linkset),
|
|
158
|
+
dialogs: [],
|
|
116
159
|
lifecycle: {
|
|
117
160
|
dead: false,
|
|
118
161
|
dying: false,
|
|
@@ -144,7 +187,21 @@ export class Script {
|
|
|
144
187
|
host.addScript(this);
|
|
145
188
|
}
|
|
146
189
|
}
|
|
190
|
+
/** A state transition leaves on_damage before lifecycle handlers execute. */
|
|
191
|
+
clearDamageAdjustment() {
|
|
192
|
+
const context = this.state.detectedStack.at(-1);
|
|
193
|
+
if (context?.event === 'on_damage')
|
|
194
|
+
context.event = '';
|
|
195
|
+
}
|
|
196
|
+
/** Configure an avatar's combat health, parcel and attachment scripts. */
|
|
197
|
+
setCombatAvatar(id, avatar) {
|
|
198
|
+
combatAvatars(this).set(id.toLowerCase(), { ...avatar, region: this.linkset.region });
|
|
199
|
+
}
|
|
147
200
|
/** Current virtual time in milliseconds since linkset construction. */
|
|
201
|
+
/** The linkset holding this script's prim; it changes when the prim is linked or unlinked. */
|
|
202
|
+
get linkset() {
|
|
203
|
+
return this.host.linkset;
|
|
204
|
+
}
|
|
148
205
|
get now() {
|
|
149
206
|
return this.linkset.clock.now;
|
|
150
207
|
}
|
|
@@ -187,6 +244,18 @@ export class Script {
|
|
|
187
244
|
callsOf(name) {
|
|
188
245
|
return this.state.calls.filter((c) => c.name === name);
|
|
189
246
|
}
|
|
247
|
+
/** The object's character, or null before creation and after deletion. */
|
|
248
|
+
get character() {
|
|
249
|
+
return characterFor(this);
|
|
250
|
+
}
|
|
251
|
+
/** Set fixed navigation query answers. These fixtures survive script resets. */
|
|
252
|
+
setNavigationResults(results) {
|
|
253
|
+
setNavigationResults(this, results);
|
|
254
|
+
}
|
|
255
|
+
/** Raise path_update on every script in the character's root prim. */
|
|
256
|
+
pathUpdate(type, reserved = []) {
|
|
257
|
+
pathUpdate(this, type, reserved);
|
|
258
|
+
}
|
|
190
259
|
/** Captured outgoing HTTP requests from `llHTTPRequest`. */
|
|
191
260
|
get httpRequests() {
|
|
192
261
|
return this.state.httpRequests;
|
|
@@ -224,6 +293,46 @@ export class Script {
|
|
|
224
293
|
throw new Error('no HTTP request to respond to');
|
|
225
294
|
this.respondToHttp(req.key, response);
|
|
226
295
|
}
|
|
296
|
+
/** URLs the script holds from llRequestURL / llRequestSecureURL, oldest first. */
|
|
297
|
+
get urls() {
|
|
298
|
+
return this.state.urls;
|
|
299
|
+
}
|
|
300
|
+
/** Grant later llRequestURL / llRequestSecureURL calls (the default). */
|
|
301
|
+
grantUrls() {
|
|
302
|
+
this.state.urlPolicy = 'grant';
|
|
303
|
+
}
|
|
304
|
+
/** Deny later llRequestURL / llRequestSecureURL calls with URL_REQUEST_DENIED. */
|
|
305
|
+
denyUrls() {
|
|
306
|
+
this.state.urlPolicy = 'deny';
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Send a request to one of the script's URLs, with a path and query after
|
|
310
|
+
* it if you like, and fire `http_request`. The returned request's
|
|
311
|
+
* `response` holds what the script answered with llHTTPResponse.
|
|
312
|
+
*/
|
|
313
|
+
sendHttpRequest(url, init = {}) {
|
|
314
|
+
return sendIncomingHttp(this, this.state, url, init);
|
|
315
|
+
}
|
|
316
|
+
/** Mail the script sent with llEmail, oldest first. */
|
|
317
|
+
get emails() {
|
|
318
|
+
return this.state.emails;
|
|
319
|
+
}
|
|
320
|
+
/** Mail the script sent with llTargetedEmail, oldest first. */
|
|
321
|
+
get targetedEmails() {
|
|
322
|
+
return this.state.targetedEmails;
|
|
323
|
+
}
|
|
324
|
+
/** Mail queued on the script's prim, oldest first, waiting for llGetNextEmail. */
|
|
325
|
+
get inbox() {
|
|
326
|
+
return emailQueue(this.host);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Queue mail on the script's prim. It fires `email` only when a script in
|
|
330
|
+
* the prim asks for it with llGetNextEmail. Returns false, queuing
|
|
331
|
+
* nothing, when the prim already holds 101 mails.
|
|
332
|
+
*/
|
|
333
|
+
deliverEmail(init) {
|
|
334
|
+
return deliverEmail(this, init);
|
|
335
|
+
}
|
|
227
336
|
/** Currently active listen registrations (from `llListen`). */
|
|
228
337
|
get listens() {
|
|
229
338
|
return this.state.listens;
|
|
@@ -280,6 +389,8 @@ export class Script {
|
|
|
280
389
|
const granted = req.permissions & (permissions ?? req.permissions);
|
|
281
390
|
this.state.permissions.granted = granted;
|
|
282
391
|
this.state.permissions.agent = req.agent;
|
|
392
|
+
if (!(granted & PERMISSION_TAKE_CONTROLS))
|
|
393
|
+
this.state.controls.taken = null;
|
|
283
394
|
const started = this.handles('run_time_permissions') ? [this] : [];
|
|
284
395
|
this.clockView.schedule(this.now, 'run_time_permissions', { perm: granted });
|
|
285
396
|
this.linkset.settle(started);
|
|
@@ -288,10 +399,170 @@ export class Script {
|
|
|
288
399
|
denyPermissions() {
|
|
289
400
|
this.grantPermissions(0);
|
|
290
401
|
}
|
|
402
|
+
/** The experience the script was compiled with, or null. */
|
|
403
|
+
get experience() {
|
|
404
|
+
return this.state.experience;
|
|
405
|
+
}
|
|
406
|
+
/** llRequestExperiencePermissions calls waiting for an answer, oldest first. */
|
|
407
|
+
get experienceRequests() {
|
|
408
|
+
return this.state.experienceRequests;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Accept the most recent unanswered llRequestExperiencePermissions: the
|
|
412
|
+
* agent joins the experience, the script holds the experience permissions
|
|
413
|
+
* for that agent, and `experience_permissions` fires.
|
|
414
|
+
*/
|
|
415
|
+
grantExperiencePermissions() {
|
|
416
|
+
const req = this.answerExperienceRequest();
|
|
417
|
+
const started = this.handles('experience_permissions') ? [this] : [];
|
|
418
|
+
grantExperience(this.state, req.agent);
|
|
419
|
+
this.linkset.settle(started);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Decline the most recent unanswered llRequestExperiencePermissions:
|
|
423
|
+
* `experience_permissions_denied` fires with `reason`
|
|
424
|
+
* (XP_ERROR_NOT_PERMITTED by default).
|
|
425
|
+
*/
|
|
426
|
+
denyExperiencePermissions(reason = XP_ERROR_NOT_PERMITTED) {
|
|
427
|
+
const req = this.answerExperienceRequest();
|
|
428
|
+
this.fireAndSettle('experience_permissions_denied', { agent_id: req.agent, reason });
|
|
429
|
+
}
|
|
430
|
+
answerExperienceRequest() {
|
|
431
|
+
const req = [...this.state.experienceRequests].reverse().find((r) => !r.answered);
|
|
432
|
+
if (!req)
|
|
433
|
+
throw new Error('no pending experience permission request');
|
|
434
|
+
req.answered = true;
|
|
435
|
+
return req;
|
|
436
|
+
}
|
|
437
|
+
fireAndSettle(event, payload) {
|
|
438
|
+
const started = this.handles(event) ? [this] : [];
|
|
439
|
+
this.clockView.schedule(this.now, event, payload);
|
|
440
|
+
this.linkset.settle(started);
|
|
441
|
+
}
|
|
442
|
+
/** Controls taken with llTakeControls, or null when none are. */
|
|
443
|
+
get controls() {
|
|
444
|
+
return this.state.controls.taken;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Have the permission agent hold exactly the controls in `level` (CONTROL_*
|
|
448
|
+
* bits; 0 lets go of all). Fires `control` with the held and just-changed
|
|
449
|
+
* bits among those taken, if the script took controls with accept TRUE and
|
|
450
|
+
* any of them is held or changed.
|
|
451
|
+
*/
|
|
452
|
+
sendControl(level) {
|
|
453
|
+
const edge = level ^ this.state.controls.level;
|
|
454
|
+
this.state.controls.level = level;
|
|
455
|
+
const taken = this.state.controls.taken;
|
|
456
|
+
if (!taken?.accept)
|
|
457
|
+
return;
|
|
458
|
+
const held = level & taken.controls;
|
|
459
|
+
const changed = edge & taken.controls;
|
|
460
|
+
if (held === 0 && changed === 0)
|
|
461
|
+
return;
|
|
462
|
+
const started = this.handles('control') ? [this] : [];
|
|
463
|
+
this.clockView.schedule(this.now, 'control', { id: this.state.permissions.agent, level: held, edge: changed });
|
|
464
|
+
this.linkset.settle(started);
|
|
465
|
+
}
|
|
466
|
+
/** Set an avatar's semantic input fixture, shared by scripts in this World. */
|
|
467
|
+
setGameControl(agent, input) {
|
|
468
|
+
const avatars = gameControlAvatars(this.linkset);
|
|
469
|
+
if (input === null)
|
|
470
|
+
avatars.delete(agent.toLowerCase());
|
|
471
|
+
else
|
|
472
|
+
avatars.set(agent.toLowerCase(), copyGameControl(input));
|
|
473
|
+
}
|
|
474
|
+
/** Queue raw input and a copy of the avatar's semantic fixture for this script. */
|
|
475
|
+
sendGameControl(agent, raw = {}) {
|
|
476
|
+
if (!(this.state.permissions.granted & PERMISSION_GAME_CONTROL)
|
|
477
|
+
|| agent.toLowerCase() !== this.state.permissions.agent.toLowerCase())
|
|
478
|
+
return;
|
|
479
|
+
const input = gameControlAvatars(this.linkset).get(agent.toLowerCase());
|
|
480
|
+
const snapshot = input ? { ...copyGameControl(input), agent: agent.toLowerCase() } : null;
|
|
481
|
+
this.fire('game_control', {
|
|
482
|
+
id: agent, button_levels: (raw.buttons ?? 0) | 0,
|
|
483
|
+
axes: typedList((raw.axes ?? []).map(Math.fround), (raw.axes ?? []).map(() => 'float')),
|
|
484
|
+
gameControlSnapshot: snapshot,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
/** Set the permission agent's camera that llGetCameraPos / Rot / FOV / Aspect report. */
|
|
488
|
+
setCamera(camera) {
|
|
489
|
+
const { pos, rot: r, fov, aspect } = { ...this.state.camera, ...camera };
|
|
490
|
+
this.state.camera = {
|
|
491
|
+
pos: vec(pos.x, pos.y, pos.z),
|
|
492
|
+
rot: rot(r.x, r.y, r.z, r.s),
|
|
493
|
+
fov: Math.fround(fov),
|
|
494
|
+
aspect: Math.fround(aspect),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
/** Camera rules set with llSetCameraParams, by rule. */
|
|
498
|
+
get cameraParams() {
|
|
499
|
+
return this.state.cameraParams;
|
|
500
|
+
}
|
|
501
|
+
/** Register an avatar in this region and set its default locomotion animation. */
|
|
502
|
+
setAnimationState(agent, state) {
|
|
503
|
+
setAnimationState(this.state.animations, agent, state);
|
|
504
|
+
}
|
|
505
|
+
/** Playing animation UUIDs for an avatar, in UUID order. Returns a fresh array. */
|
|
506
|
+
getPlayingAnimations(agent) {
|
|
507
|
+
return playingAnimations(this.state.animations, agent);
|
|
508
|
+
}
|
|
509
|
+
/** Inventory offers and completed prim deliveries, oldest first. */
|
|
510
|
+
get inventoryOffers() {
|
|
511
|
+
return this.state.inventoryOffers;
|
|
512
|
+
}
|
|
513
|
+
/** Teleport attempts made by this script, oldest first. */
|
|
514
|
+
get teleportRequests() {
|
|
515
|
+
return this.state.teleportRequests;
|
|
516
|
+
}
|
|
517
|
+
/** URL/map requests and rejected floater calls, oldest first. */
|
|
518
|
+
get viewerRequests() {
|
|
519
|
+
return this.state.viewerRequests;
|
|
520
|
+
}
|
|
521
|
+
/** Override whether an avatar is present in this region, for every script sharing it. */
|
|
522
|
+
setAgentInRegion(agent, present = true) {
|
|
523
|
+
this.state.agentPresence.set(agent.toLowerCase(), present);
|
|
524
|
+
}
|
|
525
|
+
/** Dialogs and text boxes this script showed with llDialog / llTextBox, oldest first. */
|
|
526
|
+
get dialogs() {
|
|
527
|
+
return this.state.dialogs;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Answer a dialog as its avatar: the most recent unanswered one, or
|
|
531
|
+
* `opts.dialog`. The avatar says `text` on the dialog's channel, heard by
|
|
532
|
+
* listens within 20 m of the prim that showed it. A llDialog answer must
|
|
533
|
+
* be one of its buttons; a text box takes any text.
|
|
534
|
+
*/
|
|
535
|
+
answerDialog(text, opts = {}) {
|
|
536
|
+
const dialog = opts.dialog ?? [...this.state.dialogs].reverse().find((d) => !d.answered);
|
|
537
|
+
if (!dialog)
|
|
538
|
+
throw new Error('no unanswered dialog');
|
|
539
|
+
if (!this.state.dialogs.includes(dialog))
|
|
540
|
+
throw new Error('that dialog was not shown by this script');
|
|
541
|
+
if (dialog.answered)
|
|
542
|
+
throw new Error('that dialog was already answered');
|
|
543
|
+
if (dialog.kind === 'dialog' && !dialog.buttons.includes(text)) {
|
|
544
|
+
throw new Error(`'${text}' is not a button on the dialog: ${dialog.buttons.join(', ')}`);
|
|
545
|
+
}
|
|
546
|
+
dialog.answered = true;
|
|
547
|
+
const chat = { channel: dialog.channel, name: opts.name ?? '', key: dialog.agent, message: text };
|
|
548
|
+
this.linkset.settle(this.linkset.routeAvatarChat(this.host, chat).filter((s) => s.handles('listen')));
|
|
549
|
+
}
|
|
291
550
|
/** Currently displayed floating text (from llSetText). null if unset. */
|
|
292
551
|
get text() {
|
|
293
552
|
return this.state.appearance.text;
|
|
294
553
|
}
|
|
554
|
+
/** The prim's llSetTouchText text, '' if unset. */
|
|
555
|
+
get touchText() {
|
|
556
|
+
return this.host.touchText;
|
|
557
|
+
}
|
|
558
|
+
/** The prim's llSetSitText text, '' if unset. */
|
|
559
|
+
get sitText() {
|
|
560
|
+
return this.host.sitText;
|
|
561
|
+
}
|
|
562
|
+
/** The prim's llSetPayPrice arguments, null if unset. */
|
|
563
|
+
get payPrice() {
|
|
564
|
+
return this.host.payPrice;
|
|
565
|
+
}
|
|
295
566
|
/** Object description from llSetObjectDesc. */
|
|
296
567
|
get objectDesc() {
|
|
297
568
|
return this.state.appearance.description;
|
|
@@ -312,6 +583,10 @@ export class Script {
|
|
|
312
583
|
this.state.linksetData.set(k, { value: v.value, password: v.password ?? '' });
|
|
313
584
|
}
|
|
314
585
|
}
|
|
586
|
+
/** The script's memory limit in bytes, as llGetMemoryLimit reads it. */
|
|
587
|
+
get memoryLimit() {
|
|
588
|
+
return this.state.memory.limit;
|
|
589
|
+
}
|
|
315
590
|
/** True once `llDie()` has been called. Subsequent fire() calls are no-ops. */
|
|
316
591
|
get dead() {
|
|
317
592
|
return this.state.lifecycle.dead;
|
|
@@ -397,7 +672,7 @@ export class Script {
|
|
|
397
672
|
if (this.state.lifecycle.dead)
|
|
398
673
|
return;
|
|
399
674
|
if (!this.running) {
|
|
400
|
-
this.parkedEvents.push({ at: this.now, event: eventName, payload });
|
|
675
|
+
withCollisionFilter(this, eventName, payload, p => this.parkedEvents.push({ at: this.now, event: eventName, payload: p }));
|
|
401
676
|
return;
|
|
402
677
|
}
|
|
403
678
|
if (!this.started) {
|
|
@@ -408,12 +683,15 @@ export class Script {
|
|
|
408
683
|
this.linkset.settle([this]);
|
|
409
684
|
}
|
|
410
685
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
this.
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
686
|
+
withCollisionFilter(this, eventName, payload, p => {
|
|
687
|
+
const started = this.handles(eventName) ? [this] : [];
|
|
688
|
+
if (this.task || (DELAYED_EVENTS.has(eventName) && this.clockView.delayedUntilMs > this.now)) {
|
|
689
|
+
this.clockView.schedule(this.now, eventName, p);
|
|
690
|
+
}
|
|
691
|
+
else
|
|
692
|
+
this.deliver(eventName, p);
|
|
693
|
+
this.linkset.settle(started);
|
|
694
|
+
});
|
|
417
695
|
}
|
|
418
696
|
/**
|
|
419
697
|
* Run state_entry of the default state. If the script has been auto-started
|
|
@@ -427,6 +705,55 @@ export class Script {
|
|
|
427
705
|
this.boot();
|
|
428
706
|
this.linkset.settle([this]);
|
|
429
707
|
}
|
|
708
|
+
/** Queue a fresh inventory script's first event without waiting for its handler. */
|
|
709
|
+
queueStart() {
|
|
710
|
+
if (this.started || this.dead)
|
|
711
|
+
return;
|
|
712
|
+
this.started = true;
|
|
713
|
+
this.clockView.schedule(this.now, 'state_entry');
|
|
714
|
+
}
|
|
715
|
+
/** Copy inventory code; fresh copies omit saved runtime state for remote loading. */
|
|
716
|
+
copyTo(host, opts = {}) {
|
|
717
|
+
const copy = new Script(this.ast, { ...this.options, host, coverage: false });
|
|
718
|
+
Object.assign(copy.mocks, this.mocks);
|
|
719
|
+
copy.state.coverage = this.state.coverage;
|
|
720
|
+
if (opts.fresh)
|
|
721
|
+
return copy;
|
|
722
|
+
for (const g of this.ast.globals) {
|
|
723
|
+
const value = this.global(g.name);
|
|
724
|
+
const saved = Array.isArray(value)
|
|
725
|
+
? typedList(structuredClone(value), listElementTypes(value) ?? [])
|
|
726
|
+
: structuredClone(value);
|
|
727
|
+
copy.setGlobal(g.name, saved);
|
|
728
|
+
}
|
|
729
|
+
copy.state.memory.limit = this.state.memory.limit;
|
|
730
|
+
copy.state.currentState = this.state.currentState;
|
|
731
|
+
copy.started = this.started;
|
|
732
|
+
copy.running = this.running && !this.dead;
|
|
733
|
+
Object.assign(copy.state.random, this.state.random);
|
|
734
|
+
copy.state.listens.push(...structuredClone(this.state.listens));
|
|
735
|
+
copy.state.listenHandleCounter = this.state.listenHandleCounter;
|
|
736
|
+
copy.clockView.timerIntervalMs = this.clockView.timerIntervalMs;
|
|
737
|
+
copy.clockView.timerNextFireMs = copy.now + Math.max(0, this.clockView.timerNextFireMs - this.now);
|
|
738
|
+
copy.clockView.timeReferenceMs = copy.now - (this.now - this.clockView.timeReferenceMs);
|
|
739
|
+
copy.clockView.minEventDelayMs = this.clockView.minEventDelayMs;
|
|
740
|
+
for (const kind of ['move', 'look']) {
|
|
741
|
+
const request = host.linkset.prims[0].targetMotion[kind];
|
|
742
|
+
if (request?.owner.prim === this.host.key && request.owner.script === this.scriptName)
|
|
743
|
+
request.owner.prim = host.key;
|
|
744
|
+
}
|
|
745
|
+
return copy;
|
|
746
|
+
}
|
|
747
|
+
/** Queue rez lifecycle events after restoring the inventory script's saved data. */
|
|
748
|
+
queueRez() {
|
|
749
|
+
if (!this.running)
|
|
750
|
+
return;
|
|
751
|
+
if (!this.started) {
|
|
752
|
+
this.started = true;
|
|
753
|
+
this.clockView.schedule(this.now, 'state_entry');
|
|
754
|
+
}
|
|
755
|
+
this.clockView.schedule(this.now, 'on_rez', { start_param: this.startParameter });
|
|
756
|
+
}
|
|
430
757
|
boot() {
|
|
431
758
|
this.started = true;
|
|
432
759
|
const entry = this.handlersByState.get('default')?.get('state_entry');
|
|
@@ -461,11 +788,20 @@ export class Script {
|
|
|
461
788
|
this.state.lifecycle.dying = false;
|
|
462
789
|
this.state.lifecycle.removed = false;
|
|
463
790
|
this.state.listens.length = 0;
|
|
791
|
+
this.state.urls.length = 0;
|
|
464
792
|
this.state.permissions.granted = 0;
|
|
465
793
|
this.state.permissions.agent = NULL_KEY;
|
|
466
794
|
for (const r of this.state.permissions.requests)
|
|
467
795
|
r.answered = true;
|
|
796
|
+
for (const r of this.state.experienceRequests)
|
|
797
|
+
r.answered = true;
|
|
798
|
+
this.state.controls.taken = null;
|
|
799
|
+
stopSensors(this);
|
|
800
|
+
clearTargets(this);
|
|
801
|
+
clearTargetMotion(this);
|
|
468
802
|
this.clockView.cancelTimer();
|
|
803
|
+
this.clockView.setMinEventDelay(0);
|
|
804
|
+
this.startParameter = 0;
|
|
469
805
|
this.clockView.resetReference();
|
|
470
806
|
this.linkset.clock.purgeTarget(this);
|
|
471
807
|
this.parkedEvents.length = 0;
|
|
@@ -494,8 +830,11 @@ export class Script {
|
|
|
494
830
|
return;
|
|
495
831
|
}
|
|
496
832
|
const handler = this.handlersByState.get(this.state.currentState)?.get(event);
|
|
497
|
-
if (handler)
|
|
498
|
-
|
|
833
|
+
if (!handler)
|
|
834
|
+
return;
|
|
835
|
+
if (DELAYED_EVENTS.has(event))
|
|
836
|
+
this.clockView.delayedUntilMs = this.now + this.clockView.minEventDelayMs;
|
|
837
|
+
this.begin(handler, bindPayload(event, payload), payload);
|
|
499
838
|
}
|
|
500
839
|
/** Whether the current state has a handler for `event`. */
|
|
501
840
|
handles(event) {
|
|
@@ -517,6 +856,7 @@ export class Script {
|
|
|
517
856
|
this.step({ run: this.runHandler(handler, args, payload), startedAt: this.now, wakeAt: 0 });
|
|
518
857
|
}
|
|
519
858
|
step(task) {
|
|
859
|
+
this.resumedAt = this.now;
|
|
520
860
|
let r;
|
|
521
861
|
try {
|
|
522
862
|
r = task.run.next();
|
|
@@ -530,6 +870,7 @@ export class Script {
|
|
|
530
870
|
return;
|
|
531
871
|
}
|
|
532
872
|
this.finish(task);
|
|
873
|
+
this.clockView.finishHandler(r.value);
|
|
533
874
|
if (r.value)
|
|
534
875
|
this.restart();
|
|
535
876
|
}
|
|
@@ -554,10 +895,15 @@ export class Script {
|
|
|
554
895
|
* does `state foo;` continues the chain correctly.
|
|
555
896
|
*/
|
|
556
897
|
*runHandler(handler, args, payload) {
|
|
898
|
+
this.state.gameControlSnapshot = handler.name === 'game_control'
|
|
899
|
+
? payload['gameControlSnapshot'] ?? null : null;
|
|
900
|
+
enterDamageEvent(payload);
|
|
557
901
|
const detected = payload['detected'];
|
|
558
902
|
const pushed = Array.isArray(detected) && detected.length > 0;
|
|
559
903
|
if (pushed)
|
|
560
|
-
this.state.detectedStack.push({
|
|
904
|
+
this.state.detectedStack.push({
|
|
905
|
+
entries: detected, event: handler.name,
|
|
906
|
+
});
|
|
561
907
|
try {
|
|
562
908
|
this.state.memory.startEvent();
|
|
563
909
|
return yield* this.runHandlerChain(handler, args);
|
|
@@ -570,6 +916,8 @@ export class Script {
|
|
|
570
916
|
finally {
|
|
571
917
|
if (pushed)
|
|
572
918
|
this.state.detectedStack.pop();
|
|
919
|
+
this.state.gameControlSnapshot = null;
|
|
920
|
+
leaveDamageEvent(payload);
|
|
573
921
|
}
|
|
574
922
|
}
|
|
575
923
|
/** Returns true when the chain ended in llResetScript. */
|
|
@@ -583,7 +931,6 @@ export class Script {
|
|
|
583
931
|
invalidPrograms: this.invalidPrograms,
|
|
584
932
|
script: this,
|
|
585
933
|
prim: this.host,
|
|
586
|
-
linkset: this.linkset,
|
|
587
934
|
};
|
|
588
935
|
let pending = {
|
|
589
936
|
handler,
|
|
@@ -602,6 +949,8 @@ export class Script {
|
|
|
602
949
|
return false;
|
|
603
950
|
if (!(e instanceof StateChangeSignal))
|
|
604
951
|
throw e;
|
|
952
|
+
this.state.gameControlSnapshot = null;
|
|
953
|
+
this.clearDamageAdjustment();
|
|
605
954
|
if (this.state.lifecycle.removed)
|
|
606
955
|
return false;
|
|
607
956
|
const target = e.target;
|
|
@@ -626,6 +975,7 @@ export class Script {
|
|
|
626
975
|
if (!this.handlersByState.has(target)) {
|
|
627
976
|
throw new Error(`unknown state '${target}' in state change`);
|
|
628
977
|
}
|
|
978
|
+
stopSensors(this);
|
|
629
979
|
this.state.listens.length = 0;
|
|
630
980
|
this.linkset.clock.purgeTarget(this, this.now, '__reset');
|
|
631
981
|
this.state.currentState = target;
|
|
@@ -656,6 +1006,10 @@ export class Script {
|
|
|
656
1006
|
*/
|
|
657
1007
|
markDead() {
|
|
658
1008
|
this.state.lifecycle.dead = true;
|
|
1009
|
+
this.state.urls.length = 0;
|
|
1010
|
+
stopSensors(this);
|
|
1011
|
+
clearTargets(this);
|
|
1012
|
+
clearTargetMotion(this);
|
|
659
1013
|
this.clockView.cancelTimer();
|
|
660
1014
|
this.abandon();
|
|
661
1015
|
}
|