Compare commits
41 Commits
cursor/imp
...
snapshot-s
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c652778cc6 | ||
![]() |
049058eabb | ||
![]() |
20203d850f | ||
![]() |
aeeab60470 | ||
![]() |
a01507ac5d | ||
![]() |
f13feaa905 | ||
![]() |
c06a5d44ef | ||
![]() |
ac691a7590 | ||
![]() |
03dec17c75 | ||
![]() |
9b76c5c691 | ||
![]() |
8be1d936b2 | ||
![]() |
31d9ff4ff1 | ||
![]() |
78adaa6310 | ||
![]() |
7efb7014e9 | ||
![]() |
0b1f3b74ac | ||
![]() |
7711ea8e5c | ||
![]() |
5ed0abda37 | ||
![]() |
082e7d287c | ||
![]() |
1f3e8cd254 | ||
![]() |
5f7496704c | ||
![]() |
026f4964f3 | ||
![]() |
0860551f0f | ||
![]() |
cbc9975a1d | ||
![]() |
0f140c6e54 | ||
![]() |
8ac201c8ca | ||
![]() |
8606b2571a | ||
![]() |
d5c455899e | ||
![]() |
ace65de155 | ||
![]() |
83bd850d22 | ||
![]() |
15a6e97e81 | ||
![]() |
9e2bd6d1a0 | ||
![]() |
9b00761419 | ||
![]() |
d48b55c4b9 | ||
![]() |
fd9331ca22 | ||
![]() |
751f71c770 | ||
![]() |
1365d10717 | ||
![]() |
91eb2395d5 | ||
![]() |
4d6719a74b | ||
![]() |
806e7591d7 | ||
![]() |
824ef9496f | ||
![]() |
b9b2b6ad48 |
@@ -5,8 +5,6 @@ namespace gdjs {
|
|||||||
type Object3DNetworkSyncDataType = {
|
type Object3DNetworkSyncDataType = {
|
||||||
// z is position on the Z axis, different from zo, which is Z order
|
// z is position on the Z axis, different from zo, which is Z order
|
||||||
z: number;
|
z: number;
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
d: number;
|
d: number;
|
||||||
rx: number;
|
rx: number;
|
||||||
ry: number;
|
ry: number;
|
||||||
@@ -112,12 +110,12 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): Object3DNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): Object3DNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
z: this.getZ(),
|
z: this.getZ(),
|
||||||
w: this.getWidth(),
|
|
||||||
h: this.getHeight(),
|
|
||||||
d: this.getDepth(),
|
d: this.getDepth(),
|
||||||
rx: this.getRotationX(),
|
rx: this.getRotationX(),
|
||||||
ry: this.getRotationY(),
|
ry: this.getRotationY(),
|
||||||
@@ -127,11 +125,12 @@ namespace gdjs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
networkSyncData: Object3DNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||||
if (networkSyncData.w !== undefined) this.setWidth(networkSyncData.w);
|
|
||||||
if (networkSyncData.h !== undefined) this.setHeight(networkSyncData.h);
|
|
||||||
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||||
if (networkSyncData.rx !== undefined)
|
if (networkSyncData.rx !== undefined)
|
||||||
this.setRotationX(networkSyncData.rx);
|
this.setRotationX(networkSyncData.rx);
|
||||||
|
@@ -434,9 +434,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): Cube3DObjectNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): Cube3DObjectNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
mt: this._materialType,
|
mt: this._materialType,
|
||||||
fo: this._facesOrientation,
|
fo: this._facesOrientation,
|
||||||
bfu: this._backFaceUpThroughWhichAxisRotation,
|
bfu: this._backFaceUpThroughWhichAxisRotation,
|
||||||
@@ -448,9 +450,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: Cube3DObjectNetworkSyncData
|
networkSyncData: Cube3DObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
if (networkSyncData.mt !== undefined) {
|
if (networkSyncData.mt !== undefined) {
|
||||||
this._materialType = networkSyncData.mt;
|
this._materialType = networkSyncData.mt;
|
||||||
|
@@ -1,4 +1,12 @@
|
|||||||
namespace gdjs {
|
namespace gdjs {
|
||||||
|
type CustomObject3DNetworkSyncDataType = CustomObjectNetworkSyncDataType & {
|
||||||
|
z: float;
|
||||||
|
d: float;
|
||||||
|
rx: float;
|
||||||
|
ry: float;
|
||||||
|
ifz: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for 3D custom objects.
|
* Base class for 3D custom objects.
|
||||||
*/
|
*/
|
||||||
@@ -78,6 +86,33 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): CustomObject3DNetworkSyncDataType {
|
||||||
|
return {
|
||||||
|
...super.getNetworkSyncData(syncOptions),
|
||||||
|
z: this.getZ(),
|
||||||
|
d: this.getDepth(),
|
||||||
|
rx: this.getRotationX(),
|
||||||
|
ry: this.getRotationY(),
|
||||||
|
ifz: this.isFlippedZ(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFromNetworkSyncData(
|
||||||
|
networkSyncData: CustomObject3DNetworkSyncDataType,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
): void {
|
||||||
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
|
||||||
|
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
|
||||||
|
if (networkSyncData.rx !== undefined)
|
||||||
|
this.setRotationX(networkSyncData.rx);
|
||||||
|
if (networkSyncData.ry !== undefined)
|
||||||
|
this.setRotationY(networkSyncData.ry);
|
||||||
|
if (networkSyncData.ifz !== undefined) this.flipZ(networkSyncData.ifz);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the object position on the Z axis.
|
* Set the object position on the Z axis.
|
||||||
*/
|
*/
|
||||||
|
@@ -20,6 +20,7 @@ namespace gdjs {
|
|||||||
/** The base parameters of the Model3D object */
|
/** The base parameters of the Model3D object */
|
||||||
content: Object3DDataContent & {
|
content: Object3DDataContent & {
|
||||||
modelResourceName: string;
|
modelResourceName: string;
|
||||||
|
depth: number;
|
||||||
rotationX: number;
|
rotationX: number;
|
||||||
rotationY: number;
|
rotationY: number;
|
||||||
rotationZ: number;
|
rotationZ: number;
|
||||||
@@ -198,9 +199,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): Model3DObjectNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): Model3DObjectNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
mt: this._materialType,
|
mt: this._materialType,
|
||||||
op: this._originPoint,
|
op: this._originPoint,
|
||||||
cp: this._centerPoint,
|
cp: this._centerPoint,
|
||||||
@@ -209,13 +212,15 @@ namespace gdjs {
|
|||||||
ass: this._animationSpeedScale,
|
ass: this._animationSpeedScale,
|
||||||
ap: this._animationPaused,
|
ap: this._animationPaused,
|
||||||
cfd: this._crossfadeDuration,
|
cfd: this._crossfadeDuration,
|
||||||
|
d: this.getDepth(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: Model3DObjectNetworkSyncData
|
networkSyncData: Model3DObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
if (networkSyncData.mt !== undefined) {
|
if (networkSyncData.mt !== undefined) {
|
||||||
this._materialType = networkSyncData.mt;
|
this._materialType = networkSyncData.mt;
|
||||||
@@ -243,6 +248,9 @@ namespace gdjs {
|
|||||||
if (networkSyncData.cfd !== undefined) {
|
if (networkSyncData.cfd !== undefined) {
|
||||||
this._crossfadeDuration = networkSyncData.cfd;
|
this._crossfadeDuration = networkSyncData.cfd;
|
||||||
}
|
}
|
||||||
|
if (networkSyncData.d !== undefined) {
|
||||||
|
this.setDepth(networkSyncData.d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_reloadModel(objectData: Model3DObjectData) {
|
_reloadModel(objectData: Model3DObjectData) {
|
||||||
|
@@ -145,9 +145,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override getNetworkSyncData(): BBTextObjectNetworkSyncData {
|
override getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): BBTextObjectNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
text: this._text,
|
text: this._text,
|
||||||
o: this._opacity,
|
o: this._opacity,
|
||||||
c: this._color,
|
c: this._color,
|
||||||
@@ -162,9 +164,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override updateFromNetworkSyncData(
|
override updateFromNetworkSyncData(
|
||||||
networkSyncData: BBTextObjectNetworkSyncData
|
networkSyncData: BBTextObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
if (this._text !== undefined) {
|
if (this._text !== undefined) {
|
||||||
this.setBBText(networkSyncData.text);
|
this.setBBText(networkSyncData.text);
|
||||||
}
|
}
|
||||||
|
@@ -155,9 +155,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override getNetworkSyncData(): BitmapTextObjectNetworkSyncData {
|
override getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): BitmapTextObjectNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
text: this._text,
|
text: this._text,
|
||||||
opa: this._opacity,
|
opa: this._opacity,
|
||||||
tint: this._tint,
|
tint: this._tint,
|
||||||
@@ -172,9 +174,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override updateFromNetworkSyncData(
|
override updateFromNetworkSyncData(
|
||||||
networkSyncData: BitmapTextObjectNetworkSyncData
|
networkSyncData: BitmapTextObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
if (this._text !== undefined) {
|
if (this._text !== undefined) {
|
||||||
this.setText(networkSyncData.text);
|
this.setText(networkSyncData.text);
|
||||||
}
|
}
|
||||||
|
@@ -87,16 +87,21 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): LightNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): LightNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
rad: this.getRadius(),
|
rad: this.getRadius(),
|
||||||
col: this.getColor(),
|
col: this.getColor(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(networkSyncData: LightNetworkSyncData): void {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
networkSyncData: LightNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
): void {
|
||||||
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
if (networkSyncData.rad !== undefined) {
|
if (networkSyncData.rad !== undefined) {
|
||||||
this.setRadius(networkSyncData.rad);
|
this.setRadius(networkSyncData.rad);
|
||||||
|
@@ -729,7 +729,9 @@ namespace gdjs {
|
|||||||
behavior.playerNumber = ownerPlayerNumber;
|
behavior.playerNumber = ownerPlayerNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.updateFromNetworkSyncData(messageData);
|
instance.updateFromNetworkSyncData(messageData, {
|
||||||
|
clearMemory: false,
|
||||||
|
});
|
||||||
|
|
||||||
setLastClockReceivedForInstanceOnScene({
|
setLastClockReceivedForInstanceOnScene({
|
||||||
sceneNetworkId,
|
sceneNetworkId,
|
||||||
@@ -1737,7 +1739,7 @@ namespace gdjs {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeScene.updateFromNetworkSyncData(messageData);
|
runtimeScene.updateFromNetworkSyncData(messageData, {});
|
||||||
} else {
|
} else {
|
||||||
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
||||||
// This can happen when joining a game that is already running.
|
// This can happen when joining a game that is already running.
|
||||||
@@ -1890,7 +1892,7 @@ namespace gdjs {
|
|||||||
const messageData = message.getData();
|
const messageData = message.getData();
|
||||||
const messageSender = message.getSender();
|
const messageSender = message.getSender();
|
||||||
if (gdjs.multiplayer.isReadyToSendOrReceiveGameUpdateMessages()) {
|
if (gdjs.multiplayer.isReadyToSendOrReceiveGameUpdateMessages()) {
|
||||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
|
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
|
||||||
} else {
|
} else {
|
||||||
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
// If the game is not ready to receive game update messages, we need to save the data for later use.
|
||||||
// This can happen when joining a game that is already running.
|
// This can happen when joining a game that is already running.
|
||||||
@@ -1918,7 +1920,7 @@ namespace gdjs {
|
|||||||
// Reapply the game saved updates.
|
// Reapply the game saved updates.
|
||||||
lastReceivedGameSyncDataUpdates.getUpdates().forEach((messageData) => {
|
lastReceivedGameSyncDataUpdates.getUpdates().forEach((messageData) => {
|
||||||
debugLogger.info(`Reapplying saved update of game.`);
|
debugLogger.info(`Reapplying saved update of game.`);
|
||||||
runtimeScene.getGame().updateFromNetworkSyncData(messageData);
|
runtimeScene.getGame().updateFromNetworkSyncData(messageData, {});
|
||||||
});
|
});
|
||||||
// Game updates are always applied properly, so we can clear them.
|
// Game updates are always applied properly, so we can clear them.
|
||||||
lastReceivedGameSyncDataUpdates.clear();
|
lastReceivedGameSyncDataUpdates.clear();
|
||||||
@@ -1937,7 +1939,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
debugLogger.info(`Reapplying saved update of scene ${sceneNetworkId}.`);
|
debugLogger.info(`Reapplying saved update of scene ${sceneNetworkId}.`);
|
||||||
|
|
||||||
runtimeScene.updateFromNetworkSyncData(messageData);
|
runtimeScene.updateFromNetworkSyncData(messageData, {});
|
||||||
// We only remove the message if it was successfully applied, so it can be reapplied later,
|
// We only remove the message if it was successfully applied, so it can be reapplied later,
|
||||||
// in case we were not on the right scene.
|
// in case we were not on the right scene.
|
||||||
lastReceivedSceneSyncDataUpdates.remove(messageData);
|
lastReceivedSceneSyncDataUpdates.remove(messageData);
|
||||||
|
@@ -278,7 +278,9 @@ namespace gdjs {
|
|||||||
|
|
||||||
const instanceNetworkId = this._getOrCreateInstanceNetworkId();
|
const instanceNetworkId = this._getOrCreateInstanceNetworkId();
|
||||||
const objectName = this.owner.getName();
|
const objectName = this.owner.getName();
|
||||||
const objectNetworkSyncData = this.owner.getNetworkSyncData();
|
const objectNetworkSyncData = this.owner.getNetworkSyncData({
|
||||||
|
forceSyncEverything: false,
|
||||||
|
});
|
||||||
|
|
||||||
// this._logToConsoleWithThrottle(
|
// this._logToConsoleWithThrottle(
|
||||||
// `Synchronizing object ${this.owner.getName()} (instance ${
|
// `Synchronizing object ${this.owner.getName()} (instance ${
|
||||||
@@ -293,6 +295,8 @@ namespace gdjs {
|
|||||||
x: objectNetworkSyncData.x,
|
x: objectNetworkSyncData.x,
|
||||||
y: objectNetworkSyncData.y,
|
y: objectNetworkSyncData.y,
|
||||||
z: objectNetworkSyncData.z,
|
z: objectNetworkSyncData.z,
|
||||||
|
w: objectNetworkSyncData.w,
|
||||||
|
h: objectNetworkSyncData.h,
|
||||||
zo: objectNetworkSyncData.zo,
|
zo: objectNetworkSyncData.zo,
|
||||||
a: objectNetworkSyncData.a,
|
a: objectNetworkSyncData.a,
|
||||||
hid: objectNetworkSyncData.hid,
|
hid: objectNetworkSyncData.hid,
|
||||||
@@ -300,6 +304,7 @@ namespace gdjs {
|
|||||||
if: objectNetworkSyncData.if,
|
if: objectNetworkSyncData.if,
|
||||||
pfx: objectNetworkSyncData.pfx,
|
pfx: objectNetworkSyncData.pfx,
|
||||||
pfy: objectNetworkSyncData.pfy,
|
pfy: objectNetworkSyncData.pfy,
|
||||||
|
n: objectNetworkSyncData.n,
|
||||||
});
|
});
|
||||||
const shouldSyncObjectBasicInfo =
|
const shouldSyncObjectBasicInfo =
|
||||||
!this._hasObjectBasicInfoBeenSyncedRecently() ||
|
!this._hasObjectBasicInfoBeenSyncedRecently() ||
|
||||||
@@ -369,6 +374,8 @@ namespace gdjs {
|
|||||||
this._lastSentBasicObjectSyncData = {
|
this._lastSentBasicObjectSyncData = {
|
||||||
x: objectNetworkSyncData.x,
|
x: objectNetworkSyncData.x,
|
||||||
y: objectNetworkSyncData.y,
|
y: objectNetworkSyncData.y,
|
||||||
|
w: objectNetworkSyncData.w,
|
||||||
|
h: objectNetworkSyncData.h,
|
||||||
zo: objectNetworkSyncData.zo,
|
zo: objectNetworkSyncData.zo,
|
||||||
a: objectNetworkSyncData.a,
|
a: objectNetworkSyncData.a,
|
||||||
hid: objectNetworkSyncData.hid,
|
hid: objectNetworkSyncData.hid,
|
||||||
@@ -376,6 +383,7 @@ namespace gdjs {
|
|||||||
if: objectNetworkSyncData.if,
|
if: objectNetworkSyncData.if,
|
||||||
pfx: objectNetworkSyncData.pfx,
|
pfx: objectNetworkSyncData.pfx,
|
||||||
pfy: objectNetworkSyncData.pfy,
|
pfy: objectNetworkSyncData.pfy,
|
||||||
|
n: objectNetworkSyncData.n,
|
||||||
};
|
};
|
||||||
this._numberOfForcedBasicObjectUpdates = Math.max(
|
this._numberOfForcedBasicObjectUpdates = Math.max(
|
||||||
this._numberOfForcedBasicObjectUpdates - 1,
|
this._numberOfForcedBasicObjectUpdates - 1,
|
||||||
@@ -443,7 +451,9 @@ namespace gdjs {
|
|||||||
objectOwner: this.playerNumber,
|
objectOwner: this.playerNumber,
|
||||||
objectName,
|
objectName,
|
||||||
instanceNetworkId,
|
instanceNetworkId,
|
||||||
objectNetworkSyncData: this.owner.getNetworkSyncData(),
|
objectNetworkSyncData: this.owner.getNetworkSyncData({
|
||||||
|
forceSyncEverything: false,
|
||||||
|
}),
|
||||||
sceneNetworkId,
|
sceneNetworkId,
|
||||||
});
|
});
|
||||||
this._sendDataToPeersWithIncreasedClock(
|
this._sendDataToPeersWithIncreasedClock(
|
||||||
@@ -593,7 +603,9 @@ namespace gdjs {
|
|||||||
debugLogger.info(
|
debugLogger.info(
|
||||||
'Sending update message to move the object immediately.'
|
'Sending update message to move the object immediately.'
|
||||||
);
|
);
|
||||||
const objectNetworkSyncData = this.owner.getNetworkSyncData();
|
const objectNetworkSyncData = this.owner.getNetworkSyncData({
|
||||||
|
forceSyncEverything: false,
|
||||||
|
});
|
||||||
const {
|
const {
|
||||||
messageName: updateMessageName,
|
messageName: updateMessageName,
|
||||||
messageData: updateMessageData,
|
messageData: updateMessageData,
|
||||||
|
@@ -25,8 +25,6 @@ namespace gdjs {
|
|||||||
export type PanelSpriteObjectData = ObjectData & PanelSpriteObjectDataType;
|
export type PanelSpriteObjectData = ObjectData & PanelSpriteObjectDataType;
|
||||||
|
|
||||||
export type PanelSpriteNetworkSyncDataType = {
|
export type PanelSpriteNetworkSyncDataType = {
|
||||||
wid: number;
|
|
||||||
hei: number;
|
|
||||||
op: number;
|
op: number;
|
||||||
color: string;
|
color: string;
|
||||||
};
|
};
|
||||||
@@ -86,12 +84,6 @@ namespace gdjs {
|
|||||||
oldObjectData: PanelSpriteObjectData,
|
oldObjectData: PanelSpriteObjectData,
|
||||||
newObjectData: PanelSpriteObjectData
|
newObjectData: PanelSpriteObjectData
|
||||||
): boolean {
|
): boolean {
|
||||||
if (oldObjectData.width !== newObjectData.width) {
|
|
||||||
this.setWidth(newObjectData.width);
|
|
||||||
}
|
|
||||||
if (oldObjectData.height !== newObjectData.height) {
|
|
||||||
this.setHeight(newObjectData.height);
|
|
||||||
}
|
|
||||||
let updateTexture = false;
|
let updateTexture = false;
|
||||||
if (oldObjectData.rightMargin !== newObjectData.rightMargin) {
|
if (oldObjectData.rightMargin !== newObjectData.rightMargin) {
|
||||||
this._rBorder = newObjectData.rightMargin;
|
this._rBorder = newObjectData.rightMargin;
|
||||||
@@ -121,29 +113,24 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): PanelSpriteNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): PanelSpriteNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
wid: this.getWidth(),
|
|
||||||
hei: this.getHeight(),
|
|
||||||
op: this.getOpacity(),
|
op: this.getOpacity(),
|
||||||
color: this.getColor(),
|
color: this.getColor(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: PanelSpriteNetworkSyncData
|
networkSyncData: PanelSpriteNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
// Texture is not synchronized, see if this is asked or not.
|
// Texture is not synchronized, see if this is asked or not.
|
||||||
|
|
||||||
if (networkSyncData.wid !== undefined) {
|
|
||||||
this.setWidth(networkSyncData.wid);
|
|
||||||
}
|
|
||||||
if (networkSyncData.hei !== undefined) {
|
|
||||||
this.setHeight(networkSyncData.hei);
|
|
||||||
}
|
|
||||||
if (networkSyncData.op !== undefined) {
|
if (networkSyncData.op !== undefined) {
|
||||||
this.setOpacity(networkSyncData.op);
|
this.setOpacity(networkSyncData.op);
|
||||||
}
|
}
|
||||||
|
@@ -370,9 +370,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): ParticleEmitterObjectNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): ParticleEmitterObjectNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
prms: this.particleRotationMinSpeed,
|
prms: this.particleRotationMinSpeed,
|
||||||
prmx: this.particleRotationMaxSpeed,
|
prmx: this.particleRotationMaxSpeed,
|
||||||
mpc: this.maxParticlesCount,
|
mpc: this.maxParticlesCount,
|
||||||
@@ -399,9 +401,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
syncData: ParticleEmitterObjectNetworkSyncData
|
syncData: ParticleEmitterObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(syncData);
|
super.updateFromNetworkSyncData(syncData, options);
|
||||||
if (syncData.x !== undefined) {
|
if (syncData.x !== undefined) {
|
||||||
this.setX(syncData.x);
|
this.setX(syncData.x);
|
||||||
}
|
}
|
||||||
|
@@ -133,9 +133,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): PathfindingNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
options: GetNetworkSyncDataOptions
|
||||||
|
): PathfindingNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(options),
|
||||||
props: {
|
props: {
|
||||||
path: this._path,
|
path: this._path,
|
||||||
pf: this._pathFound,
|
pf: this._pathFound,
|
||||||
@@ -150,9 +152,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: PathfindingNetworkSyncData
|
networkSyncData: PathfindingNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
const behaviorSpecificProps = networkSyncData.props;
|
const behaviorSpecificProps = networkSyncData.props;
|
||||||
if (behaviorSpecificProps.path !== undefined) {
|
if (behaviorSpecificProps.path !== undefined) {
|
||||||
this._path = behaviorSpecificProps.path;
|
this._path = behaviorSpecificProps.path;
|
||||||
|
@@ -499,7 +499,9 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): Physics2NetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
options: GetNetworkSyncDataOptions
|
||||||
|
): Physics2NetworkSyncData {
|
||||||
const bodyProps = this._body
|
const bodyProps = this._body
|
||||||
? {
|
? {
|
||||||
tpx: this._body.GetTransform().get_p().get_x(),
|
tpx: this._body.GetTransform().get_p().get_x(),
|
||||||
@@ -520,7 +522,7 @@ namespace gdjs {
|
|||||||
aw: undefined,
|
aw: undefined,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(options),
|
||||||
props: {
|
props: {
|
||||||
...bodyProps,
|
...bodyProps,
|
||||||
layers: this.layers,
|
layers: this.layers,
|
||||||
@@ -529,8 +531,11 @@ namespace gdjs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(networkSyncData: Physics2NetworkSyncData) {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
networkSyncData: Physics2NetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
const behaviorSpecificProps = networkSyncData.props;
|
const behaviorSpecificProps = networkSyncData.props;
|
||||||
if (
|
if (
|
||||||
|
@@ -478,7 +478,9 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override getNetworkSyncData(): Physics3DNetworkSyncData {
|
override getNetworkSyncData(
|
||||||
|
options: GetNetworkSyncDataOptions
|
||||||
|
): Physics3DNetworkSyncData {
|
||||||
let bodyProps;
|
let bodyProps;
|
||||||
if (this._body) {
|
if (this._body) {
|
||||||
const position = this._body.GetPosition();
|
const position = this._body.GetPosition();
|
||||||
@@ -520,7 +522,7 @@ namespace gdjs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(options),
|
||||||
props: {
|
props: {
|
||||||
...bodyProps,
|
...bodyProps,
|
||||||
layers: this.layers,
|
layers: this.layers,
|
||||||
@@ -530,9 +532,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override updateFromNetworkSyncData(
|
override updateFromNetworkSyncData(
|
||||||
networkSyncData: Physics3DNetworkSyncData
|
networkSyncData: Physics3DNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
) {
|
) {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
const behaviorSpecificProps = networkSyncData.props;
|
const behaviorSpecificProps = networkSyncData.props;
|
||||||
if (
|
if (
|
||||||
|
@@ -101,7 +101,7 @@ namespace gdjs {
|
|||||||
// This is useful when the object is synchronized by an external source
|
// This is useful when the object is synchronized by an external source
|
||||||
// like in a multiplayer game, and we want to be able to predict the
|
// like in a multiplayer game, and we want to be able to predict the
|
||||||
// movement of the object, even if the inputs are not updated every frame.
|
// movement of the object, even if the inputs are not updated every frame.
|
||||||
private _dontClearInputsBetweenFrames: boolean = false;
|
private _clearInputsBetweenFrames: boolean = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A very small value compare to 1 pixel, yet very huge compare to rounding errors.
|
* A very small value compare to 1 pixel, yet very huge compare to rounding errors.
|
||||||
@@ -268,13 +268,15 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override getNetworkSyncData(): PhysicsCharacter3DNetworkSyncData {
|
override getNetworkSyncData(
|
||||||
|
options: GetNetworkSyncDataOptions
|
||||||
|
): PhysicsCharacter3DNetworkSyncData {
|
||||||
// This method is called, so we are synchronizing this object.
|
// This method is called, so we are synchronizing this object.
|
||||||
// Let's clear the inputs between frames as we control it.
|
// Let's clear the inputs between frames as we control it.
|
||||||
this._dontClearInputsBetweenFrames = false;
|
this._clearInputsBetweenFrames = true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(options),
|
||||||
props: {
|
props: {
|
||||||
fwa: this._forwardAngle,
|
fwa: this._forwardAngle,
|
||||||
fws: this._currentForwardSpeed,
|
fws: this._currentForwardSpeed,
|
||||||
@@ -297,9 +299,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override updateFromNetworkSyncData(
|
override updateFromNetworkSyncData(
|
||||||
networkSyncData: PhysicsCharacter3DNetworkSyncData
|
networkSyncData: PhysicsCharacter3DNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
) {
|
) {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
const behaviorSpecificProps = networkSyncData.props;
|
const behaviorSpecificProps = networkSyncData.props;
|
||||||
this._forwardAngle = behaviorSpecificProps.fwa;
|
this._forwardAngle = behaviorSpecificProps.fwa;
|
||||||
@@ -319,8 +322,8 @@ namespace gdjs {
|
|||||||
this._timeSinceCurrentJumpStart = behaviorSpecificProps.tscjs;
|
this._timeSinceCurrentJumpStart = behaviorSpecificProps.tscjs;
|
||||||
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
|
this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
|
||||||
|
|
||||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
// Clear user inputs between frames only if requested.
|
||||||
this._dontClearInputsBetweenFrames = true;
|
this._clearInputsBetweenFrames = !!options.clearMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
|
getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
|
||||||
@@ -629,7 +632,7 @@ namespace gdjs {
|
|||||||
this._wasJumpKeyPressed = this._hasPressedJumpKey;
|
this._wasJumpKeyPressed = this._hasPressedJumpKey;
|
||||||
this._wasStickUsed = this._hasUsedStick;
|
this._wasStickUsed = this._hasUsedStick;
|
||||||
|
|
||||||
if (!this._dontClearInputsBetweenFrames) {
|
if (this._clearInputsBetweenFrames) {
|
||||||
this._hasPressedForwardKey = false;
|
this._hasPressedForwardKey = false;
|
||||||
this._hasPressedBackwardKey = false;
|
this._hasPressedBackwardKey = false;
|
||||||
this._hasPressedRightKey = false;
|
this._hasPressedRightKey = false;
|
||||||
|
@@ -145,7 +145,7 @@ namespace gdjs {
|
|||||||
// This is useful when the object is synchronized by an external source
|
// This is useful when the object is synchronized by an external source
|
||||||
// like in a multiplayer game, and we want to be able to predict the
|
// like in a multiplayer game, and we want to be able to predict the
|
||||||
// movement of the object, even if the inputs are not updated every frame.
|
// movement of the object, even if the inputs are not updated every frame.
|
||||||
_dontClearInputsBetweenFrames: boolean = false;
|
private _clearInputsBetweenFrames: boolean = true;
|
||||||
// This is useful when the object is synchronized over the network,
|
// This is useful when the object is synchronized over the network,
|
||||||
// object is controlled by the network and we want to ensure the current player
|
// object is controlled by the network and we want to ensure the current player
|
||||||
// cannot control it.
|
// cannot control it.
|
||||||
@@ -221,14 +221,16 @@ namespace gdjs {
|
|||||||
this._state = this._falling;
|
this._state = this._falling;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): PlatformerObjectNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): PlatformerObjectNetworkSyncData {
|
||||||
// This method is called, so we are synchronizing this object.
|
// This method is called, so we are synchronizing this object.
|
||||||
// Let's clear the inputs between frames as we control it.
|
// Let's clear the inputs between frames as we control it.
|
||||||
this._dontClearInputsBetweenFrames = false;
|
this._clearInputsBetweenFrames = true;
|
||||||
this._ignoreDefaultControlsAsSyncedByNetwork = false;
|
this._ignoreDefaultControlsAsSyncedByNetwork = false;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
props: {
|
props: {
|
||||||
cs: this._currentSpeed,
|
cs: this._currentSpeed,
|
||||||
|
|
||||||
@@ -256,9 +258,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: PlatformerObjectNetworkSyncData
|
networkSyncData: PlatformerObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
) {
|
) {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
const behaviorSpecificProps = networkSyncData.props;
|
const behaviorSpecificProps = networkSyncData.props;
|
||||||
if (behaviorSpecificProps.cs !== this._currentSpeed) {
|
if (behaviorSpecificProps.cs !== this._currentSpeed) {
|
||||||
@@ -336,10 +339,10 @@ namespace gdjs {
|
|||||||
this._state.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
this._state.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
// Clear user inputs between frames only if requested.
|
||||||
this._dontClearInputsBetweenFrames = true;
|
this._clearInputsBetweenFrames = !!options.clearMemory;
|
||||||
// And we are not using the default controls.
|
// And we are not using the default controls.
|
||||||
this._ignoreDefaultControlsAsSyncedByNetwork = true;
|
this._ignoreDefaultControlsAsSyncedByNetwork = !options.keepControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||||
@@ -521,8 +524,9 @@ namespace gdjs {
|
|||||||
this._wasJumpKeyPressed = this._jumpKey;
|
this._wasJumpKeyPressed = this._jumpKey;
|
||||||
this._wasReleasePlatformKeyPressed = this._releasePlatformKey;
|
this._wasReleasePlatformKeyPressed = this._releasePlatformKey;
|
||||||
this._wasReleaseLadderKeyPressed = this._releaseLadderKey;
|
this._wasReleaseLadderKeyPressed = this._releaseLadderKey;
|
||||||
|
|
||||||
//4) Do not forget to reset pressed keys
|
//4) Do not forget to reset pressed keys
|
||||||
if (!this._dontClearInputsBetweenFrames) {
|
if (this._clearInputsBetweenFrames) {
|
||||||
// Reset the keys only if the inputs are not supposed to survive between frames.
|
// Reset the keys only if the inputs are not supposed to survive between frames.
|
||||||
// (Most of the time, except if this object is synchronized by an external source)
|
// (Most of the time, except if this object is synchronized by an external source)
|
||||||
this._leftKey = false;
|
this._leftKey = false;
|
||||||
|
99
Extensions/SaveState/JsExtension.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
//@ts-check
|
||||||
|
/// <reference path="../JsExtensionTypes.d.ts" />
|
||||||
|
/**
|
||||||
|
* This is a declaration of an extension for GDevelop 5.
|
||||||
|
*
|
||||||
|
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||||
|
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||||
|
*
|
||||||
|
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||||
|
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||||
|
* and search for any errors.
|
||||||
|
*
|
||||||
|
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {ExtensionModule} */
|
||||||
|
module.exports = {
|
||||||
|
createExtension: function (_, gd) {
|
||||||
|
const extension = new gd.PlatformExtension();
|
||||||
|
extension
|
||||||
|
.setExtensionInformation(
|
||||||
|
'SaveState',
|
||||||
|
_('Save State'),
|
||||||
|
|
||||||
|
'This allows to save and load whole games.',
|
||||||
|
'Neyl Mahfouf',
|
||||||
|
'Gdevelop'
|
||||||
|
)
|
||||||
|
.setExtensionHelpPath('/all-features/save-state')
|
||||||
|
.setCategory('Save & Load');
|
||||||
|
extension
|
||||||
|
.addInstructionOrExpressionGroupMetadata(_('Save State'))
|
||||||
|
.setIcon('JsPlatform/Extensions/snapshotsave.svg');
|
||||||
|
|
||||||
|
// TODO: Split save action and load action into 2 different instructions to avoid
|
||||||
|
// having optional and empty parameters.
|
||||||
|
extension
|
||||||
|
.addAction(
|
||||||
|
'SaveGameSnapshot',
|
||||||
|
_('Save game'),
|
||||||
|
_(
|
||||||
|
'Takes a snapshot of the game and save it to a variable or device storage.'
|
||||||
|
),
|
||||||
|
_(
|
||||||
|
'Save the game to variable _PARAM1_ or to storage under key _PARAM2_.'
|
||||||
|
),
|
||||||
|
'',
|
||||||
|
'res/actions/Save-single-action-down.svg',
|
||||||
|
'res/actions/Save-single-action-down.svg'
|
||||||
|
)
|
||||||
|
.addCodeOnlyParameter('currentScene', '')
|
||||||
|
.addParameter(
|
||||||
|
'variable',
|
||||||
|
_('Variable to store the save to (optional)'),
|
||||||
|
'',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.addParameter('string', _('Storage key to save to (optional)'), '', true)
|
||||||
|
.getCodeExtraInformation()
|
||||||
|
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||||
|
.setFunctionName('gdjs.saveState.saveGameSnapshot');
|
||||||
|
|
||||||
|
extension
|
||||||
|
.addAction(
|
||||||
|
'LoadGameSnapshot',
|
||||||
|
_('Load game'),
|
||||||
|
_('Load game from snapshot save from a variable or storage.'),
|
||||||
|
_(
|
||||||
|
'Load the game from variable _PARAM1_ or from device storage under key _PARAM2_.'
|
||||||
|
),
|
||||||
|
'',
|
||||||
|
'res/actions/Save-single-action-up.svg',
|
||||||
|
'res/actions/Save-single-action-up.svg'
|
||||||
|
)
|
||||||
|
.addCodeOnlyParameter('currentScene', '')
|
||||||
|
.addParameter(
|
||||||
|
'variable',
|
||||||
|
_('Variable to load game from (optional)'),
|
||||||
|
'',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.addParameter(
|
||||||
|
'string',
|
||||||
|
_('Storage key to load game from (optional)'),
|
||||||
|
'',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.getCodeExtraInformation()
|
||||||
|
.setIncludeFile('Extensions/SaveState/savestatetools.js')
|
||||||
|
.setFunctionName('gdjs.saveState.loadGameFromSnapshot');
|
||||||
|
|
||||||
|
// TODO: Add condition and expression to get the last save creation datetime.
|
||||||
|
|
||||||
|
return extension;
|
||||||
|
},
|
||||||
|
runExtensionSanityTests: function (gd, extension) {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
};
|
78
Extensions/SaveState/savestatetools.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
namespace gdjs {
|
||||||
|
export namespace saveState {
|
||||||
|
export const INDEXED_DB_NAME: string = 'gd-game-snapshot-saves';
|
||||||
|
export const INDEXED_DB_KEY: string = 'game_save'; // TODO: use game id to change the key.
|
||||||
|
export const INDEXED_DB_OBJECT_STORE: string = 'saves'; // TODO: should a store be used per game on the user device?
|
||||||
|
|
||||||
|
export const saveGameSnapshot = async function (
|
||||||
|
currentScene: RuntimeScene,
|
||||||
|
sceneVar?: gdjs.Variable,
|
||||||
|
storageName?: string
|
||||||
|
) {
|
||||||
|
let allSyncData: GameSaveState = {
|
||||||
|
gameNetworkSyncData: {},
|
||||||
|
layoutNetworkSyncDatas: [],
|
||||||
|
};
|
||||||
|
const gameData = currentScene
|
||||||
|
.getGame()
|
||||||
|
.getNetworkSyncData({ forceSyncEverything: true });
|
||||||
|
const sceneStack = currentScene.getGame()._sceneStack._stack;
|
||||||
|
if (gameData) {
|
||||||
|
allSyncData.gameNetworkSyncData = gameData;
|
||||||
|
}
|
||||||
|
if (sceneStack) {
|
||||||
|
sceneStack.forEach((scene, index) => {
|
||||||
|
const sceneDatas = scene.getNetworkSyncData({
|
||||||
|
forceSyncEverything: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sceneDatas) {
|
||||||
|
allSyncData.layoutNetworkSyncDatas[index] = {
|
||||||
|
sceneData: {} as LayoutNetworkSyncData,
|
||||||
|
objectDatas: {},
|
||||||
|
};
|
||||||
|
allSyncData.layoutNetworkSyncDatas[index].sceneData = sceneDatas;
|
||||||
|
const sceneRuntimeObjects = scene.getAdhocListOfAllInstances();
|
||||||
|
const syncOptions: GetNetworkSyncDataOptions = {
|
||||||
|
forceSyncEverything: true,
|
||||||
|
};
|
||||||
|
for (const key in sceneRuntimeObjects) {
|
||||||
|
if (sceneRuntimeObjects.hasOwnProperty(key)) {
|
||||||
|
const object = sceneRuntimeObjects[key];
|
||||||
|
const syncData = object.getNetworkSyncData(syncOptions);
|
||||||
|
allSyncData.layoutNetworkSyncDatas[index].objectDatas[
|
||||||
|
object.id
|
||||||
|
] = syncData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// TODO: Store the save creation datetime.
|
||||||
|
if (sceneVar && sceneVar !== gdjs.VariablesContainer.badVariable) {
|
||||||
|
sceneVar.fromJSObject(allSyncData);
|
||||||
|
} else {
|
||||||
|
await gdjs.saveToIndexedDB(
|
||||||
|
INDEXED_DB_NAME,
|
||||||
|
INDEXED_DB_OBJECT_STORE,
|
||||||
|
storageName || INDEXED_DB_KEY,
|
||||||
|
allSyncData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadGameFromSnapshot = async function (
|
||||||
|
currentScene: RuntimeScene,
|
||||||
|
sceneVar?: gdjs.Variable,
|
||||||
|
storageName?: string
|
||||||
|
) {
|
||||||
|
currentScene.requestLoadSnapshot({
|
||||||
|
loadVariable:
|
||||||
|
sceneVar && sceneVar !== gdjs.VariablesContainer.badVariable
|
||||||
|
? sceneVar
|
||||||
|
: null,
|
||||||
|
loadStorageName: storageName || INDEXED_DB_KEY,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -113,9 +113,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): SpineNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): SpineNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
opa: this._opacity,
|
opa: this._opacity,
|
||||||
wid: this.getWidth(),
|
wid: this.getWidth(),
|
||||||
hei: this.getHeight(),
|
hei: this.getHeight(),
|
||||||
@@ -131,8 +133,11 @@ namespace gdjs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(syncData: SpineNetworkSyncData): void {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(syncData);
|
syncData: SpineNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
): void {
|
||||||
|
super.updateFromNetworkSyncData(syncData, options);
|
||||||
|
|
||||||
if (syncData.opa !== undefined && syncData.opa !== this._opacity) {
|
if (syncData.opa !== undefined && syncData.opa !== this._opacity) {
|
||||||
this.setOpacity(syncData.opa);
|
this.setOpacity(syncData.opa);
|
||||||
|
@@ -256,9 +256,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): TextInputNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): TextInputNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
opa: this.getOpacity(),
|
opa: this.getOpacity(),
|
||||||
wid: this.getWidth(),
|
wid: this.getWidth(),
|
||||||
hei: this.getHeight(),
|
hei: this.getHeight(),
|
||||||
@@ -278,8 +280,11 @@ namespace gdjs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(syncData: TextInputNetworkSyncData): void {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(syncData);
|
syncData: TextInputNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
): void {
|
||||||
|
super.updateFromNetworkSyncData(syncData, options);
|
||||||
|
|
||||||
if (syncData.opa !== undefined) this.setOpacity(syncData.opa);
|
if (syncData.opa !== undefined) this.setOpacity(syncData.opa);
|
||||||
if (syncData.wid !== undefined) this.setWidth(syncData.wid);
|
if (syncData.wid !== undefined) this.setWidth(syncData.wid);
|
||||||
|
@@ -214,9 +214,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): TextObjectNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): TextObjectNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
str: this._str,
|
str: this._str,
|
||||||
o: this.opacity,
|
o: this.opacity,
|
||||||
cs: this._characterSize,
|
cs: this._characterSize,
|
||||||
@@ -243,9 +245,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: TextObjectNetworkSyncData
|
networkSyncData: TextObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
if (networkSyncData.str !== undefined) {
|
if (networkSyncData.str !== undefined) {
|
||||||
this.setText(networkSyncData.str);
|
this.setText(networkSyncData.str);
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,6 @@ namespace gdjs {
|
|||||||
export type SimpleTileMapNetworkSyncDataType = {
|
export type SimpleTileMapNetworkSyncDataType = {
|
||||||
op: number;
|
op: number;
|
||||||
ai: string;
|
ai: string;
|
||||||
wid: number;
|
|
||||||
hei: number;
|
|
||||||
// TODO: Support tilemap synchronization. Find an efficient way to send tiles changes.
|
// TODO: Support tilemap synchronization. Find an efficient way to send tiles changes.
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -165,20 +163,21 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): SimpleTileMapNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): SimpleTileMapNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
op: this._opacity,
|
op: this._opacity,
|
||||||
ai: this._atlasImage,
|
ai: this._atlasImage,
|
||||||
wid: this.getWidth(),
|
|
||||||
hei: this.getHeight(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: SimpleTileMapNetworkSyncData
|
networkSyncData: SimpleTileMapNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
networkSyncData.op !== undefined &&
|
networkSyncData.op !== undefined &&
|
||||||
@@ -186,18 +185,6 @@ namespace gdjs {
|
|||||||
) {
|
) {
|
||||||
this.setOpacity(networkSyncData.op);
|
this.setOpacity(networkSyncData.op);
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
networkSyncData.wid !== undefined &&
|
|
||||||
networkSyncData.wid !== this.getWidth()
|
|
||||||
) {
|
|
||||||
this.setWidth(networkSyncData.wid);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
networkSyncData.hei !== undefined &&
|
|
||||||
networkSyncData.hei !== this.getHeight()
|
|
||||||
) {
|
|
||||||
this.setHeight(networkSyncData.hei);
|
|
||||||
}
|
|
||||||
if (networkSyncData.ai !== undefined) {
|
if (networkSyncData.ai !== undefined) {
|
||||||
// TODO: support changing the atlas texture
|
// TODO: support changing the atlas texture
|
||||||
}
|
}
|
||||||
|
@@ -26,8 +26,6 @@ namespace gdjs {
|
|||||||
os: float;
|
os: float;
|
||||||
fo: float;
|
fo: float;
|
||||||
oo: float;
|
oo: float;
|
||||||
wid: float;
|
|
||||||
hei: float;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TilemapCollisionMaskNetworkSyncData = ObjectNetworkSyncData &
|
export type TilemapCollisionMaskNetworkSyncData = ObjectNetworkSyncData &
|
||||||
@@ -191,9 +189,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): TilemapCollisionMaskNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): TilemapCollisionMaskNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
tmjf: this.getTilemapJsonFile(),
|
tmjf: this.getTilemapJsonFile(),
|
||||||
tsjf: this.getTilesetJsonFile(),
|
tsjf: this.getTilesetJsonFile(),
|
||||||
dm: this.getDebugMode(),
|
dm: this.getDebugMode(),
|
||||||
@@ -202,15 +202,14 @@ namespace gdjs {
|
|||||||
os: this.getOutlineSize(),
|
os: this.getOutlineSize(),
|
||||||
fo: this.getFillOpacity(),
|
fo: this.getFillOpacity(),
|
||||||
oo: this.getOutlineOpacity(),
|
oo: this.getOutlineOpacity(),
|
||||||
wid: this.getWidth(),
|
|
||||||
hei: this.getHeight(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: TilemapCollisionMaskNetworkSyncData
|
networkSyncData: TilemapCollisionMaskNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
if (networkSyncData.tmjf !== undefined) {
|
if (networkSyncData.tmjf !== undefined) {
|
||||||
this.setTilemapJsonFile(networkSyncData.tmjf);
|
this.setTilemapJsonFile(networkSyncData.tmjf);
|
||||||
@@ -236,12 +235,6 @@ namespace gdjs {
|
|||||||
if (networkSyncData.oo !== undefined) {
|
if (networkSyncData.oo !== undefined) {
|
||||||
this.setOutlineOpacity(networkSyncData.oo);
|
this.setOutlineOpacity(networkSyncData.oo);
|
||||||
}
|
}
|
||||||
if (networkSyncData.wid !== undefined) {
|
|
||||||
this.setWidth(networkSyncData.wid);
|
|
||||||
}
|
|
||||||
if (networkSyncData.hei !== undefined) {
|
|
||||||
this.setHeight(networkSyncData.hei);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extraInitializationFromInitialInstance(initialInstanceData): void {
|
extraInitializationFromInitialInstance(initialInstanceData): void {
|
||||||
|
@@ -25,8 +25,6 @@ namespace gdjs {
|
|||||||
lai: number;
|
lai: number;
|
||||||
lei: number;
|
lei: number;
|
||||||
asps: number;
|
asps: number;
|
||||||
wid: number;
|
|
||||||
hei: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TilemapNetworkSyncData = ObjectNetworkSyncData &
|
export type TilemapNetworkSyncData = ObjectNetworkSyncData &
|
||||||
@@ -147,9 +145,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): TilemapNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): TilemapNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
op: this._opacity,
|
op: this._opacity,
|
||||||
tmjf: this._tilemapJsonFile,
|
tmjf: this._tilemapJsonFile,
|
||||||
tsjf: this._tilesetJsonFile,
|
tsjf: this._tilesetJsonFile,
|
||||||
@@ -158,13 +158,14 @@ namespace gdjs {
|
|||||||
lai: this._layerIndex,
|
lai: this._layerIndex,
|
||||||
lei: this._levelIndex,
|
lei: this._levelIndex,
|
||||||
asps: this._animationSpeedScale,
|
asps: this._animationSpeedScale,
|
||||||
wid: this.getWidth(),
|
|
||||||
hei: this.getHeight(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(networkSyncData: TilemapNetworkSyncData): void {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
networkSyncData: TilemapNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
): void {
|
||||||
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
if (networkSyncData.op !== undefined) {
|
if (networkSyncData.op !== undefined) {
|
||||||
this.setOpacity(networkSyncData.op);
|
this.setOpacity(networkSyncData.op);
|
||||||
@@ -190,12 +191,6 @@ namespace gdjs {
|
|||||||
if (networkSyncData.asps !== undefined) {
|
if (networkSyncData.asps !== undefined) {
|
||||||
this.setAnimationSpeedScale(networkSyncData.asps);
|
this.setAnimationSpeedScale(networkSyncData.asps);
|
||||||
}
|
}
|
||||||
if (networkSyncData.wid !== undefined) {
|
|
||||||
this.setWidth(networkSyncData.wid);
|
|
||||||
}
|
|
||||||
if (networkSyncData.hei !== undefined) {
|
|
||||||
this.setHeight(networkSyncData.hei);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extraInitializationFromInitialInstance(initialInstanceData): void {
|
extraInitializationFromInitialInstance(initialInstanceData): void {
|
||||||
|
@@ -15,8 +15,6 @@ namespace gdjs {
|
|||||||
export type TiledSpriteObjectData = ObjectData & TiledSpriteObjectDataType;
|
export type TiledSpriteObjectData = ObjectData & TiledSpriteObjectDataType;
|
||||||
|
|
||||||
export type TiledSpriteNetworkSyncDataType = {
|
export type TiledSpriteNetworkSyncDataType = {
|
||||||
wid: number;
|
|
||||||
hei: number;
|
|
||||||
xo: number;
|
xo: number;
|
||||||
yo: number;
|
yo: number;
|
||||||
op: number;
|
op: number;
|
||||||
@@ -80,11 +78,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): TiledSpriteNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptons: GetNetworkSyncDataOptions
|
||||||
|
): TiledSpriteNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptons),
|
||||||
wid: this.getWidth(),
|
|
||||||
hei: this.getHeight(),
|
|
||||||
xo: this.getXOffset(),
|
xo: this.getXOffset(),
|
||||||
yo: this.getYOffset(),
|
yo: this.getYOffset(),
|
||||||
op: this.getOpacity(),
|
op: this.getOpacity(),
|
||||||
@@ -93,18 +91,13 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: TiledSpriteNetworkSyncData
|
networkSyncData: TiledSpriteNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
// Texture is not synchronized, see if this is asked or not.
|
// Texture is not synchronized, see if this is asked or not.
|
||||||
|
|
||||||
if (networkSyncData.wid !== undefined) {
|
|
||||||
this.setWidth(networkSyncData.wid);
|
|
||||||
}
|
|
||||||
if (networkSyncData.hei !== undefined) {
|
|
||||||
this.setHeight(networkSyncData.hei);
|
|
||||||
}
|
|
||||||
if (networkSyncData.xo !== undefined) {
|
if (networkSyncData.xo !== undefined) {
|
||||||
this.setXOffset(networkSyncData.xo);
|
this.setXOffset(networkSyncData.xo);
|
||||||
}
|
}
|
||||||
|
@@ -62,7 +62,7 @@ namespace gdjs {
|
|||||||
// This is useful when the object is synchronized by an external source
|
// This is useful when the object is synchronized by an external source
|
||||||
// like in a multiplayer game, and we want to be able to predict the
|
// like in a multiplayer game, and we want to be able to predict the
|
||||||
// movement of the object, even if the inputs are not updated every frame.
|
// movement of the object, even if the inputs are not updated every frame.
|
||||||
_dontClearInputsBetweenFrames: boolean = false;
|
private _clearInputsBetweenFrames: boolean = true;
|
||||||
// This is useful when the object is synchronized over the network,
|
// This is useful when the object is synchronized over the network,
|
||||||
// object is controlled by the network and we want to ensure the current player
|
// object is controlled by the network and we want to ensure the current player
|
||||||
// cannot control it.
|
// cannot control it.
|
||||||
@@ -104,14 +104,16 @@ namespace gdjs {
|
|||||||
this._movementAngleOffset = behaviorData.movementAngleOffset || 0;
|
this._movementAngleOffset = behaviorData.movementAngleOffset || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): TopDownMovementNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
options: GetNetworkSyncDataOptions
|
||||||
|
): TopDownMovementNetworkSyncData {
|
||||||
// This method is called, so we are synchronizing this object.
|
// This method is called, so we are synchronizing this object.
|
||||||
// Let's clear the inputs between frames as we control it.
|
// Let's clear the inputs between frames as we control it.
|
||||||
this._dontClearInputsBetweenFrames = false;
|
this._clearInputsBetweenFrames = true;
|
||||||
this._ignoreDefaultControlsAsSyncedByNetwork = false;
|
this._ignoreDefaultControlsAsSyncedByNetwork = false;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(options),
|
||||||
props: {
|
props: {
|
||||||
a: this._angle,
|
a: this._angle,
|
||||||
xv: this._xVelocity,
|
xv: this._xVelocity,
|
||||||
@@ -129,9 +131,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(
|
updateFromNetworkSyncData(
|
||||||
networkSyncData: TopDownMovementNetworkSyncData
|
networkSyncData: TopDownMovementNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
): void {
|
): void {
|
||||||
super.updateFromNetworkSyncData(networkSyncData);
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
|
||||||
const behaviorSpecificProps = networkSyncData.props;
|
const behaviorSpecificProps = networkSyncData.props;
|
||||||
if (behaviorSpecificProps.a !== undefined) {
|
if (behaviorSpecificProps.a !== undefined) {
|
||||||
@@ -168,10 +171,10 @@ namespace gdjs {
|
|||||||
this._stickForce = behaviorSpecificProps.sf;
|
this._stickForce = behaviorSpecificProps.sf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the object is synchronized from the network, the inputs must not be cleared.
|
// Clear user inputs between frames only if requested.
|
||||||
this._dontClearInputsBetweenFrames = true;
|
this._clearInputsBetweenFrames = !!options.clearMemory;
|
||||||
// And we are not using the default controls.
|
// And we are not using the default controls.
|
||||||
this._ignoreDefaultControlsAsSyncedByNetwork = true;
|
this._ignoreDefaultControlsAsSyncedByNetwork = !options.keepControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
|
||||||
@@ -584,7 +587,7 @@ namespace gdjs {
|
|||||||
this._wasRightKeyPressed = this._rightKey;
|
this._wasRightKeyPressed = this._rightKey;
|
||||||
this._wasUpKeyPressed = this._upKey;
|
this._wasUpKeyPressed = this._upKey;
|
||||||
this._wasDownKeyPressed = this._downKey;
|
this._wasDownKeyPressed = this._downKey;
|
||||||
if (!this._dontClearInputsBetweenFrames) {
|
if (this._clearInputsBetweenFrames) {
|
||||||
this._leftKey = false;
|
this._leftKey = false;
|
||||||
this._rightKey = false;
|
this._rightKey = false;
|
||||||
this._upKey = false;
|
this._upKey = false;
|
||||||
|
@@ -59,6 +59,214 @@ namespace gdjs {
|
|||||||
const exponentialInterpolation =
|
const exponentialInterpolation =
|
||||||
gdjs.evtTools.common.exponentialInterpolation;
|
gdjs.evtTools.common.exponentialInterpolation;
|
||||||
|
|
||||||
|
// TODO: Use this factory to get the tween setter and store only type and options
|
||||||
|
// in tween instance.
|
||||||
|
const tweenSetterFactory =
|
||||||
|
(object: RuntimeObject) => (type: string, options: any) => {
|
||||||
|
if (type === 'variable') {
|
||||||
|
// TODO: Find variable.
|
||||||
|
// return (value: float) => variable.setNumber(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'positionX') {
|
||||||
|
return (value: float) => object.setX(value);
|
||||||
|
}
|
||||||
|
if (type === 'positionY') {
|
||||||
|
return (value: float) => object.setY(value);
|
||||||
|
}
|
||||||
|
if (type === 'position') {
|
||||||
|
return ([x, y]) => object.setPosition(x, y);
|
||||||
|
}
|
||||||
|
if (type === 'positionZ') {
|
||||||
|
if (!is3D(object)) return () => {};
|
||||||
|
return (value: float) => object.setZ(value);
|
||||||
|
}
|
||||||
|
if (type === 'width') {
|
||||||
|
return (value: float) => object.setWidth(value);
|
||||||
|
}
|
||||||
|
if (type === 'height') {
|
||||||
|
return (value: float) => object.setHeight(value);
|
||||||
|
}
|
||||||
|
if (type === 'depth') {
|
||||||
|
if (!is3D(object)) return () => {};
|
||||||
|
return (value: float) => object.setDepth(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'angle') {
|
||||||
|
return (value: float) => object.setAngle(value);
|
||||||
|
}
|
||||||
|
if (type === 'rotationX') {
|
||||||
|
if (!is3D(object)) return () => {};
|
||||||
|
return (value: float) => object.setRotationX(value);
|
||||||
|
}
|
||||||
|
if (type === 'rotationY') {
|
||||||
|
if (!is3D(object)) return () => {};
|
||||||
|
return (value: float) => object.setRotationY(value);
|
||||||
|
}
|
||||||
|
if (type === 'scale') {
|
||||||
|
if (!isScalable(object)) return;
|
||||||
|
|
||||||
|
// This action doesn't require 3D capabilities.
|
||||||
|
// So, gdjs.RuntimeObject3D may not exist
|
||||||
|
// when the 3D extension is not used.
|
||||||
|
const object3d = is3D(object) ? object : null;
|
||||||
|
const setValue = options.scaleFromCenterOfObject
|
||||||
|
? (scale: float) => {
|
||||||
|
const oldX = object.getCenterXInScene();
|
||||||
|
const oldY = object.getCenterYInScene();
|
||||||
|
const oldZ = object3d ? object3d.getCenterZInScene() : 0;
|
||||||
|
object.setScale(scale);
|
||||||
|
object.setCenterXInScene(oldX);
|
||||||
|
object.setCenterYInScene(oldY);
|
||||||
|
if (object3d) {
|
||||||
|
object3d.setCenterZInScene(oldZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: (scale: float) => object.setScale(scale);
|
||||||
|
|
||||||
|
return setValue;
|
||||||
|
}
|
||||||
|
if (type === 'scaleXAndY') {
|
||||||
|
if (!isScalable(object)) return;
|
||||||
|
|
||||||
|
const setValue = options.scaleFromCenterOfObject
|
||||||
|
? ([scaleX, scaleY]: float[]) => {
|
||||||
|
const oldX = object.getCenterXInScene();
|
||||||
|
const oldY = object.getCenterYInScene();
|
||||||
|
object.setScaleX(scaleX);
|
||||||
|
object.setScaleY(scaleY);
|
||||||
|
object.setCenterPositionInScene(oldX, oldY);
|
||||||
|
}
|
||||||
|
: ([scaleX, scaleY]: float[]) => {
|
||||||
|
object.setScaleX(scaleX);
|
||||||
|
object.setScaleY(scaleY);
|
||||||
|
};
|
||||||
|
return setValue;
|
||||||
|
}
|
||||||
|
if (type === 'scaleX') {
|
||||||
|
if (!isScalable(object)) return;
|
||||||
|
|
||||||
|
const setValue = options.scaleFromCenterOfObject
|
||||||
|
? (scaleX: float) => {
|
||||||
|
const oldX = object.getCenterXInScene();
|
||||||
|
object.setScaleX(scaleX);
|
||||||
|
object.setCenterXInScene(oldX);
|
||||||
|
}
|
||||||
|
: (scaleX: float) => object.setScaleX(scaleX);
|
||||||
|
|
||||||
|
return setValue;
|
||||||
|
}
|
||||||
|
if (type === 'scaleY') {
|
||||||
|
if (!isScalable(object)) return;
|
||||||
|
|
||||||
|
const setValue = options.scaleFromCenterOfObject
|
||||||
|
? (scaleY: float) => {
|
||||||
|
const oldY = object.getCenterYInScene();
|
||||||
|
object.setScaleY(scaleY);
|
||||||
|
object.setCenterYInScene(oldY);
|
||||||
|
}
|
||||||
|
: (scaleY: float) => object.setScaleY(scaleY);
|
||||||
|
|
||||||
|
return setValue;
|
||||||
|
}
|
||||||
|
if (type === 'opacity') {
|
||||||
|
if (!isOpaque(object)) return () => {};
|
||||||
|
return (value: float) => object.setOpacity(value);
|
||||||
|
}
|
||||||
|
if (type === 'characterSize') {
|
||||||
|
if (!isCharacterScalable(object)) return () => {};
|
||||||
|
return (value: float) => object.setCharacterSize(value);
|
||||||
|
}
|
||||||
|
if (type === 'numberEffectProperty') {
|
||||||
|
const effect = object.getRendererEffects()[options.effectName];
|
||||||
|
if (!effect) {
|
||||||
|
logger.error(
|
||||||
|
`The object "${object.name}" doesn't have any effect called "${options.effectName}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (value: float) => {
|
||||||
|
effect.updateDoubleParameter(options.propertyName, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type === 'colorEffectProperty') {
|
||||||
|
const effect = object.getRendererEffects()[options.effectName];
|
||||||
|
if (!effect) {
|
||||||
|
logger.error(
|
||||||
|
`The object "${object.name}" doesn't have any effect called "${options.effectName}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ([hue, saturation, lightness]) => {
|
||||||
|
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
|
||||||
|
hue,
|
||||||
|
saturation,
|
||||||
|
lightness
|
||||||
|
);
|
||||||
|
effect.updateColorParameter(
|
||||||
|
options.propertyName,
|
||||||
|
gdjs.rgbToHexNumber(
|
||||||
|
rgbFromHslColor[0],
|
||||||
|
rgbFromHslColor[1],
|
||||||
|
rgbFromHslColor[2]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'objectColor') {
|
||||||
|
if (!isColorable(object)) return;
|
||||||
|
|
||||||
|
if (options.useHSLColorTransition) {
|
||||||
|
const setValue = ([hue, saturation, lightness]) => {
|
||||||
|
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
|
||||||
|
hue,
|
||||||
|
saturation,
|
||||||
|
lightness
|
||||||
|
);
|
||||||
|
object.setColor(
|
||||||
|
Math.floor(rgbFromHslColor[0]) +
|
||||||
|
';' +
|
||||||
|
Math.floor(rgbFromHslColor[1]) +
|
||||||
|
';' +
|
||||||
|
Math.floor(rgbFromHslColor[2])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return setValue;
|
||||||
|
} else {
|
||||||
|
const setValue = ([red, green, blue]) => {
|
||||||
|
object.setColor(
|
||||||
|
Math.floor(red) + ';' + Math.floor(green) + ';' + Math.floor(blue)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return setValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type === 'objectColorHSL') {
|
||||||
|
if (!isColorable(object)) return;
|
||||||
|
|
||||||
|
const setValue = ([hue, saturation, lightness]) => {
|
||||||
|
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
|
||||||
|
hue,
|
||||||
|
saturation,
|
||||||
|
lightness
|
||||||
|
);
|
||||||
|
|
||||||
|
object.setColor(
|
||||||
|
Math.floor(rgbFromHslColor[0]) +
|
||||||
|
';' +
|
||||||
|
Math.floor(rgbFromHslColor[1]) +
|
||||||
|
';' +
|
||||||
|
Math.floor(rgbFromHslColor[2])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return setValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'objectValue' type doesn't set anything.
|
||||||
|
return () => {};
|
||||||
|
};
|
||||||
|
|
||||||
export class TweenRuntimeBehavior extends gdjs.RuntimeBehavior {
|
export class TweenRuntimeBehavior extends gdjs.RuntimeBehavior {
|
||||||
private _tweens = new gdjs.evtTools.tween.TweenManager();
|
private _tweens = new gdjs.evtTools.tween.TweenManager();
|
||||||
private _isActive: boolean = true;
|
private _isActive: boolean = true;
|
||||||
|
@@ -10,6 +10,71 @@ namespace gdjs {
|
|||||||
export namespace tween {
|
export namespace tween {
|
||||||
const logger = new gdjs.Logger('Tween');
|
const logger = new gdjs.Logger('Tween');
|
||||||
|
|
||||||
|
// TODO: Use this factory to get the tween setter and store only type and options
|
||||||
|
// in tween instance.
|
||||||
|
const tweenSetterFactory =
|
||||||
|
(runtimeScene: RuntimeScene) => (type: string, options: any) => {
|
||||||
|
if (type === 'variable') {
|
||||||
|
// TODO: Find variable.
|
||||||
|
// return (value: float) => variable.setNumber(value)
|
||||||
|
}
|
||||||
|
if (type === 'cameraZoom') {
|
||||||
|
const layer = runtimeScene.getLayer(options.layerName);
|
||||||
|
return (value: float) => layer.setCameraZoom(value);
|
||||||
|
}
|
||||||
|
if (type === 'cameraRotation') {
|
||||||
|
const layer = runtimeScene.getLayer(options.layerName);
|
||||||
|
return (value: float) => layer.setCameraRotation(value);
|
||||||
|
}
|
||||||
|
if (type === 'cameraPosition') {
|
||||||
|
const layer = runtimeScene.getLayer(options.layerName);
|
||||||
|
return ([x, y]) => {
|
||||||
|
layer.setCameraX(x);
|
||||||
|
layer.setCameraY(y);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type === 'colorEffectProperty') {
|
||||||
|
const layer = runtimeScene.getLayer(options.layerName);
|
||||||
|
const effect = layer.getRendererEffects()[options.effectName];
|
||||||
|
if (!effect) {
|
||||||
|
logger.error(
|
||||||
|
`The layer "${options.layerName}" doesn't have any effect called "${options.effectName}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ([hue, saturation, lightness]) => {
|
||||||
|
const rgbFromHslColor = gdjs.evtTools.tween.hslToRgb(
|
||||||
|
hue,
|
||||||
|
saturation,
|
||||||
|
lightness
|
||||||
|
);
|
||||||
|
effect.updateColorParameter(
|
||||||
|
options.propertyName,
|
||||||
|
gdjs.rgbToHexNumber(
|
||||||
|
rgbFromHslColor[0],
|
||||||
|
rgbFromHslColor[1],
|
||||||
|
rgbFromHslColor[2]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type === 'numberEffectProperty') {
|
||||||
|
const layer = runtimeScene.getLayer(options.layerName);
|
||||||
|
const effect = layer.getRendererEffects()[options.effectName];
|
||||||
|
if (!effect) {
|
||||||
|
logger.error(
|
||||||
|
`The layer "${options.layerName}" doesn't have any effect called "${options.effectName}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (value: float) => {
|
||||||
|
effect.updateDoubleParameter(options.propertyName, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'layoutValue' and 'layerValue' types don't set anything.
|
||||||
|
return () => {};
|
||||||
|
};
|
||||||
|
|
||||||
export const getTweensMap = (runtimeScene: RuntimeScene) =>
|
export const getTweensMap = (runtimeScene: RuntimeScene) =>
|
||||||
runtimeScene._tweens ||
|
runtimeScene._tweens ||
|
||||||
(runtimeScene._tweens = new gdjs.evtTools.tween.TweenManager());
|
(runtimeScene._tweens = new gdjs.evtTools.tween.TweenManager());
|
||||||
|
@@ -16,7 +16,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
export type VideoObjectData = ObjectData & VideoObjectDataType;
|
export type VideoObjectData = ObjectData & VideoObjectDataType;
|
||||||
|
|
||||||
export type VideoNetworkSyncDataType = {
|
export type VideoObjectNetworkSyncDataType = {
|
||||||
op: float;
|
op: float;
|
||||||
wid: float;
|
wid: float;
|
||||||
hei: float;
|
hei: float;
|
||||||
@@ -27,8 +27,8 @@ namespace gdjs {
|
|||||||
ps: number;
|
ps: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VideoNetworkSyncData = ObjectNetworkSyncData &
|
export type VideoObjectNetworkSyncData = ObjectNetworkSyncData &
|
||||||
VideoNetworkSyncDataType;
|
VideoObjectNetworkSyncDataType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object displaying a video on screen.
|
* An object displaying a video on screen.
|
||||||
@@ -101,9 +101,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): VideoNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): VideoObjectNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
op: this._opacity,
|
op: this._opacity,
|
||||||
wid: this.getWidth(),
|
wid: this.getWidth(),
|
||||||
hei: this.getHeight(),
|
hei: this.getHeight(),
|
||||||
@@ -114,8 +116,11 @@ namespace gdjs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(syncData: VideoNetworkSyncData): void {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(syncData);
|
syncData: VideoObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
): void {
|
||||||
|
super.updateFromNetworkSyncData(syncData, options);
|
||||||
|
|
||||||
if (this._opacity !== undefined && this._opacity && syncData.op) {
|
if (this._opacity !== undefined && this._opacity && syncData.op) {
|
||||||
this.setOpacity(syncData.op);
|
this.setOpacity(syncData.op);
|
||||||
|
@@ -844,6 +844,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
|||||||
InsertUnique(includesFiles, "CustomRuntimeObjectInstanceContainer.js");
|
InsertUnique(includesFiles, "CustomRuntimeObjectInstanceContainer.js");
|
||||||
InsertUnique(includesFiles, "CustomRuntimeObject.js");
|
InsertUnique(includesFiles, "CustomRuntimeObject.js");
|
||||||
InsertUnique(includesFiles, "CustomRuntimeObject2D.js");
|
InsertUnique(includesFiles, "CustomRuntimeObject2D.js");
|
||||||
|
InsertUnique(includesFiles, "indexeddb.js");
|
||||||
|
|
||||||
// Common includes for events only.
|
// Common includes for events only.
|
||||||
InsertUnique(includesFiles, "events-tools/commontools.js");
|
InsertUnique(includesFiles, "events-tools/commontools.js");
|
||||||
|
@@ -16,6 +16,11 @@ namespace gdjs {
|
|||||||
childrenContent: { [objectName: string]: ObjectConfiguration & any };
|
childrenContent: { [objectName: string]: ObjectConfiguration & any };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CustomObjectNetworkSyncDataType = ObjectNetworkSyncData & {
|
||||||
|
ifx: boolean;
|
||||||
|
ify: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object that contains other object.
|
* An object that contains other object.
|
||||||
*
|
*
|
||||||
@@ -168,6 +173,29 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): CustomObjectNetworkSyncDataType {
|
||||||
|
return {
|
||||||
|
...super.getNetworkSyncData(syncOptions),
|
||||||
|
ifx: this.isFlippedX(),
|
||||||
|
ify: this.isFlippedY(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFromNetworkSyncData(
|
||||||
|
networkSyncData: CustomObjectNetworkSyncDataType,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||||
|
if (networkSyncData.ifx !== undefined) {
|
||||||
|
this.flipX(networkSyncData.ifx);
|
||||||
|
}
|
||||||
|
if (networkSyncData.ify !== undefined) {
|
||||||
|
this.flipY(networkSyncData.ify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override extraInitializationFromInitialInstance(
|
override extraInitializationFromInitialInstance(
|
||||||
initialInstanceData: InstanceData
|
initialInstanceData: InstanceData
|
||||||
) {
|
) {
|
||||||
|
@@ -623,7 +623,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new object from its name. The object is also added to the instances
|
* Create a new object from its name. The object is also added to the instances
|
||||||
* living in the container (No need to call {@link gdjs.RuntimeScene.addObject})
|
* living in the container (No need to call {@link addObject})
|
||||||
* @param objectName The name of the object to be created
|
* @param objectName The name of the object to be created
|
||||||
* @return The created object
|
* @return The created object
|
||||||
*/
|
*/
|
||||||
|
@@ -84,11 +84,30 @@ namespace gdjs {
|
|||||||
*/
|
*/
|
||||||
private _onPlay: Array<HowlCallback> = [];
|
private _onPlay: Array<HowlCallback> = [];
|
||||||
|
|
||||||
constructor(howl: Howl, volume: float, loop: boolean, rate: float) {
|
/**
|
||||||
|
* The filepath to the resource
|
||||||
|
*/
|
||||||
|
private _audioResourceName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The channel index on which the sound is played.
|
||||||
|
*/
|
||||||
|
private _channel: float | undefined;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
howl: Howl,
|
||||||
|
volume: float,
|
||||||
|
loop: boolean,
|
||||||
|
rate: float,
|
||||||
|
audioResourceName: string,
|
||||||
|
channel: float | undefined
|
||||||
|
) {
|
||||||
this._howl = howl;
|
this._howl = howl;
|
||||||
this._initialVolume = clampVolume(volume);
|
this._initialVolume = clampVolume(volume);
|
||||||
this._loop = loop;
|
this._loop = loop;
|
||||||
this._rate = rate;
|
this._rate = rate;
|
||||||
|
this._audioResourceName = audioResourceName;
|
||||||
|
this._channel = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,10 +376,21 @@ namespace gdjs {
|
|||||||
if (this._id !== null) this._howl.off(event, handler, this._id);
|
if (this._id !== null) this._howl.off(event, handler, this._id);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNetworkSyncData(): SoundSyncData {
|
||||||
|
return {
|
||||||
|
resourceName: this._audioResourceName,
|
||||||
|
loop: this._loop,
|
||||||
|
volume: this.getVolume(),
|
||||||
|
rate: this._rate,
|
||||||
|
position: this.getSeek(),
|
||||||
|
channel: this._channel || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HowlerSoundManager is used to manage the sounds and musics of a RuntimeScene.
|
* HowlerSoundManager is used to manage the sounds and musics of a RuntimeGame.
|
||||||
*
|
*
|
||||||
* It is basically a container to associate channels to sounds and keep a list
|
* It is basically a container to associate channels to sounds and keep a list
|
||||||
* of all sounds being played.
|
* of all sounds being played.
|
||||||
@@ -574,7 +604,8 @@ namespace gdjs {
|
|||||||
isMusic: boolean,
|
isMusic: boolean,
|
||||||
volume: float,
|
volume: float,
|
||||||
loop: boolean,
|
loop: boolean,
|
||||||
rate: float
|
rate: float,
|
||||||
|
channel?: float
|
||||||
): HowlerSound {
|
): HowlerSound {
|
||||||
const cacheContainer = isMusic ? this._loadedMusics : this._loadedSounds;
|
const cacheContainer = isMusic ? this._loadedMusics : this._loadedSounds;
|
||||||
const resource = this._getAudioResource(soundName);
|
const resource = this._getAudioResource(soundName);
|
||||||
@@ -601,8 +632,7 @@ namespace gdjs {
|
|||||||
);
|
);
|
||||||
cacheContainer.set(resource, howl);
|
cacheContainer.set(resource, howl);
|
||||||
}
|
}
|
||||||
|
return new gdjs.HowlerSound(howl, volume, loop, rate, soundName, channel);
|
||||||
return new gdjs.HowlerSound(howl, volume, loop, rate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -700,7 +730,13 @@ namespace gdjs {
|
|||||||
this._loadedSounds.clear();
|
this._loadedSounds.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
playSound(soundName: string, loop: boolean, volume: float, pitch: float) {
|
playSound(
|
||||||
|
soundName: string,
|
||||||
|
loop: boolean,
|
||||||
|
volume: float,
|
||||||
|
pitch: float,
|
||||||
|
position?: float
|
||||||
|
) {
|
||||||
const sound = this.createHowlerSound(
|
const sound = this.createHowlerSound(
|
||||||
soundName,
|
soundName,
|
||||||
/* isMusic= */ false,
|
/* isMusic= */ false,
|
||||||
@@ -716,6 +752,9 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
sound.play();
|
sound.play();
|
||||||
|
if (position) {
|
||||||
|
sound.setSeek(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playSoundOnChannel(
|
playSoundOnChannel(
|
||||||
@@ -723,7 +762,8 @@ namespace gdjs {
|
|||||||
channel: integer,
|
channel: integer,
|
||||||
loop: boolean,
|
loop: boolean,
|
||||||
volume: float,
|
volume: float,
|
||||||
pitch: float
|
pitch: float,
|
||||||
|
position?: float
|
||||||
) {
|
) {
|
||||||
if (this._sounds[channel]) this._sounds[channel].stop();
|
if (this._sounds[channel]) this._sounds[channel].stop();
|
||||||
|
|
||||||
@@ -732,7 +772,8 @@ namespace gdjs {
|
|||||||
/* isMusic= */ false,
|
/* isMusic= */ false,
|
||||||
volume / 100,
|
volume / 100,
|
||||||
loop,
|
loop,
|
||||||
pitch
|
pitch,
|
||||||
|
channel
|
||||||
);
|
);
|
||||||
const spatialPosition = this._cachedSpatialPosition[channel];
|
const spatialPosition = this._cachedSpatialPosition[channel];
|
||||||
if (spatialPosition) {
|
if (spatialPosition) {
|
||||||
@@ -748,13 +789,22 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
sound.play();
|
sound.play();
|
||||||
|
if (position) {
|
||||||
|
sound.setSeek(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSoundOnChannel(channel: integer): HowlerSound | null {
|
getSoundOnChannel(channel: integer): HowlerSound | null {
|
||||||
return this._sounds[channel] || null;
|
return this._sounds[channel] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
playMusic(soundName: string, loop: boolean, volume: float, pitch: float) {
|
playMusic(
|
||||||
|
soundName: string,
|
||||||
|
loop: boolean,
|
||||||
|
volume: float,
|
||||||
|
pitch: float,
|
||||||
|
position?: float
|
||||||
|
) {
|
||||||
const music = this.createHowlerSound(
|
const music = this.createHowlerSound(
|
||||||
soundName,
|
soundName,
|
||||||
/* isMusic= */ true,
|
/* isMusic= */ true,
|
||||||
@@ -770,6 +820,9 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
music.play();
|
music.play();
|
||||||
|
if (position) {
|
||||||
|
music.setSeek(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playMusicOnChannel(
|
playMusicOnChannel(
|
||||||
@@ -777,7 +830,8 @@ namespace gdjs {
|
|||||||
channel: integer,
|
channel: integer,
|
||||||
loop: boolean,
|
loop: boolean,
|
||||||
volume: float,
|
volume: float,
|
||||||
pitch: float
|
pitch: float,
|
||||||
|
position?: float
|
||||||
) {
|
) {
|
||||||
if (this._musics[channel]) this._musics[channel].stop();
|
if (this._musics[channel]) this._musics[channel].stop();
|
||||||
|
|
||||||
@@ -786,7 +840,8 @@ namespace gdjs {
|
|||||||
/* isMusic= */ true,
|
/* isMusic= */ true,
|
||||||
volume / 100,
|
volume / 100,
|
||||||
loop,
|
loop,
|
||||||
pitch
|
pitch,
|
||||||
|
channel
|
||||||
);
|
);
|
||||||
// Musics are played with the html5 backend, that is not compatible with spatialization.
|
// Musics are played with the html5 backend, that is not compatible with spatialization.
|
||||||
this._musics[channel] = music;
|
this._musics[channel] = music;
|
||||||
@@ -797,6 +852,9 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
music.play();
|
music.play();
|
||||||
|
if (position) {
|
||||||
|
music.setSeek(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getMusicOnChannel(channel: integer): HowlerSound | null {
|
getMusicOnChannel(channel: integer): HowlerSound | null {
|
||||||
@@ -924,6 +982,88 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNetworkSyncData(): SoundManagerSyncData {
|
||||||
|
const freeMusicsDatas: SoundSyncData[] = this._freeMusics.map(
|
||||||
|
(freeMusic) => freeMusic.getNetworkSyncData()
|
||||||
|
);
|
||||||
|
const freeSoundsDatas: SoundSyncData[] = this._freeSounds.map(
|
||||||
|
(freeSound) => freeSound.getNetworkSyncData()
|
||||||
|
);
|
||||||
|
const musicsDatas: SoundSyncData[] = Object.values(this._musics).map(
|
||||||
|
(music) => music.getNetworkSyncData()
|
||||||
|
);
|
||||||
|
const soundsDatas: SoundSyncData[] = Object.values(this._sounds).map(
|
||||||
|
(sound) => sound.getNetworkSyncData()
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
globalVolume: this._globalVolume,
|
||||||
|
cachedSpatialPosition: this._cachedSpatialPosition,
|
||||||
|
freeMusics: freeMusicsDatas,
|
||||||
|
freeSounds: freeSoundsDatas,
|
||||||
|
musics: musicsDatas,
|
||||||
|
sounds: soundsDatas,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFromNetworkSyncData(syncData: SoundManagerSyncData): void {
|
||||||
|
this.clearAll();
|
||||||
|
if (syncData.globalVolume !== undefined) {
|
||||||
|
this._globalVolume = syncData.globalVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncData.cachedSpatialPosition !== undefined) {
|
||||||
|
this._cachedSpatialPosition = syncData.cachedSpatialPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < syncData.freeSounds.length; i++) {
|
||||||
|
const freeSoundsSyncData: SoundSyncData = syncData.freeSounds[i];
|
||||||
|
|
||||||
|
this.playSound(
|
||||||
|
freeSoundsSyncData.resourceName,
|
||||||
|
freeSoundsSyncData.loop,
|
||||||
|
freeSoundsSyncData.volume * 100,
|
||||||
|
freeSoundsSyncData.rate,
|
||||||
|
freeSoundsSyncData.position
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < syncData.freeMusics.length; i++) {
|
||||||
|
const freeMusicsSyncData: SoundSyncData = syncData.freeMusics[i];
|
||||||
|
this.playMusic(
|
||||||
|
freeMusicsSyncData.resourceName,
|
||||||
|
freeMusicsSyncData.loop,
|
||||||
|
freeMusicsSyncData.volume * 100,
|
||||||
|
freeMusicsSyncData.rate,
|
||||||
|
freeMusicsSyncData.position
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < syncData.sounds.length; i++) {
|
||||||
|
const soundsSyncData: SoundSyncData = syncData.sounds[i];
|
||||||
|
this.playSoundOnChannel(
|
||||||
|
soundsSyncData.resourceName,
|
||||||
|
soundsSyncData.channel || 0,
|
||||||
|
soundsSyncData.loop,
|
||||||
|
soundsSyncData.volume * 100,
|
||||||
|
soundsSyncData.rate,
|
||||||
|
soundsSyncData.position
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < syncData.musics.length; i++) {
|
||||||
|
const musicsSyncData: SoundSyncData = syncData.musics[i];
|
||||||
|
this.playMusicOnChannel(
|
||||||
|
musicsSyncData.resourceName,
|
||||||
|
musicsSyncData.channel || 0,
|
||||||
|
musicsSyncData.loop,
|
||||||
|
musicsSyncData.volume * 100,
|
||||||
|
musicsSyncData.rate,
|
||||||
|
musicsSyncData.position
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called when the game is disposed.
|
* To be called when the game is disposed.
|
||||||
* Unloads all audio from memory, clear Howl cache and stop all audio.
|
* Unloads all audio from memory, clear Howl cache and stop all audio.
|
||||||
|
100
GDJS/Runtime/indexeddb.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* GDevelop JS Platform
|
||||||
|
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||||
|
* This project is released under the MIT License.
|
||||||
|
*/
|
||||||
|
namespace gdjs {
|
||||||
|
export const loadFromIndexedDB = async function (
|
||||||
|
dbName: string,
|
||||||
|
objectStoreName: string,
|
||||||
|
key: string
|
||||||
|
): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const request = indexedDB.open(dbName, 1);
|
||||||
|
request.onupgradeneeded = function () {
|
||||||
|
const db = request.result;
|
||||||
|
if (!db.objectStoreNames.contains(objectStoreName)) {
|
||||||
|
db.createObjectStore(objectStoreName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onsuccess = function () {
|
||||||
|
const db = request.result;
|
||||||
|
|
||||||
|
const tx = db.transaction(objectStoreName, 'readonly');
|
||||||
|
const store = tx.objectStore(objectStoreName);
|
||||||
|
const getRequest = store.get(key);
|
||||||
|
|
||||||
|
getRequest.onsuccess = function () {
|
||||||
|
if (getRequest.result !== undefined) {
|
||||||
|
resolve(getRequest.result);
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getRequest.onerror = function () {
|
||||||
|
console.error(
|
||||||
|
'Error loading data from IndexedDB:',
|
||||||
|
getRequest.error
|
||||||
|
);
|
||||||
|
reject(getRequest.error);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onerror = function () {
|
||||||
|
console.error('Error opening IndexedDB:', request.error);
|
||||||
|
reject(request.error);
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Exception thrown while opening IndexedDB:', err);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveToIndexedDB = async function (
|
||||||
|
dbName: string,
|
||||||
|
objectStoreName: string,
|
||||||
|
key: string,
|
||||||
|
data: any
|
||||||
|
): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const request = indexedDB.open(dbName, 1);
|
||||||
|
request.onupgradeneeded = function (event) {
|
||||||
|
const db = request.result;
|
||||||
|
if (!db.objectStoreNames.contains(objectStoreName)) {
|
||||||
|
db.createObjectStore(objectStoreName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.onsuccess = function () {
|
||||||
|
const db = request.result;
|
||||||
|
const tx = db.transaction(objectStoreName, 'readwrite');
|
||||||
|
const store = tx.objectStore(objectStoreName);
|
||||||
|
const putRequest = store.put(data, key);
|
||||||
|
|
||||||
|
putRequest.onsuccess = function () {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
putRequest.onerror = function () {
|
||||||
|
console.error('Error saving data to IndexedDB:', putRequest.error);
|
||||||
|
reject(putRequest.error);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onerror = function () {
|
||||||
|
console.error('Error opening IndexedDB:', request.error);
|
||||||
|
reject(request.error);
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Exception thrown while opening IndexedDB:', err);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
@@ -77,7 +77,9 @@ namespace gdjs {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): BehaviorNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
options: GetNetworkSyncDataOptions
|
||||||
|
): BehaviorNetworkSyncData {
|
||||||
// To be redefined by behaviors that need to synchronize properties
|
// To be redefined by behaviors that need to synchronize properties
|
||||||
// while calling super() to get the common properties.
|
// while calling super() to get the common properties.
|
||||||
return {
|
return {
|
||||||
@@ -90,7 +92,10 @@ namespace gdjs {
|
|||||||
* Update the behavior properties using the provided data.
|
* Update the behavior properties using the provided data.
|
||||||
* @param networkSyncData The new properties of the behavior.
|
* @param networkSyncData The new properties of the behavior.
|
||||||
*/
|
*/
|
||||||
updateFromNetworkSyncData(networkSyncData: BehaviorNetworkSyncData): void {
|
updateFromNetworkSyncData(
|
||||||
|
networkSyncData: BehaviorNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
): void {
|
||||||
// Must be redefined by behaviors that need to synchronize properties
|
// Must be redefined by behaviors that need to synchronize properties
|
||||||
// while calling super() to get the common properties.
|
// while calling super() to get the common properties.
|
||||||
if (networkSyncData.act !== this._activated) {
|
if (networkSyncData.act !== this._activated) {
|
||||||
|
@@ -1353,8 +1353,8 @@ namespace gdjs {
|
|||||||
const syncData: GameNetworkSyncData = {
|
const syncData: GameNetworkSyncData = {
|
||||||
var: this._variables.getNetworkSyncData(syncOptions),
|
var: this._variables.getNetworkSyncData(syncOptions),
|
||||||
ss: this._sceneStack.getNetworkSyncData(syncOptions) || undefined,
|
ss: this._sceneStack.getNetworkSyncData(syncOptions) || undefined,
|
||||||
|
sm: this.getSoundManager().getNetworkSyncData() || undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const extensionsVariablesSyncData = {};
|
const extensionsVariablesSyncData = {};
|
||||||
this._variablesByExtensionName.forEach((variables, extensionName) => {
|
this._variablesByExtensionName.forEach((variables, extensionName) => {
|
||||||
const extensionVariablesSyncData =
|
const extensionVariablesSyncData =
|
||||||
@@ -1368,6 +1368,7 @@ namespace gdjs {
|
|||||||
syncData.extVar = extensionsVariablesSyncData;
|
syncData.extVar = extensionsVariablesSyncData;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
!syncOptions.forceSyncEverything &&
|
||||||
(!syncData.var || syncData.var.length === 0) &&
|
(!syncData.var || syncData.var.length === 0) &&
|
||||||
!syncData.ss &&
|
!syncData.ss &&
|
||||||
(!syncData.extVar || Object.keys(syncData.extVar).length === 0)
|
(!syncData.extVar || Object.keys(syncData.extVar).length === 0)
|
||||||
@@ -1379,14 +1380,20 @@ namespace gdjs {
|
|||||||
return syncData;
|
return syncData;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(syncData: GameNetworkSyncData) {
|
updateFromNetworkSyncData(
|
||||||
|
syncData: GameNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
this._throwIfDisposed();
|
this._throwIfDisposed();
|
||||||
if (syncData.var) {
|
if (syncData.var) {
|
||||||
this._variables.updateFromNetworkSyncData(syncData.var);
|
this._variables.updateFromNetworkSyncData(syncData.var, options);
|
||||||
}
|
}
|
||||||
if (syncData.ss) {
|
if (syncData.ss) {
|
||||||
this._sceneStack.updateFromNetworkSyncData(syncData.ss);
|
this._sceneStack.updateFromNetworkSyncData(syncData.ss);
|
||||||
}
|
}
|
||||||
|
if (options.syncSounds && syncData.sm) {
|
||||||
|
this.getSoundManager().updateFromNetworkSyncData(syncData.sm);
|
||||||
|
}
|
||||||
if (syncData.extVar) {
|
if (syncData.extVar) {
|
||||||
for (const extensionName in syncData.extVar) {
|
for (const extensionName in syncData.extVar) {
|
||||||
if (!syncData.extVar.hasOwnProperty(extensionName)) {
|
if (!syncData.extVar.hasOwnProperty(extensionName)) {
|
||||||
@@ -1397,7 +1404,8 @@ namespace gdjs {
|
|||||||
this.getVariablesForExtension(extensionName);
|
this.getVariablesForExtension(extensionName);
|
||||||
if (extensionVariables) {
|
if (extensionVariables) {
|
||||||
extensionVariables.updateFromNetworkSyncData(
|
extensionVariables.updateFromNetworkSyncData(
|
||||||
extensionVariablesData
|
extensionVariablesData,
|
||||||
|
options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -454,14 +454,19 @@ namespace gdjs {
|
|||||||
* This can be redefined by objects to send more information.
|
* This can be redefined by objects to send more information.
|
||||||
* @returns The full network sync data.
|
* @returns The full network sync data.
|
||||||
*/
|
*/
|
||||||
getNetworkSyncData(): ObjectNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): ObjectNetworkSyncData {
|
||||||
const behaviorNetworkSyncData = {};
|
const behaviorNetworkSyncData = {};
|
||||||
this._behaviors.forEach((behavior) => {
|
this._behaviors.forEach((behavior) => {
|
||||||
if (!behavior.isSyncedOverNetwork()) {
|
if (
|
||||||
|
!behavior.isSyncedOverNetwork() &&
|
||||||
|
(!syncOptions || !syncOptions.forceSyncEverything)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const networkSyncData = behavior.getNetworkSyncData();
|
const networkSyncData = behavior.getNetworkSyncData(syncOptions);
|
||||||
if (networkSyncData) {
|
if (networkSyncData) {
|
||||||
behaviorNetworkSyncData[behavior.getName()] = networkSyncData;
|
behaviorNetworkSyncData[behavior.getName()] = networkSyncData;
|
||||||
}
|
}
|
||||||
@@ -486,6 +491,8 @@ namespace gdjs {
|
|||||||
return {
|
return {
|
||||||
x: this.x,
|
x: this.x,
|
||||||
y: this.y,
|
y: this.y,
|
||||||
|
w: this.getWidth(),
|
||||||
|
h: this.getHeight(),
|
||||||
zo: this.zOrder,
|
zo: this.zOrder,
|
||||||
a: this.angle,
|
a: this.angle,
|
||||||
hid: this.hidden,
|
hid: this.hidden,
|
||||||
@@ -493,6 +500,7 @@ namespace gdjs {
|
|||||||
if: this._instantForces.map((force) => force.getNetworkSyncData()),
|
if: this._instantForces.map((force) => force.getNetworkSyncData()),
|
||||||
pfx: this._permanentForceX,
|
pfx: this._permanentForceX,
|
||||||
pfy: this._permanentForceY,
|
pfy: this._permanentForceY,
|
||||||
|
n: syncOptions.forceSyncEverything ? this.name : undefined,
|
||||||
beh: behaviorNetworkSyncData,
|
beh: behaviorNetworkSyncData,
|
||||||
var: variablesNetworkSyncData,
|
var: variablesNetworkSyncData,
|
||||||
eff: effectsNetworkSyncData,
|
eff: effectsNetworkSyncData,
|
||||||
@@ -507,13 +515,22 @@ namespace gdjs {
|
|||||||
* @param networkSyncData The new data for the object.
|
* @param networkSyncData The new data for the object.
|
||||||
* @returns true if the object was updated, false if it could not (i.e: network sync is not supported).
|
* @returns true if the object was updated, false if it could not (i.e: network sync is not supported).
|
||||||
*/
|
*/
|
||||||
updateFromNetworkSyncData(networkSyncData: ObjectNetworkSyncData) {
|
updateFromNetworkSyncData(
|
||||||
|
networkSyncData: ObjectNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
if (networkSyncData.x !== undefined) {
|
if (networkSyncData.x !== undefined) {
|
||||||
this.setX(networkSyncData.x);
|
this.setX(networkSyncData.x);
|
||||||
}
|
}
|
||||||
if (networkSyncData.y !== undefined) {
|
if (networkSyncData.y !== undefined) {
|
||||||
this.setY(networkSyncData.y);
|
this.setY(networkSyncData.y);
|
||||||
}
|
}
|
||||||
|
if (networkSyncData.w !== undefined) {
|
||||||
|
this.setWidth(networkSyncData.w);
|
||||||
|
}
|
||||||
|
if (networkSyncData.h !== undefined) {
|
||||||
|
this.setHeight(networkSyncData.h);
|
||||||
|
}
|
||||||
if (networkSyncData.zo !== undefined) {
|
if (networkSyncData.zo !== undefined) {
|
||||||
this.setZOrder(networkSyncData.zo);
|
this.setZOrder(networkSyncData.zo);
|
||||||
}
|
}
|
||||||
@@ -561,13 +578,13 @@ namespace gdjs {
|
|||||||
const behaviorNetworkSyncData = networkSyncData.beh[behaviorName];
|
const behaviorNetworkSyncData = networkSyncData.beh[behaviorName];
|
||||||
const behavior = this.getBehavior(behaviorName);
|
const behavior = this.getBehavior(behaviorName);
|
||||||
if (behavior) {
|
if (behavior) {
|
||||||
behavior.updateFromNetworkSyncData(behaviorNetworkSyncData);
|
behavior.updateFromNetworkSyncData(behaviorNetworkSyncData, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If variables are synchronized, update them.
|
// If variables are synchronized, update them.
|
||||||
if (networkSyncData.var) {
|
if (networkSyncData.var) {
|
||||||
this._variables.updateFromNetworkSyncData(networkSyncData.var);
|
this._variables.updateFromNetworkSyncData(networkSyncData.var, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If effects are synchronized, update them.
|
// If effects are synchronized, update them.
|
||||||
|
@@ -23,6 +23,7 @@ namespace gdjs {
|
|||||||
_timeManager: TimeManager;
|
_timeManager: TimeManager;
|
||||||
_gameStopRequested: boolean = false;
|
_gameStopRequested: boolean = false;
|
||||||
_requestedScene: string = '';
|
_requestedScene: string = '';
|
||||||
|
_loadRequestOptions: LoadRequestOptions | null = null;
|
||||||
private _asyncTasksManager = new gdjs.AsyncTasksManager();
|
private _asyncTasksManager = new gdjs.AsyncTasksManager();
|
||||||
|
|
||||||
/** True if loadFromScene was called and the scene is being played. */
|
/** True if loadFromScene was called and the scene is being played. */
|
||||||
@@ -124,7 +125,13 @@ namespace gdjs {
|
|||||||
* @param sceneAndExtensionsData An object containing the scene data.
|
* @param sceneAndExtensionsData An object containing the scene data.
|
||||||
* @see gdjs.RuntimeGame#getSceneAndExtensionsData
|
* @see gdjs.RuntimeGame#getSceneAndExtensionsData
|
||||||
*/
|
*/
|
||||||
loadFromScene(sceneAndExtensionsData: SceneAndExtensionsData | null) {
|
loadFromScene(
|
||||||
|
sceneAndExtensionsData: SceneAndExtensionsData | null,
|
||||||
|
options?: {
|
||||||
|
preventInitialInstancesCreation: boolean;
|
||||||
|
preventSoundManagerClearing: boolean;
|
||||||
|
}
|
||||||
|
) {
|
||||||
if (!sceneAndExtensionsData) {
|
if (!sceneAndExtensionsData) {
|
||||||
logger.error('loadFromScene was called without a scene');
|
logger.error('loadFromScene was called without a scene');
|
||||||
return;
|
return;
|
||||||
@@ -181,15 +188,16 @@ namespace gdjs {
|
|||||||
this.registerObject(sceneData.objects[i]);
|
this.registerObject(sceneData.objects[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create initial instances of objects
|
// Create initial instances of objects
|
||||||
this.createObjectsFrom(
|
if (!options || !options.preventInitialInstancesCreation)
|
||||||
sceneData.instances,
|
this.createObjectsFrom(
|
||||||
0,
|
sceneData.instances,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
/*trackByPersistentUuid=*/
|
0,
|
||||||
true
|
/*trackByPersistentUuid=*/
|
||||||
);
|
true
|
||||||
|
);
|
||||||
|
|
||||||
// Set up the default z order (for objects created from events)
|
// Set up the default z order (for objects created from events)
|
||||||
this._setLayerDefaultZOrders();
|
this._setLayerDefaultZOrders();
|
||||||
@@ -207,7 +215,11 @@ namespace gdjs {
|
|||||||
for (let i = 0; i < gdjs.callbacksRuntimeSceneLoaded.length; ++i) {
|
for (let i = 0; i < gdjs.callbacksRuntimeSceneLoaded.length; ++i) {
|
||||||
gdjs.callbacksRuntimeSceneLoaded[i](this);
|
gdjs.callbacksRuntimeSceneLoaded[i](this);
|
||||||
}
|
}
|
||||||
if (sceneData.stopSoundsOnStartup && this._runtimeGame) {
|
if (
|
||||||
|
sceneData.stopSoundsOnStartup &&
|
||||||
|
this._runtimeGame &&
|
||||||
|
(!options || !options.preventSoundManagerClearing)
|
||||||
|
) {
|
||||||
this._runtimeGame.getSoundManager().clearAll();
|
this._runtimeGame.getSoundManager().clearAll();
|
||||||
}
|
}
|
||||||
this._isLoaded = true;
|
this._isLoaded = true;
|
||||||
@@ -649,7 +661,6 @@ namespace gdjs {
|
|||||||
getViewportOriginY(): float {
|
getViewportOriginY(): float {
|
||||||
return this._cachedGameResolutionHeight / 2;
|
return this._cachedGameResolutionHeight / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
convertCoords(x: float, y: float, result: FloatPoint): FloatPoint {
|
convertCoords(x: float, y: float, result: FloatPoint): FloatPoint {
|
||||||
// The result parameter used to be optional.
|
// The result parameter used to be optional.
|
||||||
const point = result || [0, 0];
|
const point = result || [0, 0];
|
||||||
@@ -748,6 +759,10 @@ namespace gdjs {
|
|||||||
if (sceneName) this._requestedScene = sceneName;
|
if (sceneName) this._requestedScene = sceneName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestLoadSnapshot(loadRequestOptions: LoadRequestOptions | null): void {
|
||||||
|
this._loadRequestOptions = loadRequestOptions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the profiler associated with the scene, or null if none.
|
* Get the profiler associated with the scene, or null if none.
|
||||||
*/
|
*/
|
||||||
@@ -836,12 +851,19 @@ namespace gdjs {
|
|||||||
var: variablesNetworkSyncData,
|
var: variablesNetworkSyncData,
|
||||||
extVar: extensionsVariablesSyncData,
|
extVar: extensionsVariablesSyncData,
|
||||||
id: this.getOrCreateNetworkId(),
|
id: this.getOrCreateNetworkId(),
|
||||||
|
timeManager: this._timeManager.getNetworkSyncData(),
|
||||||
|
tweenManager: gdjs.evtTools.tween
|
||||||
|
.getTweensMap(this)
|
||||||
|
.getNetworkSyncData(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(syncData: LayoutNetworkSyncData) {
|
updateFromNetworkSyncData(
|
||||||
|
syncData: LayoutNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
if (syncData.var) {
|
if (syncData.var) {
|
||||||
this._variables.updateFromNetworkSyncData(syncData.var);
|
this._variables.updateFromNetworkSyncData(syncData.var, options);
|
||||||
}
|
}
|
||||||
if (syncData.extVar) {
|
if (syncData.extVar) {
|
||||||
for (const extensionName in syncData.extVar) {
|
for (const extensionName in syncData.extVar) {
|
||||||
@@ -853,11 +875,22 @@ namespace gdjs {
|
|||||||
this._variablesByExtensionName.get(extensionName);
|
this._variablesByExtensionName.get(extensionName);
|
||||||
if (extensionVariables) {
|
if (extensionVariables) {
|
||||||
extensionVariables.updateFromNetworkSyncData(
|
extensionVariables.updateFromNetworkSyncData(
|
||||||
extensionVariablesData
|
extensionVariablesData,
|
||||||
|
options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (syncData.timeManager && options.syncTimers) {
|
||||||
|
this._timeManager.updateFromNetworkSyncData(syncData.timeManager);
|
||||||
|
}
|
||||||
|
if (syncData.tweenManager && options.syncTweens) {
|
||||||
|
gdjs.evtTools.tween.getTweensMap(this).updateFromNetworkSyncData(
|
||||||
|
syncData.tweenManager,
|
||||||
|
// TODO: Use correct time source identifier.
|
||||||
|
(timeSourceIdentifier) => this
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrCreateNetworkId(): string {
|
getOrCreateNetworkId(): string {
|
||||||
@@ -867,6 +900,10 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
return this.networkId;
|
return this.networkId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLoadRequestOptions(): LoadRequestOptions | null {
|
||||||
|
return this._loadRequestOptions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//The flags to describe the change request by a scene:
|
//The flags to describe the change request by a scene:
|
||||||
|
@@ -34,6 +34,41 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_loadGameFromSave(saveState: GameSaveState): void {
|
||||||
|
const options: UpdateFromNetworkSyncDataOptions = {
|
||||||
|
clearMemory: true,
|
||||||
|
keepControl: true,
|
||||||
|
syncSounds: true,
|
||||||
|
syncTimers: true,
|
||||||
|
syncTweens: true,
|
||||||
|
ignoreVariableOwnership: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
this._runtimeGame.updateFromNetworkSyncData(
|
||||||
|
saveState.gameNetworkSyncData,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
this.applyUpdateFromNetworkSyncDataIfAny(options);
|
||||||
|
|
||||||
|
const sceneStack = this._stack;
|
||||||
|
sceneStack.forEach((scene, index) => {
|
||||||
|
const layoutSyncData = saveState.layoutNetworkSyncDatas[index];
|
||||||
|
if (!layoutSyncData) return;
|
||||||
|
|
||||||
|
scene.updateFromNetworkSyncData(layoutSyncData.sceneData, options);
|
||||||
|
|
||||||
|
const objectDatas = layoutSyncData.objectDatas;
|
||||||
|
for (const id in objectDatas) {
|
||||||
|
const objectNetworkSyncData = objectDatas[id];
|
||||||
|
const object = scene.createObject(objectNetworkSyncData.n || '');
|
||||||
|
if (object) {
|
||||||
|
object.updateFromNetworkSyncData(objectNetworkSyncData, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
step(elapsedTime: float): boolean {
|
step(elapsedTime: float): boolean {
|
||||||
this._throwIfDisposed();
|
this._throwIfDisposed();
|
||||||
if (this._isNextLayoutLoading || this._stack.length === 0) {
|
if (this._isNextLayoutLoading || this._stack.length === 0) {
|
||||||
@@ -67,11 +102,49 @@ namespace gdjs {
|
|||||||
this.replace(currentScene.getRequestedScene());
|
this.replace(currentScene.getRequestedScene());
|
||||||
} else if (request === gdjs.SceneChangeRequest.CLEAR_SCENES) {
|
} else if (request === gdjs.SceneChangeRequest.CLEAR_SCENES) {
|
||||||
this.replace(currentScene.getRequestedScene(), true);
|
this.replace(currentScene.getRequestedScene(), true);
|
||||||
} else {
|
|
||||||
logger.error('Unrecognized change in scene stack: ' + request);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadRequestOptions = currentScene.getLoadRequestOptions();
|
||||||
|
if (!loadRequestOptions) return true;
|
||||||
|
|
||||||
|
currentScene.requestLoadSnapshot(null);
|
||||||
|
|
||||||
|
if (loadRequestOptions.loadVariable) {
|
||||||
|
if (
|
||||||
|
loadRequestOptions.loadVariable !==
|
||||||
|
gdjs.VariablesContainer.badVariable
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
'Requested loading save from wrongly defined variable.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const saveState =
|
||||||
|
loadRequestOptions.loadVariable.toJSObject() as GameSaveState;
|
||||||
|
try {
|
||||||
|
this._loadGameFromSave(saveState);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error loading from variable:', error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const storageKey =
|
||||||
|
loadRequestOptions.loadStorageName || gdjs.saveState.INDEXED_DB_KEY;
|
||||||
|
|
||||||
|
gdjs
|
||||||
|
.loadFromIndexedDB(
|
||||||
|
gdjs.saveState.INDEXED_DB_NAME,
|
||||||
|
gdjs.saveState.INDEXED_DB_OBJECT_STORE,
|
||||||
|
storageKey
|
||||||
|
)
|
||||||
|
.then((jsonData) => {
|
||||||
|
const saveState = jsonData as GameSaveState;
|
||||||
|
this._loadGameFromSave(saveState);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error('Error loading from IndexedDB:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +192,8 @@ namespace gdjs {
|
|||||||
*/
|
*/
|
||||||
push(
|
push(
|
||||||
newSceneName: string,
|
newSceneName: string,
|
||||||
externalLayoutName?: string
|
externalLayoutName?: string,
|
||||||
|
options?: UpdateFromNetworkSyncDataOptions
|
||||||
): gdjs.RuntimeScene | null {
|
): gdjs.RuntimeScene | null {
|
||||||
this._throwIfDisposed();
|
this._throwIfDisposed();
|
||||||
|
|
||||||
@@ -132,12 +206,12 @@ namespace gdjs {
|
|||||||
// Avoid a risk of displaying an intermediate loading screen
|
// Avoid a risk of displaying an intermediate loading screen
|
||||||
// during 1 frame.
|
// during 1 frame.
|
||||||
if (this._runtimeGame.areSceneAssetsReady(newSceneName)) {
|
if (this._runtimeGame.areSceneAssetsReady(newSceneName)) {
|
||||||
return this._loadNewScene(newSceneName, externalLayoutName);
|
return this._loadNewScene(newSceneName, externalLayoutName, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._isNextLayoutLoading = true;
|
this._isNextLayoutLoading = true;
|
||||||
this._runtimeGame.loadSceneAssets(newSceneName).then(() => {
|
this._runtimeGame.loadSceneAssets(newSceneName).then(() => {
|
||||||
this._loadNewScene(newSceneName);
|
this._loadNewScene(newSceneName, undefined, options);
|
||||||
this._isNextLayoutLoading = false;
|
this._isNextLayoutLoading = false;
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
@@ -145,14 +219,21 @@ namespace gdjs {
|
|||||||
|
|
||||||
private _loadNewScene(
|
private _loadNewScene(
|
||||||
newSceneName: string,
|
newSceneName: string,
|
||||||
externalLayoutName?: string
|
externalLayoutName?: string,
|
||||||
|
options?: UpdateFromNetworkSyncDataOptions
|
||||||
): gdjs.RuntimeScene {
|
): gdjs.RuntimeScene {
|
||||||
this._throwIfDisposed();
|
this._throwIfDisposed();
|
||||||
|
const preventInitialInstancesCreation = !!options;
|
||||||
|
const preventSoundManagerClearing = !!options;
|
||||||
|
|
||||||
// Load the new one
|
// Load the new one
|
||||||
const newScene = new gdjs.RuntimeScene(this._runtimeGame);
|
const newScene = new gdjs.RuntimeScene(this._runtimeGame);
|
||||||
newScene.loadFromScene(
|
newScene.loadFromScene(
|
||||||
this._runtimeGame.getSceneAndExtensionsData(newSceneName)
|
this._runtimeGame.getSceneAndExtensionsData(newSceneName),
|
||||||
|
{
|
||||||
|
preventInitialInstancesCreation,
|
||||||
|
preventSoundManagerClearing,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
this._wasFirstSceneLoaded = true;
|
this._wasFirstSceneLoaded = true;
|
||||||
|
|
||||||
@@ -160,7 +241,7 @@ namespace gdjs {
|
|||||||
if (externalLayoutName) {
|
if (externalLayoutName) {
|
||||||
const externalLayoutData =
|
const externalLayoutData =
|
||||||
this._runtimeGame.getExternalLayoutData(externalLayoutName);
|
this._runtimeGame.getExternalLayoutData(externalLayoutName);
|
||||||
if (externalLayoutData) {
|
if (externalLayoutData && !preventInitialInstancesCreation) {
|
||||||
newScene.createObjectsFrom(
|
newScene.createObjectsFrom(
|
||||||
externalLayoutData.instances,
|
externalLayoutData.instances,
|
||||||
0,
|
0,
|
||||||
@@ -179,8 +260,13 @@ namespace gdjs {
|
|||||||
* Start the specified scene, replacing the one currently being played.
|
* Start the specified scene, replacing the one currently being played.
|
||||||
* If `clear` is set to true, all running scenes are also removed from the stack of scenes.
|
* If `clear` is set to true, all running scenes are also removed from the stack of scenes.
|
||||||
*/
|
*/
|
||||||
replace(newSceneName: string, clear?: boolean): gdjs.RuntimeScene | null {
|
replace(
|
||||||
|
newSceneName: string,
|
||||||
|
clear?: boolean,
|
||||||
|
options?: UpdateFromNetworkSyncDataOptions
|
||||||
|
): gdjs.RuntimeScene | null {
|
||||||
this._throwIfDisposed();
|
this._throwIfDisposed();
|
||||||
|
|
||||||
if (!!clear) {
|
if (!!clear) {
|
||||||
// Unload all the scenes
|
// Unload all the scenes
|
||||||
while (this._stack.length !== 0) {
|
while (this._stack.length !== 0) {
|
||||||
@@ -198,7 +284,7 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.push(newSceneName);
|
return this.push(newSceneName, undefined, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,7 +345,9 @@ namespace gdjs {
|
|||||||
this._sceneStackSyncDataToApply = sceneStackSyncData;
|
this._sceneStackSyncDataToApply = sceneStackSyncData;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyUpdateFromNetworkSyncDataIfAny(): boolean {
|
applyUpdateFromNetworkSyncDataIfAny(
|
||||||
|
options?: UpdateFromNetworkSyncDataOptions
|
||||||
|
): boolean {
|
||||||
this._throwIfDisposed();
|
this._throwIfDisposed();
|
||||||
const sceneStackSyncData = this._sceneStackSyncDataToApply;
|
const sceneStackSyncData = this._sceneStackSyncDataToApply;
|
||||||
let hasMadeChangeToStack = false;
|
let hasMadeChangeToStack = false;
|
||||||
@@ -267,6 +355,23 @@ namespace gdjs {
|
|||||||
|
|
||||||
this._sceneStackSyncDataToApply = null;
|
this._sceneStackSyncDataToApply = null;
|
||||||
|
|
||||||
|
if (options && options.clearMemory) {
|
||||||
|
while (this._stack.length !== 0) {
|
||||||
|
let scene = this._stack.pop();
|
||||||
|
if (scene) {
|
||||||
|
scene.unloadScene();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < sceneStackSyncData.length; ++i) {
|
||||||
|
const sceneSyncData = sceneStackSyncData[i];
|
||||||
|
const newScene = this.push(sceneSyncData.name, undefined, options);
|
||||||
|
if (newScene) {
|
||||||
|
newScene.networkId = sceneSyncData.networkId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasMadeChangeToStack = true;
|
||||||
|
return hasMadeChangeToStack;
|
||||||
|
}
|
||||||
// If this method is called, we are a client.
|
// If this method is called, we are a client.
|
||||||
// We trust the host to be the source of truth for the scene stack.
|
// We trust the host to be the source of truth for the scene stack.
|
||||||
// So we loop through the scenes in the stack given by the host and either:
|
// So we loop through the scenes in the stack given by the host and either:
|
||||||
@@ -276,12 +381,13 @@ namespace gdjs {
|
|||||||
for (let i = 0; i < sceneStackSyncData.length; ++i) {
|
for (let i = 0; i < sceneStackSyncData.length; ++i) {
|
||||||
const sceneSyncData = sceneStackSyncData[i];
|
const sceneSyncData = sceneStackSyncData[i];
|
||||||
const sceneAtThisPositionInOurStack = this._stack[i];
|
const sceneAtThisPositionInOurStack = this._stack[i];
|
||||||
|
|
||||||
if (!sceneAtThisPositionInOurStack) {
|
if (!sceneAtThisPositionInOurStack) {
|
||||||
debugLogger.info(
|
debugLogger.info(
|
||||||
`Scene at position ${i} with name ${sceneSyncData.name} is missing from the stack, adding it.`
|
`Scene at position ${i} with name ${sceneSyncData.name} is missing from the stack, adding it.`
|
||||||
);
|
);
|
||||||
// We have fewer scenes in the stack than the host, let's add the scene.
|
// We have fewer scenes in the stack than the host, let's add the scene.
|
||||||
const newScene = this.push(sceneSyncData.name);
|
const newScene = this.push(sceneSyncData.name, undefined, options);
|
||||||
if (newScene) {
|
if (newScene) {
|
||||||
newScene.networkId = sceneSyncData.networkId;
|
newScene.networkId = sceneSyncData.networkId;
|
||||||
}
|
}
|
||||||
@@ -298,9 +404,11 @@ namespace gdjs {
|
|||||||
);
|
);
|
||||||
// The scene does not correspond to the scene at this position in our stack
|
// The scene does not correspond to the scene at this position in our stack
|
||||||
// Let's unload everything after this position to recreate the stack.
|
// Let's unload everything after this position to recreate the stack.
|
||||||
|
|
||||||
const newScene = this.replace(
|
const newScene = this.replace(
|
||||||
sceneSyncData.name,
|
sceneSyncData.name,
|
||||||
true // Clear the stack
|
true, // Clear the stack
|
||||||
|
options
|
||||||
);
|
);
|
||||||
if (newScene) {
|
if (newScene) {
|
||||||
newScene.networkId = sceneSyncData.networkId;
|
newScene.networkId = sceneSyncData.networkId;
|
||||||
@@ -343,7 +451,8 @@ namespace gdjs {
|
|||||||
// We need to replace it with a new scene
|
// We need to replace it with a new scene
|
||||||
const newScene = this.replace(
|
const newScene = this.replace(
|
||||||
sceneSyncData.name,
|
sceneSyncData.name,
|
||||||
false // Don't clear the stack
|
false, // Don't clear the stack
|
||||||
|
options
|
||||||
);
|
);
|
||||||
if (newScene) {
|
if (newScene) {
|
||||||
newScene.networkId = sceneSyncData.networkId;
|
newScene.networkId = sceneSyncData.networkId;
|
||||||
|
@@ -115,9 +115,11 @@ namespace gdjs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNetworkSyncData(): SpriteNetworkSyncData {
|
getNetworkSyncData(
|
||||||
|
syncOptions: GetNetworkSyncDataOptions
|
||||||
|
): SpriteNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
...super.getNetworkSyncData(),
|
...super.getNetworkSyncData(syncOptions),
|
||||||
anim: this._animator.getNetworkSyncData(),
|
anim: this._animator.getNetworkSyncData(),
|
||||||
ifx: this.isFlippedX(),
|
ifx: this.isFlippedX(),
|
||||||
ify: this.isFlippedY(),
|
ify: this.isFlippedY(),
|
||||||
@@ -128,8 +130,11 @@ namespace gdjs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(newNetworkSyncData: SpriteNetworkSyncData) {
|
updateFromNetworkSyncData(
|
||||||
super.updateFromNetworkSyncData(newNetworkSyncData);
|
newNetworkSyncData: SpriteNetworkSyncData,
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
|
super.updateFromNetworkSyncData(newNetworkSyncData, options);
|
||||||
if (newNetworkSyncData.ifx !== undefined) {
|
if (newNetworkSyncData.ifx !== undefined) {
|
||||||
this.flipX(newNetworkSyncData.ifx);
|
this.flipX(newNetworkSyncData.ifx);
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,15 @@ namespace gdjs {
|
|||||||
* frame, since the beginning of the scene and other time related values.
|
* frame, since the beginning of the scene and other time related values.
|
||||||
* All durations are expressed in milliseconds.
|
* All durations are expressed in milliseconds.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
declare interface TimeManagerSyncData {
|
||||||
|
elapsedTime: float;
|
||||||
|
timeScale: float;
|
||||||
|
timeFromStart: float;
|
||||||
|
firstFrame: boolean;
|
||||||
|
timers: Hashtable<TimerNetworkSyncData>;
|
||||||
|
firstUpdateDone: boolean;
|
||||||
|
}
|
||||||
export class TimeManager {
|
export class TimeManager {
|
||||||
_elapsedTime: float = 0;
|
_elapsedTime: float = 0;
|
||||||
_timeScale: float = 1;
|
_timeScale: float = 1;
|
||||||
@@ -59,6 +68,47 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNetworkSyncData(): TimeManagerSyncData {
|
||||||
|
const timerNetworkSyncDatas = new Hashtable<TimerNetworkSyncData>();
|
||||||
|
Object.entries(this._timers.items).forEach(([key, timer]) => {
|
||||||
|
timerNetworkSyncDatas.put(key, timer.getNetworkSyncData());
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
elapsedTime: this._elapsedTime,
|
||||||
|
timeScale: this._timeScale,
|
||||||
|
timeFromStart: this._timeFromStart,
|
||||||
|
firstFrame: this._firstFrame,
|
||||||
|
timers: timerNetworkSyncDatas,
|
||||||
|
firstUpdateDone: this._firstUpdateDone,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFromNetworkSyncData(syncData: TimeManagerSyncData): void {
|
||||||
|
if (syncData.elapsedTime !== undefined) {
|
||||||
|
this._elapsedTime = syncData.elapsedTime;
|
||||||
|
}
|
||||||
|
if (syncData.timeScale !== undefined) {
|
||||||
|
this._timeScale = syncData.timeScale;
|
||||||
|
}
|
||||||
|
if (syncData.timeFromStart !== undefined) {
|
||||||
|
this._timeFromStart = syncData.timeFromStart;
|
||||||
|
}
|
||||||
|
if (syncData.firstFrame !== undefined) {
|
||||||
|
this._firstFrame = syncData.firstFrame;
|
||||||
|
}
|
||||||
|
if (syncData.timers !== undefined) {
|
||||||
|
Object.entries(syncData.timers.items).forEach(([key, timerData]) => {
|
||||||
|
const newTimer = new gdjs.Timer(timerData.name as string);
|
||||||
|
newTimer.updateFromNetworkSyncData(timerData);
|
||||||
|
this._timers.put(key, newTimer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncData.firstUpdateDone !== undefined) {
|
||||||
|
this._firstUpdateDone = syncData.firstUpdateDone;
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Get the time scale.
|
* Get the time scale.
|
||||||
* @return The time scale (positive, 1 is normal speed).
|
* @return The time scale (positive, 1 is normal speed).
|
||||||
|
@@ -77,6 +77,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
getNetworkSyncData(): TimerNetworkSyncData {
|
getNetworkSyncData(): TimerNetworkSyncData {
|
||||||
return {
|
return {
|
||||||
|
name: this._name,
|
||||||
time: this._time,
|
time: this._time,
|
||||||
paused: this._paused,
|
paused: this._paused,
|
||||||
};
|
};
|
||||||
|
43
GDJS/Runtime/types/project-data.d.ts
vendored
@@ -42,6 +42,16 @@ declare type ObjectData = {
|
|||||||
declare type GetNetworkSyncDataOptions = {
|
declare type GetNetworkSyncDataOptions = {
|
||||||
playerNumber?: number;
|
playerNumber?: number;
|
||||||
isHost?: boolean;
|
isHost?: boolean;
|
||||||
|
forceSyncEverything?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type UpdateFromNetworkSyncDataOptions = {
|
||||||
|
clearMemory?: boolean;
|
||||||
|
syncSounds?: boolean;
|
||||||
|
syncTimers?: boolean;
|
||||||
|
syncTweens?: boolean;
|
||||||
|
keepControl?: boolean;
|
||||||
|
ignoreVariableOwnership?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Object containing basic properties for all objects synchronizing over the network. */
|
/** Object containing basic properties for all objects synchronizing over the network. */
|
||||||
@@ -52,6 +62,10 @@ declare type BasicObjectNetworkSyncData = {
|
|||||||
y: number;
|
y: number;
|
||||||
/** The position of the instance on the Z axis. Defined only for 3D games */
|
/** The position of the instance on the Z axis. Defined only for 3D games */
|
||||||
z?: number;
|
z?: number;
|
||||||
|
/** The width of the instance */
|
||||||
|
w: number;
|
||||||
|
/** The height of the instance */
|
||||||
|
h: number;
|
||||||
/** Z order of the instance */
|
/** Z order of the instance */
|
||||||
zo: number;
|
zo: number;
|
||||||
/** The angle of the instance. */
|
/** The angle of the instance. */
|
||||||
@@ -66,6 +80,8 @@ declare type BasicObjectNetworkSyncData = {
|
|||||||
pfx: number;
|
pfx: number;
|
||||||
/** Permanent force on Y */
|
/** Permanent force on Y */
|
||||||
pfy: number;
|
pfy: number;
|
||||||
|
/* name :*/
|
||||||
|
n?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,6 +114,7 @@ declare type ForceNetworkSyncData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
declare type TimerNetworkSyncData = {
|
declare type TimerNetworkSyncData = {
|
||||||
|
name?: string;
|
||||||
time: float;
|
time: float;
|
||||||
paused: boolean;
|
paused: boolean;
|
||||||
};
|
};
|
||||||
@@ -177,6 +194,8 @@ declare interface LayoutNetworkSyncData {
|
|||||||
extVar?: {
|
extVar?: {
|
||||||
[extensionName: string]: VariableNetworkSyncData[];
|
[extensionName: string]: VariableNetworkSyncData[];
|
||||||
};
|
};
|
||||||
|
timeManager?: TimeManagerSyncData;
|
||||||
|
tweenManager?: gdjs.evtTools.tween.TweenManagerNetworkSyncData;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface SceneStackSceneNetworkSyncData {
|
declare interface SceneStackSceneNetworkSyncData {
|
||||||
@@ -186,12 +205,36 @@ declare interface SceneStackSceneNetworkSyncData {
|
|||||||
|
|
||||||
declare type SceneStackNetworkSyncData = SceneStackSceneNetworkSyncData[];
|
declare type SceneStackNetworkSyncData = SceneStackSceneNetworkSyncData[];
|
||||||
|
|
||||||
|
declare type SoundManagerSyncData = {
|
||||||
|
globalVolume: float;
|
||||||
|
cachedSpatialPosition: Record<integer, [number, number, number]>;
|
||||||
|
freeSounds: SoundSyncData[];
|
||||||
|
freeMusics: SoundSyncData[];
|
||||||
|
musics: SoundSyncData[];
|
||||||
|
sounds: SoundSyncData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type SoundSyncData = {
|
||||||
|
loop: boolean;
|
||||||
|
volume: float;
|
||||||
|
rate: float;
|
||||||
|
resourceName: string;
|
||||||
|
position: float;
|
||||||
|
channel?: float;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type LoadRequestOptions = {
|
||||||
|
loadStorageName: string;
|
||||||
|
loadVariable: gdjs.Variable | null;
|
||||||
|
};
|
||||||
|
|
||||||
declare interface GameNetworkSyncData {
|
declare interface GameNetworkSyncData {
|
||||||
var?: VariableNetworkSyncData[];
|
var?: VariableNetworkSyncData[];
|
||||||
ss?: SceneStackNetworkSyncData;
|
ss?: SceneStackNetworkSyncData;
|
||||||
extVar?: {
|
extVar?: {
|
||||||
[extensionName: string]: VariableNetworkSyncData[];
|
[extensionName: string]: VariableNetworkSyncData[];
|
||||||
};
|
};
|
||||||
|
sm?: SoundManagerSyncData;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface EventsFunctionsExtensionData {
|
declare interface EventsFunctionsExtensionData {
|
||||||
|
9
GDJS/Runtime/types/save-state.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
declare type SceneSaveState = {
|
||||||
|
sceneData: LayoutNetworkSyncData;
|
||||||
|
objectDatas: { [objectId: integer]: ObjectNetworkSyncData };
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type GameSaveState = {
|
||||||
|
gameNetworkSyncData: GameNetworkSyncData;
|
||||||
|
layoutNetworkSyncDatas: SceneSaveState[];
|
||||||
|
};
|
@@ -242,8 +242,9 @@ namespace gdjs {
|
|||||||
const variable = this._variables.get(variableName);
|
const variable = this._variables.get(variableName);
|
||||||
const variableOwner = variable.getPlayerOwnership();
|
const variableOwner = variable.getPlayerOwnership();
|
||||||
if (
|
if (
|
||||||
// Variable undefined.
|
(!syncOptions.forceSyncEverything &&
|
||||||
variable.isUndefinedInContainer() ||
|
// Variable undefined.
|
||||||
|
variable.isUndefinedInContainer()) ||
|
||||||
// Variable marked as not to be synchronized.
|
// Variable marked as not to be synchronized.
|
||||||
variableOwner === null ||
|
variableOwner === null ||
|
||||||
// Getting sync data for a specific player:
|
// Getting sync data for a specific player:
|
||||||
@@ -352,7 +353,10 @@ namespace gdjs {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFromNetworkSyncData(networkSyncData: VariableNetworkSyncData[]) {
|
updateFromNetworkSyncData(
|
||||||
|
networkSyncData: VariableNetworkSyncData[],
|
||||||
|
options: UpdateFromNetworkSyncDataOptions
|
||||||
|
) {
|
||||||
const that = this;
|
const that = this;
|
||||||
for (let j = 0; j < networkSyncData.length; ++j) {
|
for (let j = 0; j < networkSyncData.length; ++j) {
|
||||||
const variableSyncData = networkSyncData[j];
|
const variableSyncData = networkSyncData[j];
|
||||||
@@ -370,20 +374,23 @@ namespace gdjs {
|
|||||||
// - If we are not the owner of the variable, then assume that we missed the ownership change message, so update the variable's
|
// - If we are not the owner of the variable, then assume that we missed the ownership change message, so update the variable's
|
||||||
// ownership and then update the variable.
|
// ownership and then update the variable.
|
||||||
const syncedVariableOwner = variableSyncData.owner;
|
const syncedVariableOwner = variableSyncData.owner;
|
||||||
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
|
if (!options.ignoreVariableOwnership) {
|
||||||
const currentVariableOwner = variable.getPlayerOwnership();
|
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
|
||||||
if (currentPlayerNumber === currentVariableOwner) {
|
|
||||||
console.info(
|
|
||||||
`Variable ${variableName} is owned by us ${gdjs.multiplayer.playerNumber}, ignoring update message from ${syncedVariableOwner}.`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syncedVariableOwner !== currentVariableOwner) {
|
const currentVariableOwner = variable.getPlayerOwnership();
|
||||||
console.info(
|
if (currentPlayerNumber === currentVariableOwner) {
|
||||||
`Variable ${variableName} is owned by ${currentVariableOwner} on our game, changing ownership to ${syncedVariableOwner} as part of the update event.`
|
console.info(
|
||||||
);
|
`Variable ${variableName} is owned by us ${gdjs.multiplayer.playerNumber}, ignoring update message from ${syncedVariableOwner}.`
|
||||||
variable.setPlayerOwnership(syncedVariableOwner);
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncedVariableOwner !== currentVariableOwner) {
|
||||||
|
console.info(
|
||||||
|
`Variable ${variableName} is owned by ${currentVariableOwner} on our game, changing ownership to ${syncedVariableOwner} as part of the update event.`
|
||||||
|
);
|
||||||
|
variable.setPlayerOwnership(syncedVariableOwner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
variable.reinitialize(variableData);
|
variable.reinitialize(variableData);
|
||||||
|
After Width: | Height: | Size: 172 B |
BIN
GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Coin.glb
Normal file
After Width: | Height: | Size: 100 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 430 B |
BIN
GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Van2.glb
Normal file
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 987 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.4 KiB |