multyx-client 0.1.0 → 0.1.2
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/README.md +1611 -0
- package/dist/controller.d.ts +57 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +106 -60
- package/dist/items/index.d.ts +6 -0
- package/dist/items/index.js +4 -0
- package/dist/items/list.d.ts +59 -0
- package/dist/items/list.js +186 -46
- package/dist/items/object.d.ts +34 -0
- package/dist/items/object.js +84 -38
- package/dist/items/router.d.ts +1 -0
- package/dist/items/value.d.ts +30 -0
- package/dist/items/value.js +27 -59
- package/dist/message.d.ts +116 -0
- package/dist/message.js +68 -4
- package/dist/options.d.ts +8 -0
- package/dist/options.js +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/utils.d.ts +29 -0
- package/dist/utils.js +2 -1
- package/multyx.js +1 -1
- package/package.json +3 -3
- package/src/index.ts +102 -59
- package/src/items/index.ts +5 -0
- package/src/items/list.ts +220 -55
- package/src/items/object.ts +96 -49
- package/src/items/value.ts +37 -66
- package/src/message.ts +60 -4
- package/src/options.ts +1 -1
- package/src/types.ts +7 -1
- package/src/utils.ts +1 -0
- package/tsconfig.json +1 -0
package/src/index.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { Message } from "./message";
|
|
2
|
-
import { Unpack, EditWrapper, Add } from './utils';
|
|
3
|
-
import { RawObject } from "./types";
|
|
1
|
+
import { Message, UncompressUpdate } from "./message";
|
|
2
|
+
import { Unpack, EditWrapper, Add, Edit, Done } from './utils';
|
|
3
|
+
import { RawObject, ResponseUpdate } from "./types";
|
|
4
4
|
import { Controller } from "./controller";
|
|
5
|
-
import { MultyxClientObject } from "./items";
|
|
5
|
+
import { MultyxClientObject, MultyxClientValue } from "./items";
|
|
6
6
|
import { DefaultOptions, Options } from "./options";
|
|
7
|
-
|
|
8
7
|
export default class Multyx {
|
|
9
8
|
ws: WebSocket;
|
|
10
9
|
uuid: string;
|
|
@@ -12,15 +11,17 @@ export default class Multyx {
|
|
|
12
11
|
ping: number;
|
|
13
12
|
events: Map<string | Symbol, ((data?: any) => void)[]>;
|
|
14
13
|
self: RawObject;
|
|
14
|
+
tps: number;
|
|
15
15
|
all: RawObject;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
space: string;
|
|
17
|
+
clients: { [key: string]: MultyxClientObject };
|
|
18
|
+
teams: MultyxClientObject;
|
|
18
19
|
controller: Controller;
|
|
19
20
|
|
|
20
21
|
options: Options;
|
|
21
22
|
|
|
22
23
|
// Queue of functions to be called after each frame
|
|
23
|
-
|
|
24
|
+
[Done]: ((...args: any[]) => void)[] = [];
|
|
24
25
|
|
|
25
26
|
static Start = Symbol('start');
|
|
26
27
|
static Connection = Symbol('connection');
|
|
@@ -33,16 +34,17 @@ export default class Multyx {
|
|
|
33
34
|
constructor(options: Options = {}, callback?: () => void) {
|
|
34
35
|
this.options = { ...DefaultOptions, ...options };
|
|
35
36
|
|
|
36
|
-
const url = `ws${this.options.secure ? 's' : ''}://${this.options.uri}:${this.options.port}
|
|
37
|
+
const url = `ws${this.options.secure ? 's' : ''}://${this.options.uri.split('/')[0]}:${this.options.port}/${this.options.uri.split('/')[1] ?? ''}`;
|
|
37
38
|
this.ws = new WebSocket(url);
|
|
38
39
|
this.ping = 0;
|
|
40
|
+
this.space = 'default';
|
|
39
41
|
this.events = new Map();
|
|
40
42
|
this.self = {};
|
|
43
|
+
this.tps = 0;
|
|
41
44
|
this.all = {};
|
|
42
|
-
this.teams = {};
|
|
45
|
+
this.teams = new MultyxClientObject(this, {}, [], true);
|
|
43
46
|
this.clients = {};
|
|
44
47
|
this.controller = new Controller(this.ws);
|
|
45
|
-
this.listenerQueue = [];
|
|
46
48
|
|
|
47
49
|
callback?.();
|
|
48
50
|
|
|
@@ -53,25 +55,51 @@ export default class Multyx {
|
|
|
53
55
|
if(msg.native) {
|
|
54
56
|
this.parseNativeEvent(msg);
|
|
55
57
|
this.events.get(Multyx.Native)?.forEach(cb => cb(msg));
|
|
56
|
-
} else
|
|
57
|
-
this.events
|
|
58
|
+
} else {
|
|
59
|
+
this.events.get(msg.name)?.forEach(cb => {
|
|
60
|
+
const response = cb(msg.data);
|
|
61
|
+
if(response !== undefined) this.send(msg.name, response);
|
|
62
|
+
});
|
|
58
63
|
this.events.get(Multyx.Custom)?.forEach(cb => cb(msg));
|
|
59
64
|
}
|
|
60
65
|
this.events.get(Multyx.Any)?.forEach(cb => cb(msg));
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Listen for a message from the server
|
|
71
|
+
* @param name Name of the message
|
|
72
|
+
* @param callback Function to call when the message is received
|
|
73
|
+
*/
|
|
74
|
+
on(name: string | Symbol, callback: (data: RawObject) => any) {
|
|
65
75
|
const events = this.events.get(name) ?? [];
|
|
66
76
|
events.push(callback);
|
|
67
77
|
this.events.set(name, events);
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Send a message to the server
|
|
82
|
+
* @param name Name of the message
|
|
83
|
+
* @param data Data to send
|
|
84
|
+
*/
|
|
85
|
+
send(name: string, data: any) {
|
|
71
86
|
if(name[0] === '_') name = '_' + name;
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
const update = {
|
|
88
|
+
instruction: 'resp',
|
|
89
|
+
name,
|
|
90
|
+
response: data
|
|
91
|
+
} as ResponseUpdate;
|
|
92
|
+
this.ws.send(Message.Native(update));
|
|
93
|
+
}
|
|
74
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Send a message to the server and wait for a response
|
|
97
|
+
* @param name Name of the message
|
|
98
|
+
* @param data Data to send
|
|
99
|
+
* @returns Promise that resolves when the message is received
|
|
100
|
+
*/
|
|
101
|
+
await(name: string, data?: any) {
|
|
102
|
+
this.send(name, data);
|
|
75
103
|
return new Promise(res => this.events.set(Symbol.for("_" + name), [res]));
|
|
76
104
|
}
|
|
77
105
|
|
|
@@ -92,20 +120,21 @@ export default class Multyx {
|
|
|
92
120
|
}
|
|
93
121
|
}
|
|
94
122
|
|
|
95
|
-
|
|
96
123
|
/**
|
|
97
|
-
*
|
|
98
|
-
* @param
|
|
124
|
+
* Add a function to be called after each frame
|
|
125
|
+
* @param callback Function to call after each frame
|
|
99
126
|
*/
|
|
100
|
-
|
|
101
|
-
this.
|
|
102
|
-
this.teams.all.clients.forAll((uuid) => callback(this.clients[uuid]));
|
|
103
|
-
});
|
|
104
|
-
this.on(Multyx.Connection, callback);
|
|
127
|
+
[Add](callback: () => void) {
|
|
128
|
+
this[Done].push(callback);
|
|
105
129
|
}
|
|
106
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Parse a native event from the server
|
|
133
|
+
* @param msg Message to parse
|
|
134
|
+
*/
|
|
107
135
|
private parseNativeEvent(msg: Message) {
|
|
108
|
-
|
|
136
|
+
msg.data = msg.data.map(UncompressUpdate);
|
|
137
|
+
if(this.options.logUpdateFrame) console.log(msg.data);
|
|
109
138
|
|
|
110
139
|
for(const update of msg.data) {
|
|
111
140
|
switch(update.instruction) {
|
|
@@ -114,7 +143,7 @@ export default class Multyx {
|
|
|
114
143
|
this.initialize(update);
|
|
115
144
|
|
|
116
145
|
for(const listener of this.events.get(Multyx.Start) ?? []) {
|
|
117
|
-
this.
|
|
146
|
+
this[Done].push(() => listener(update));
|
|
118
147
|
}
|
|
119
148
|
|
|
120
149
|
// Clear start event as it will never be called again
|
|
@@ -124,10 +153,27 @@ export default class Multyx {
|
|
|
124
153
|
|
|
125
154
|
// Client or team data edit
|
|
126
155
|
case 'edit': {
|
|
127
|
-
|
|
156
|
+
if(update.path.length == 1) {
|
|
157
|
+
if(update.team) {
|
|
158
|
+
this.teams.set(update.path[0], new EditWrapper(update.value));
|
|
159
|
+
} else {
|
|
160
|
+
this.clients[update.path[0]] = new MultyxClientObject(
|
|
161
|
+
this,
|
|
162
|
+
new EditWrapper(update.value),
|
|
163
|
+
[update.path[0]],
|
|
164
|
+
false
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
const agent = update.team
|
|
169
|
+
? this.teams.get(update.path[0]) as MultyxClientObject
|
|
170
|
+
: this.clients[update.path[0]];
|
|
171
|
+
if(!agent) return;
|
|
172
|
+
agent.set(update.path.slice(1), new EditWrapper(update.value));
|
|
173
|
+
}
|
|
128
174
|
|
|
129
175
|
for(const listener of this.events.get(Multyx.Edit) ?? []) {
|
|
130
|
-
this.
|
|
176
|
+
this[Done].push(() => listener(update));
|
|
131
177
|
}
|
|
132
178
|
break;
|
|
133
179
|
}
|
|
@@ -148,7 +194,7 @@ export default class Multyx {
|
|
|
148
194
|
);
|
|
149
195
|
|
|
150
196
|
for(const listener of this.events.get(Multyx.Connection) ?? []) {
|
|
151
|
-
this.
|
|
197
|
+
this[Done].push(() => listener(this.clients[update.uuid]));
|
|
152
198
|
}
|
|
153
199
|
break;
|
|
154
200
|
}
|
|
@@ -157,7 +203,7 @@ export default class Multyx {
|
|
|
157
203
|
case 'dcon': {
|
|
158
204
|
for(const listener of this.events.get(Multyx.Disconnect) ?? []) {
|
|
159
205
|
const clientValue = this.clients[update.client].value;
|
|
160
|
-
this.
|
|
206
|
+
this[Done].push(() => listener(clientValue));
|
|
161
207
|
}
|
|
162
208
|
delete this.clients[update.client];
|
|
163
209
|
break;
|
|
@@ -166,7 +212,8 @@ export default class Multyx {
|
|
|
166
212
|
// Response to client
|
|
167
213
|
case 'resp': {
|
|
168
214
|
const promiseResolve = this.events.get(Symbol.for("_" + update.name))[0];
|
|
169
|
-
|
|
215
|
+
this.events.delete(Symbol.for("_" + update.name));
|
|
216
|
+
this[Done].push(() => promiseResolve(update.response));
|
|
170
217
|
break;
|
|
171
218
|
}
|
|
172
219
|
|
|
@@ -178,17 +225,17 @@ export default class Multyx {
|
|
|
178
225
|
}
|
|
179
226
|
}
|
|
180
227
|
|
|
181
|
-
this.
|
|
182
|
-
this.
|
|
228
|
+
this[Done].forEach(x => x());
|
|
229
|
+
this[Done].length = 0;
|
|
183
230
|
}
|
|
184
231
|
|
|
185
232
|
private initialize(update: RawObject) {
|
|
233
|
+
this.tps = update.tps;
|
|
186
234
|
this.uuid = update.client.uuid;
|
|
187
235
|
this.joinTime = update.client.joinTime;
|
|
188
236
|
this.controller.listening = new Set(update.client.controller);
|
|
189
237
|
|
|
190
238
|
// Create MultyxClientObject for all teams
|
|
191
|
-
this.teams = new MultyxClientObject(this, {}, [], true);
|
|
192
239
|
for(const team of Object.keys(update.teams)) {
|
|
193
240
|
this.teams[team] = new EditWrapper(update.teams[team]);
|
|
194
241
|
}
|
|
@@ -222,40 +269,36 @@ export default class Multyx {
|
|
|
222
269
|
}
|
|
223
270
|
}
|
|
224
271
|
|
|
225
|
-
private parseEdit(update: RawObject) {
|
|
226
|
-
let route: any = update.team ? this.teams : this.clients;
|
|
227
|
-
if(!route) return;
|
|
228
|
-
|
|
229
|
-
// Loop through path to get to object being edited
|
|
230
|
-
for(const p of update.path.slice(0, -1)) {
|
|
231
|
-
// Create new object at path if non-existent
|
|
232
|
-
if(!(p in route)) route[p] = new EditWrapper({});
|
|
233
|
-
route = route[p];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const prop = update.path.slice(-1)[0];
|
|
237
|
-
route[prop] = new EditWrapper(update.value);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
272
|
private parseSelf(update: RawObject) {
|
|
241
|
-
if(update.
|
|
273
|
+
if(update.property == 'controller') {
|
|
242
274
|
this.controller.listening = new Set(update.data);
|
|
243
|
-
} else if(update.
|
|
275
|
+
} else if(update.property == 'uuid') {
|
|
244
276
|
this.uuid = update.data;
|
|
245
|
-
} else if(update.
|
|
277
|
+
} else if(update.property == 'constraint') {
|
|
246
278
|
let route = this.uuid == update.data.path[0] ? this.self : this.teams[update.data.path[0]];
|
|
247
279
|
for(const prop of update.data.path.slice(1)) route = route?.[prop];
|
|
248
280
|
if(route === undefined) return;
|
|
249
281
|
|
|
250
282
|
route[Unpack]({ [update.data.name]: update.data.args });
|
|
283
|
+
} else if(update.property == 'space') {
|
|
284
|
+
this.space = update.data;
|
|
285
|
+
this.updateSpace();
|
|
251
286
|
}
|
|
252
287
|
}
|
|
253
288
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
289
|
+
// Hide all spaces except the current one
|
|
290
|
+
private updateSpace() {
|
|
291
|
+
if(this.space == 'default') {
|
|
292
|
+
(document.querySelectorAll('[data-multyx-space]') as NodeListOf<HTMLElement>).forEach(space => {
|
|
293
|
+
space.style.display = 'block';
|
|
294
|
+
space.style.pointerEvents = 'auto';
|
|
295
|
+
});
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
(document.querySelectorAll('[data-multyx-space]') as NodeListOf<HTMLElement>).forEach(space => {
|
|
300
|
+
space.style.display = space.dataset.multyxSpace == this.space ? 'block' : 'none';
|
|
301
|
+
space.style.pointerEvents = space.dataset.multyxSpace == this.space ? 'auto' : 'none';
|
|
302
|
+
});
|
|
260
303
|
}
|
|
261
304
|
}
|
package/src/items/index.ts
CHANGED
|
@@ -6,9 +6,14 @@ type MultyxClientItem<T = any> = T extends any[] ? MultyxClientList
|
|
|
6
6
|
: T extends object ? MultyxClientObject
|
|
7
7
|
: MultyxClientValue;
|
|
8
8
|
|
|
9
|
+
function IsMultyxClientItem(value: any): value is MultyxClientItem {
|
|
10
|
+
return value instanceof MultyxClientList || value instanceof MultyxClientObject || value instanceof MultyxClientValue;
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
export {
|
|
10
14
|
MultyxClientList,
|
|
11
15
|
MultyxClientObject,
|
|
12
16
|
MultyxClientValue,
|
|
13
17
|
MultyxClientItem,
|
|
18
|
+
IsMultyxClientItem,
|
|
14
19
|
};
|
package/src/items/list.ts
CHANGED
|
@@ -1,76 +1,251 @@
|
|
|
1
1
|
import Multyx from '../';
|
|
2
|
-
import { MultyxClientItem } from '.';
|
|
3
|
-
import { EditWrapper } from '../utils';
|
|
4
|
-
import
|
|
2
|
+
import { IsMultyxClientItem, type MultyxClientItem, type MultyxClientObject, MultyxClientValue } from '.';
|
|
3
|
+
import { Add, Done, Edit, EditWrapper, Unpack } from '../utils';
|
|
4
|
+
import MultyxClientItemRouter from './router';
|
|
5
|
+
import { Message } from '../message';
|
|
5
6
|
|
|
6
|
-
export default class MultyxClientList
|
|
7
|
-
|
|
7
|
+
export default class MultyxClientList {
|
|
8
|
+
protected list: MultyxClientItem[];
|
|
9
|
+
private multyx: Multyx;
|
|
10
|
+
propertyPath: string[];
|
|
11
|
+
editable: boolean;
|
|
12
|
+
|
|
13
|
+
private editCallbacks: ((index: number, value: any, oldValue: any) => void)[] = [];
|
|
14
|
+
|
|
15
|
+
addEditCallback(callback: (index: number, value: any, oldValue: any) => void) {
|
|
16
|
+
this.editCallbacks.push(callback);
|
|
17
|
+
}
|
|
8
18
|
|
|
9
19
|
get value() {
|
|
10
20
|
const parsed: any[] = [];
|
|
11
|
-
for(let i=0; i<this.length; i++) parsed[i] = this.get(i)
|
|
21
|
+
for(let i=0; i<this.length; i++) parsed[i] = this.get(i)?.value;
|
|
12
22
|
return parsed;
|
|
13
23
|
}
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
get length() {
|
|
26
|
+
return this.list.length;
|
|
27
|
+
}
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
this.
|
|
29
|
+
set length(length: number) {
|
|
30
|
+
this.list.length = length;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Shifting operations are needed to ensure that operations on elements in
|
|
35
|
+
* the list do not need to worry about the index of the element.
|
|
36
|
+
*
|
|
37
|
+
* Instead of changing each element's value when shifting, these shift
|
|
38
|
+
* operations are used to ensure that each MultyxClientItem stays the same
|
|
39
|
+
* @param index Index to shift from, -1 if special operation
|
|
40
|
+
* @param shift Shift amount, positive for right, negative for left
|
|
41
|
+
*/
|
|
42
|
+
private handleShiftOperation(index: number, shift: number) {
|
|
43
|
+
const operation = index >= 0
|
|
44
|
+
? shift >= 0 ? 'right': 'left'
|
|
45
|
+
: shift == 0 ? 'reverse' : shift < 0 ? 'length' : 'unknown';
|
|
46
|
+
|
|
47
|
+
switch(operation) {
|
|
48
|
+
// Reverse the array
|
|
49
|
+
case 'reverse':
|
|
50
|
+
for(let i=0; i<Math.floor(this.length/2); i++) {
|
|
51
|
+
const temp = this.list[i];
|
|
52
|
+
this.list[i] = this.list[this.length-1-i];
|
|
53
|
+
this.list[this.length-1-i] = temp;
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
|
|
57
|
+
// Shift items to the left
|
|
58
|
+
case 'left':
|
|
59
|
+
for(let i=index; i<this.length; i++) {
|
|
60
|
+
if(i+shift < 0) continue;
|
|
61
|
+
this.list[i+shift] = this.list[i];
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
|
|
65
|
+
// Shift items to the right
|
|
66
|
+
case 'right':
|
|
67
|
+
for(let i=this.length-1; i>=index; i--) {
|
|
68
|
+
this.list[i+shift] = this.list[i];
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
// Alter the length of the array
|
|
73
|
+
case 'length':
|
|
74
|
+
this.length += shift;
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
// Unknown operation
|
|
78
|
+
default:
|
|
79
|
+
if(this.multyx.options.verbose) {
|
|
80
|
+
console.error("Unknown shift operation: " + operation);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
constructor(multyx: Multyx, list: any[] | EditWrapper<any[]>, propertyPath: string[] = [], editable: boolean){
|
|
86
|
+
this.list = [];
|
|
87
|
+
this.propertyPath = propertyPath;
|
|
88
|
+
this.multyx = multyx;
|
|
89
|
+
this.editable = editable;
|
|
90
|
+
|
|
91
|
+
const isEditWrapper = list instanceof EditWrapper;
|
|
92
|
+
if(list instanceof MultyxClientList) list = list.value;
|
|
93
|
+
if(list instanceof EditWrapper) list = list.value;
|
|
94
|
+
|
|
95
|
+
for(let i=0; i<list.length; i++) {
|
|
96
|
+
this.set(i, isEditWrapper
|
|
97
|
+
? new EditWrapper(list[i])
|
|
98
|
+
: list[i]
|
|
99
|
+
);
|
|
100
|
+
}
|
|
20
101
|
|
|
21
102
|
return new Proxy(this, {
|
|
22
|
-
has: (o, p) => {
|
|
23
|
-
if(p
|
|
24
|
-
return o
|
|
103
|
+
has: (o, p: any) => {
|
|
104
|
+
if(typeof p == 'number') return o.has(p);
|
|
105
|
+
return p in o;
|
|
25
106
|
},
|
|
26
|
-
get: (o, p) => {
|
|
107
|
+
get: (o, p: any) => {
|
|
27
108
|
if(p in o) return o[p];
|
|
28
|
-
|
|
109
|
+
if(!isNaN(parseInt(p))) p = parseInt(p);
|
|
110
|
+
return o.get(p) as MultyxClientItem;
|
|
29
111
|
},
|
|
30
|
-
set: (o, p, v) => {
|
|
112
|
+
set: (o, p: any, v) => {
|
|
31
113
|
if(p in o) {
|
|
32
114
|
o[p] = v;
|
|
33
115
|
return true;
|
|
34
116
|
}
|
|
35
|
-
return o.set(p
|
|
117
|
+
return !!o.set(p, v);
|
|
36
118
|
},
|
|
37
|
-
deleteProperty: (o, p) => {
|
|
38
|
-
return o.delete(p
|
|
119
|
+
deleteProperty: (o, p: any) => {
|
|
120
|
+
if(typeof p == 'number') return o.delete(p);
|
|
121
|
+
return false;
|
|
39
122
|
}
|
|
40
123
|
});
|
|
41
124
|
}
|
|
42
125
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
126
|
+
has(index: number): boolean {
|
|
127
|
+
return index >= 0 && index < this.length;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
get(index: number | string[]): MultyxClientItem {
|
|
131
|
+
if(typeof index === 'number') return this.list[index];
|
|
132
|
+
if(index.length == 0) return this;
|
|
133
|
+
if(index.length == 1) return this.list[parseInt(index[0])];
|
|
134
|
+
|
|
135
|
+
const item = this.list[parseInt(index[0])];
|
|
136
|
+
if(!item || (item instanceof MultyxClientValue)) return undefined;
|
|
137
|
+
return item.get(index.slice(1));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private recursiveSet(path: string[], value: any): boolean {
|
|
141
|
+
if(path.length == 0) {
|
|
142
|
+
if(this.multyx.options.verbose) {
|
|
143
|
+
console.error(`Attempting to edit MultyxClientList with no path. Setting '${this.propertyPath.join('.')}' to ${value}`);
|
|
144
|
+
}
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if(path[0] == "shift" && value instanceof EditWrapper) {
|
|
149
|
+
this.handleShiftOperation(parseInt(path[1]), value.value);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
47
152
|
|
|
48
|
-
|
|
49
|
-
if(result && index >= this.length) this.length = index+1;
|
|
153
|
+
if(path.length == 1) return this.set(parseInt(path[0]), value);
|
|
50
154
|
|
|
51
|
-
|
|
155
|
+
let next = this.get(parseInt(path[0]));
|
|
156
|
+
if(next instanceof MultyxClientValue || next == undefined) {
|
|
157
|
+
this.set(parseInt(path[0]), new EditWrapper({}));
|
|
158
|
+
next = this.get(parseInt(path[0])) as MultyxClientObject;
|
|
159
|
+
}
|
|
160
|
+
return next.set(path.slice(1), value);
|
|
52
161
|
}
|
|
53
162
|
|
|
54
|
-
|
|
163
|
+
set(index: number | string[], value: any): boolean {
|
|
164
|
+
if(Array.isArray(index)) return this.recursiveSet(index, value);
|
|
165
|
+
|
|
166
|
+
const oldValue = this.get(index);
|
|
167
|
+
|
|
168
|
+
const serverSet = value instanceof EditWrapper;
|
|
169
|
+
const allowed = serverSet || this.editable;
|
|
170
|
+
if(serverSet || IsMultyxClientItem(value)) value = value.value;
|
|
171
|
+
if(value === undefined) return this.delete(index, serverSet);
|
|
172
|
+
|
|
173
|
+
// If value is a MultyxClientValue, set the value
|
|
174
|
+
if(this.list[index] instanceof MultyxClientValue && typeof value != 'object') {
|
|
175
|
+
return this.list[index].set(serverSet ? new EditWrapper(value) : value);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Attempting to edit property not editable to client
|
|
179
|
+
if(!allowed) {
|
|
180
|
+
if(this.multyx.options.verbose) {
|
|
181
|
+
console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join('.') + '.' + index}' to ${value}`);
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.list[index] = new (MultyxClientItemRouter(value))(
|
|
187
|
+
this.multyx,
|
|
188
|
+
serverSet ? new EditWrapper(value) : value,
|
|
189
|
+
[...this.propertyPath, index.toString()],
|
|
190
|
+
this.editable
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const propSymbol = Symbol.for("_" + this.propertyPath.join('.') + '.' + index);
|
|
194
|
+
if(this.multyx.events.has(propSymbol)) {
|
|
195
|
+
this.multyx[Done].push(...this.multyx.events.get(propSymbol).map(e =>
|
|
196
|
+
() => e(this.list[index])
|
|
197
|
+
));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// We have to push into queue, since object may not be fully created
|
|
201
|
+
// and there may still be more updates to parse
|
|
202
|
+
for(const listener of this.editCallbacks) {
|
|
203
|
+
this.multyx[Add](() => listener(index, this.get(index), oldValue));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
delete(index: number, native: boolean = false) {
|
|
210
|
+
const oldValue = this.get(index);
|
|
211
|
+
|
|
55
212
|
if(typeof index == 'string') index = parseInt(index);
|
|
56
213
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
214
|
+
if(!this.editable && !native) {
|
|
215
|
+
if(this.multyx.options.verbose) {
|
|
216
|
+
console.error(`Attempting to delete property that is not editable. Deleting '${this.propertyPath.join('.') + '.' + index}'`);
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
delete this.list[index];
|
|
222
|
+
|
|
223
|
+
for(const listener of this.editCallbacks) {
|
|
224
|
+
this.multyx[Add](() => listener(index, undefined, oldValue));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if(!native) {
|
|
228
|
+
this.multyx.ws.send(Message.Native({
|
|
229
|
+
instruction: 'edit',
|
|
230
|
+
path: [...this.propertyPath, index.toString()],
|
|
231
|
+
value: undefined
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return true;
|
|
60
236
|
}
|
|
61
237
|
|
|
62
238
|
/**
|
|
63
|
-
*
|
|
64
|
-
* @param
|
|
239
|
+
* Wait for a specific index to be set
|
|
240
|
+
* @param index Index to wait for
|
|
241
|
+
* @returns Promise that resolves when the value is set
|
|
65
242
|
*/
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
super.forAll((key, value) => callbackfn(value, key));
|
|
243
|
+
await(index: number) {
|
|
244
|
+
if(this.has(index)) return Promise.resolve(this.get(index));
|
|
245
|
+
const propSymbol = Symbol.for("_" + this.propertyPath.join('.') + '.' + index);
|
|
246
|
+
return new Promise(res => this.multyx.on(propSymbol, res));
|
|
71
247
|
}
|
|
72
248
|
|
|
73
|
-
|
|
74
249
|
/* All general array methods */
|
|
75
250
|
push(...items: any) {
|
|
76
251
|
for(const item of items) this.set(this.length, item);
|
|
@@ -152,8 +327,9 @@ export default class MultyxClientList extends MultyxClientObject {
|
|
|
152
327
|
}
|
|
153
328
|
|
|
154
329
|
map(callbackfn: (value: any, index: number, array: MultyxClientList) => any) {
|
|
330
|
+
const mapped = [];
|
|
155
331
|
for(let i=0; i<this.length; i++) {
|
|
156
|
-
|
|
332
|
+
mapped.push(callbackfn(this.get(i), i, this));
|
|
157
333
|
}
|
|
158
334
|
}
|
|
159
335
|
|
|
@@ -229,22 +405,6 @@ export default class MultyxClientList extends MultyxClientObject {
|
|
|
229
405
|
return -1;
|
|
230
406
|
}
|
|
231
407
|
|
|
232
|
-
deorder(): MultyxClientItem[] {
|
|
233
|
-
const values = [];
|
|
234
|
-
for(const index in this.object) {
|
|
235
|
-
values.push(this.get(index));
|
|
236
|
-
}
|
|
237
|
-
return values;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
deorderEntries(): [number, MultyxClientItem][] {
|
|
241
|
-
const values = [];
|
|
242
|
-
for(const index in this.object) {
|
|
243
|
-
values.push([parseInt(index), this.get(index)]);
|
|
244
|
-
}
|
|
245
|
-
return values;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
408
|
entries(): [any, number][] {
|
|
249
409
|
const entryList: [any, number][] = [];
|
|
250
410
|
for(let i=0; i<this.length; i++) {
|
|
@@ -257,6 +417,11 @@ export default class MultyxClientList extends MultyxClientObject {
|
|
|
257
417
|
return Array(this.length).fill(0).map((_, i) => i);
|
|
258
418
|
}
|
|
259
419
|
|
|
420
|
+
[Unpack](constraints: any[]) {
|
|
421
|
+
for(let i=0; i<this.length; i++) {
|
|
422
|
+
this.get(i)?.[Unpack](constraints[i]);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
260
425
|
|
|
261
426
|
/* Native methods to allow MultyxClientList to be treated as array */
|
|
262
427
|
[Symbol.iterator](): Iterator<MultyxClientItem> {
|