@@ -5,7 +5,7 @@
|
|
5
5
|
import { activeInHierarchyFieldName } from "../engine/engine_constants.js";
|
6
6
|
import { destroy, findByGuid, foreachComponent, HideFlags, type IInstantiateOptions, instantiate, isActiveInHierarchy, isActiveSelf, isDestroyed, isUsingInstancing, markAsInstancedRendered, setActive } from "../engine/engine_gameobject.js";
|
7
7
|
import * as main from "../engine/engine_mainloop_utils.js";
|
8
|
-
import { syncDestroy, syncInstantiate } from "../engine/engine_networking_instantiate.js";
|
8
|
+
import { syncDestroy, syncInstantiate, SyncInstantiateOptions } from "../engine/engine_networking_instantiate.js";
|
9
9
|
import { Context, FrameEvent } from "../engine/engine_setup.js";
|
10
10
|
import * as threeutils from "../engine/engine_three_utils.js";
|
11
11
|
import type { Collision, ComponentInit, Constructor, ConstructorConcrete, GuidsMap, ICollider, IComponent, IGameObject, SourceIdentifier } from "../engine/engine_types.js";
|
@@ -115,7 +115,7 @@
|
|
115
115
|
* @param instance object to instantiate
|
116
116
|
* @param opts options for the instantiation
|
117
117
|
*/
|
118
|
-
public static instantiateSynced(instance: GameObject | Object3D | null, opts:
|
118
|
+
public static instantiateSynced(instance: GameObject | Object3D | null, opts: SyncInstantiateOptions): GameObject | null {
|
119
119
|
if (!instance) return null;
|
120
120
|
return syncInstantiate(instance as any, opts) as GameObject | null;
|
121
121
|
}
|
@@ -188,10 +188,12 @@
|
|
188
188
|
|
189
189
|
|
190
190
|
params.parent.add(root);
|
191
|
+
root.rotateY(Math.PI / 2);
|
192
|
+
// root.quaternion.copy(params.parent.quaternion);
|
193
|
+
|
191
194
|
if (params.position) root.position?.copy(params.position);
|
192
195
|
if (params.size) {
|
193
196
|
root.worldScale = new Vector3().copy(params.size);
|
194
|
-
// root.scale?.copy(params.size);
|
195
197
|
}
|
196
198
|
root.position.y = root.scale.y / 2;
|
197
199
|
|
@@ -247,8 +247,8 @@
|
|
247
247
|
// listener.target = go;
|
248
248
|
// }
|
249
249
|
if (go.guid) {
|
250
|
-
|
251
|
-
|
250
|
+
|
251
|
+
if (debug) console.log("[Local] new instance", "gameobject:", instance?.guid);
|
252
252
|
const model = new NewInstanceModel(obj.guid, go.guid);
|
253
253
|
model.seed = seed;
|
254
254
|
if (opts.deleteOnDisconnect === true)
|
@@ -279,6 +279,8 @@
|
|
279
279
|
const con = opts?.context?.connection;
|
280
280
|
if (!con && isDevEnvironment())
|
281
281
|
console.debug("Object will be instantiated but it will not be synced: not connected", obj.guid);
|
282
|
+
|
283
|
+
if (opts.context.connection.isInRoom) syncedInstantiated.push(new WeakRef(go));
|
282
284
|
opts?.context?.connection.send(InstantiateEvent.NewInstanceCreated, model);
|
283
285
|
}
|
284
286
|
else console.warn("Missing guid, can not send new instance event", go);
|
@@ -290,7 +292,11 @@
|
|
290
292
|
return Math.random() * 9_999_999;// Number.MAX_VALUE;;
|
291
293
|
}
|
292
294
|
|
295
|
+
const syncedInstantiated = new Array<WeakRef<Object3D>>();
|
296
|
+
|
293
297
|
export function beginListenInstantiate(context: Context) {
|
298
|
+
|
299
|
+
|
294
300
|
context.connection.beginListen(InstantiateEvent.NewInstanceCreated, async (model: NewInstanceModel) => {
|
295
301
|
const obj: GameObject | null = await tryResolvePrefab(model.originalGuid, context.scene) as GameObject;
|
296
302
|
if (model.preventCreation === true) {
|
@@ -300,6 +306,7 @@
|
|
300
306
|
console.warn("could not find object that was instantiated: " + model.guid);
|
301
307
|
return;
|
302
308
|
}
|
309
|
+
console.log(obj);
|
303
310
|
const options = new InstantiateOptions();
|
304
311
|
if (model.position)
|
305
312
|
options.position = new Vector3(model.position.x, model.position.y, model.position.z);
|
@@ -315,22 +322,27 @@
|
|
315
322
|
if (debug && context.alias)
|
316
323
|
console.log("[Remote] instantiate in: " + context.alias);
|
317
324
|
const inst = instantiate(obj as GameObject, options);
|
325
|
+
syncedInstantiated.push(new WeakRef(inst));
|
318
326
|
|
319
327
|
if (inst) {
|
320
328
|
if (model.parent === "scene")
|
321
329
|
context.scene.add(inst);
|
322
|
-
// console.log(inst, model.parent === "scene");
|
323
|
-
// if (inst.guid) {
|
324
|
-
// const listener = GameObject.addNewComponent(inst, DestroyListener);
|
325
|
-
// listener.target = inst;
|
326
|
-
// }
|
327
330
|
if (debug)
|
328
331
|
console.log("[Remote] new instance", "gameobject:", inst?.guid, obj);
|
329
332
|
}
|
330
333
|
});
|
331
|
-
|
334
|
+
context.connection.beginListen("left-room", () => {
|
335
|
+
if (syncedInstantiated.length > 0)
|
336
|
+
console.debug(`Left networking room, cleaning up ${syncedInstantiated.length} instantiated objects`);
|
337
|
+
for (const prev of syncedInstantiated) {
|
338
|
+
const obj = prev.deref();
|
339
|
+
if (obj) obj.destroy();
|
340
|
+
}
|
341
|
+
syncedInstantiated.length = 0;
|
342
|
+
})
|
332
343
|
}
|
333
344
|
|
345
|
+
|
334
346
|
function instantiateSeeded(obj: GameObject, opts: IInstantiateOptions | null): { instance: GameObject | null, seed: number } {
|
335
347
|
const seed = generateSeed();
|
336
348
|
const options = opts ?? new InstantiateOptions();
|
@@ -260,7 +260,10 @@
|
|
260
260
|
(data: any | flatbuffers.ByteBuffer): void;
|
261
261
|
}
|
262
262
|
|
263
|
-
/** Main class to communicate with the networking backend
|
263
|
+
/** Main class to communicate with the networking backend
|
264
|
+
* @link https://engine.needle.tools/docs/networking.html
|
265
|
+
*
|
266
|
+
*/
|
264
267
|
export class NetworkConnection implements INetworkConnection {
|
265
268
|
|
266
269
|
private context: Context;
|
@@ -454,7 +457,28 @@
|
|
454
457
|
this._ws?.send(message);
|
455
458
|
}
|
456
459
|
|
457
|
-
/** Use to start listening to networking events
|
460
|
+
/** Use to start listening to networking events.
|
461
|
+
* To unsubscribe from events use the `stopListen` method.
|
462
|
+
* See the example below for typical usage:
|
463
|
+
*
|
464
|
+
* ### Component Example
|
465
|
+
* ```ts
|
466
|
+
* // Make sure to unsubscribe from events when the component is disabled
|
467
|
+
* export class MyComponent extends Behaviour {
|
468
|
+
* onEnable() {
|
469
|
+
* this.connection.beginListen("joined-room", this.onJoinedRoom)
|
470
|
+
* }
|
471
|
+
* onDisable() {
|
472
|
+
* this.connection.stopListen("joined-room", this.onJoinedRoom)
|
473
|
+
* }
|
474
|
+
* onJoinedRoom = () => {
|
475
|
+
* console.log("I joined a networked room")
|
476
|
+
* }
|
477
|
+
* }
|
478
|
+
* ```
|
479
|
+
* @link https://engine.needle.tools/docs/networking.html
|
480
|
+
*
|
481
|
+
*/
|
458
482
|
public beginListen(key: (string & {}) | OwnershipEvent | OwnershipEventNamesIncoming | RoomEventsIncoming | RoomEvents, callback: Function): Function {
|
459
483
|
if (!this._listeners[key])
|
460
484
|
this._listeners[key] = [];
|
@@ -465,7 +489,26 @@
|
|
465
489
|
/**@deprecated please use stopListen instead (2.65.2-pre) */
|
466
490
|
public stopListening(key: (string & {}) | OwnershipEvent | OwnershipEventNamesIncoming | RoomEventsIncoming | RoomEvents, callback: Function | null) { return this.stopListen(key, callback); }
|
467
491
|
|
468
|
-
/** Use to stop listening to networking events
|
492
|
+
/** Use to stop listening to networking events
|
493
|
+
* To subscribe to events use the `beginListen` method.
|
494
|
+
* See the example below for typical usage:
|
495
|
+
*
|
496
|
+
* ### Component Example
|
497
|
+
* ```ts
|
498
|
+
* // Make sure to unsubscribe from events when the component is disabled
|
499
|
+
* export class MyComponent extends Behaviour {
|
500
|
+
* onEnable() {
|
501
|
+
* this.connection.beginListen("joined-room", this.onJoinedRoom)
|
502
|
+
* }
|
503
|
+
* onDisable() {
|
504
|
+
* this.connection.stopListen("joined-room", this.onJoinedRoom)
|
505
|
+
* }
|
506
|
+
* onJoinedRoom = () => {
|
507
|
+
* console.log("I joined a networked room")
|
508
|
+
* }
|
509
|
+
* }
|
510
|
+
* ```
|
511
|
+
*/
|
469
512
|
public stopListen(key: (string & {}) | OwnershipEvent | OwnershipEventNamesIncoming | RoomEventsIncoming | RoomEvents, callback: Function | null) {
|
470
513
|
if (!callback) return;
|
471
514
|
if (!this._listeners[key]) return;
|
@@ -6,6 +6,7 @@
|
|
6
6
|
import { Context } from "./engine_setup.js";
|
7
7
|
import { ICamera } from "./engine_types.js";
|
8
8
|
import { RGBAColor } from "./js-extensions/index.js";
|
9
|
+
import { setCustomVisibility } from "./js-extensions/Layers.js";
|
9
10
|
|
10
11
|
declare type ScreenshotImageMimeType = "image/webp" | "image/png";
|
11
12
|
|
@@ -180,7 +181,15 @@
|
|
180
181
|
const renderers = new Array<Renderer>();
|
181
182
|
if (callRenderEvents) {
|
182
183
|
getComponentsInChildren(context.scene, Renderer, renderers);
|
183
|
-
renderers.forEach(r =>
|
184
|
+
renderers.forEach(r => {
|
185
|
+
r?.onBeforeRender();
|
186
|
+
if (r.isInstancingActive && r.instances) {
|
187
|
+
for (let i = 0; i < r.instances?.length; i++) {
|
188
|
+
const handle = r.instances[i];
|
189
|
+
setCustomVisibility(handle.object, true);
|
190
|
+
}
|
191
|
+
}
|
192
|
+
});
|
184
193
|
}
|
185
194
|
|
186
195
|
if (transparent) {
|