Skip to content

Commit

Permalink
Fix hot reload of object variables in object instances
Browse files Browse the repository at this point in the history
  • Loading branch information
D8H committed Sep 16, 2024
1 parent 428aac8 commit 4961acf
Show file tree
Hide file tree
Showing 2 changed files with 256 additions and 60 deletions.
140 changes: 84 additions & 56 deletions GDJS/Runtime/debugger-client/hot-reloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ namespace gdjs {
this._runtimeGame = runtimeGame;
}

static groupByPersistentUuid<
static indexByPersistentUuid<
ObjectWithPersistentId extends { persistentUuid: string | null }
>(
objectsWithPersistentId: ObjectWithPersistentId[]
): Record<string, ObjectWithPersistentId> {
): Map<string, ObjectWithPersistentId> {
return objectsWithPersistentId.reduce(function (objectsMap, object) {
if (object.persistentUuid) {
objectsMap[object.persistentUuid] = object;
objectsMap.set(object.persistentUuid, object);
}
return objectsMap;
}, {});
}, new Map<string, ObjectWithPersistentId>());
}

static indexByName<E extends { name: string | null }>(
Expand Down Expand Up @@ -972,18 +972,9 @@ namespace gdjs {
hotReloadSucceeded;
});

// Don't update the variables, behaviors and effects for each runtime object to avoid
// Don't update behaviors and effects for each runtime object to avoid
// doing the check for differences for every single object.

// Update variables
runtimeObjects.forEach((runtimeObject) => {
this._hotReloadVariablesContainer(
oldObjectData.variables as Required<VariableData>[],
newObjectData.variables as Required<VariableData>[],
runtimeObject.getVariables()
);
});

// Update behaviors
this._hotReloadRuntimeObjectsBehaviors(
oldObjectData.behaviors,
Expand Down Expand Up @@ -1353,23 +1344,19 @@ namespace gdjs {
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
): void {
const runtimeObjects = runtimeInstanceContainer.getAdhocListOfAllInstances();
const groupedOldInstances: {
[key: number]: InstanceData;
} = HotReloader.groupByPersistentUuid(oldInstances);
const groupedNewInstances: {
[key: number]: InstanceData;
} = HotReloader.groupByPersistentUuid(newInstances);
const groupedRuntimeObjects: {
[key: number]: gdjs.RuntimeObject;
} = HotReloader.groupByPersistentUuid(runtimeObjects);
const oldInstanceByUuid = HotReloader.indexByPersistentUuid(oldInstances);
const newInstanceByUuid = HotReloader.indexByPersistentUuid(newInstances);
const runtimeObjectByUuid = HotReloader.indexByPersistentUuid(
runtimeObjects
);

const oldObjectsMap = HotReloader.indexByName(oldObjects);
const newObjectsMap = HotReloader.indexByName(newObjects);

for (const persistentUuid in groupedOldInstances) {
const oldInstance = groupedOldInstances[persistentUuid];
const newInstance = groupedNewInstances[persistentUuid];
const runtimeObject = groupedRuntimeObjects[persistentUuid];
for (const persistentUuid of oldInstanceByUuid.keys()) {
const oldInstance = oldInstanceByUuid.get(persistentUuid);
const newInstance = newInstanceByUuid.get(persistentUuid);
const runtimeObject = runtimeObjectByUuid.get(persistentUuid);

if (
oldInstance &&
Expand All @@ -1383,16 +1370,21 @@ namespace gdjs {
}
}

for (const persistentUuid in groupedRuntimeObjects) {
const runtimeObject = groupedRuntimeObjects[persistentUuid];
for (const runtimeObject of runtimeObjects) {
const oldObjectData = oldObjectsMap.get(runtimeObject.getName());
const newObjectData = newObjectsMap.get(runtimeObject.getName());
if (!runtimeObject || !oldObjectData || !newObjectData) {
// New objects or deleted objects can't have instances to hot-reload.
continue;
}
const oldInstance = groupedOldInstances[persistentUuid];
const newInstance = groupedNewInstances[persistentUuid];
const oldInstance = oldInstanceByUuid.get(
// @ts-ignore Private attribute
runtimeObject.persistentUuid
);
const newInstance = newInstanceByUuid.get(
// @ts-ignore Private attribute
runtimeObject.persistentUuid
);
if (oldInstance && newInstance) {
// Instance was not deleted nor created, maybe modified (or not):
this._hotReloadRuntimeInstance(
Expand All @@ -1405,33 +1397,44 @@ namespace gdjs {
newInstance,
runtimeObject
);
} else if (runtimeObject instanceof gdjs.CustomRuntimeObject) {
const childrenInstanceContainer = runtimeObject.getChildrenContainer();

// The `objects` attribute is already resolved by `resolveCustomObjectConfigurations()`.
const oldCustomObjectData = oldObjectData as ObjectData &
CustomObjectConfiguration &
InstanceContainerData;
const newCustomObjectData = newObjectData as ObjectData &
CustomObjectConfiguration &
InstanceContainerData;

// Reload the content of custom objects that were created at runtime.
this._hotReloadRuntimeInstanceContainer(
oldProjectData,
newProjectData,
oldCustomObjectData,
newCustomObjectData,
changedRuntimeBehaviors,
childrenInstanceContainer
} else {
// Reload objects that were created at runtime.

// Update variables
this._hotReloadVariablesContainer(
oldObjectData.variables,
newObjectData.variables,
runtimeObject.getVariables()
);

if (runtimeObject instanceof gdjs.CustomRuntimeObject) {
const childrenInstanceContainer = runtimeObject.getChildrenContainer();

// The `objects` attribute is already resolved by `resolveCustomObjectConfigurations()`.
const oldCustomObjectData = oldObjectData as ObjectData &
CustomObjectConfiguration &
InstanceContainerData;
const newCustomObjectData = newObjectData as ObjectData &
CustomObjectConfiguration &
InstanceContainerData;

// Reload the content of custom objects that were created at runtime.
this._hotReloadRuntimeInstanceContainer(
oldProjectData,
newProjectData,
oldCustomObjectData,
newCustomObjectData,
changedRuntimeBehaviors,
childrenInstanceContainer
);
}
}
}

for (const persistentUuid in groupedNewInstances) {
const oldInstance = groupedOldInstances[persistentUuid];
const newInstance = groupedNewInstances[persistentUuid];
const runtimeObject = groupedRuntimeObjects[persistentUuid];
for (const persistentUuid of newInstanceByUuid.keys()) {
const oldInstance = oldInstanceByUuid.get(persistentUuid);
const newInstance = newInstanceByUuid.get(persistentUuid);
const runtimeObject = runtimeObjectByUuid.get(persistentUuid);
if (
newInstance &&
(!oldInstance || oldInstance.name !== newInstance.name) &&
Expand Down Expand Up @@ -1577,8 +1580,14 @@ namespace gdjs {

// Update variables
this._hotReloadVariablesContainer(
oldInstance.initialVariables as Required<VariableData>[],
newInstance.initialVariables as Required<VariableData>[],
this._mergeObjectVariablesData(
oldObjectData.variables,
oldInstance.initialVariables
),
this._mergeObjectVariablesData(
newObjectData.variables,
newInstance.initialVariables
),
runtimeObject.getVariables()
);

Expand Down Expand Up @@ -1618,6 +1627,25 @@ namespace gdjs {
}
}

_mergeObjectVariablesData(
objectVariablesData: RootVariableData[],
instanceVariablesData: RootVariableData[]
): RootVariableData[] {
if (instanceVariablesData.length === 0) {
return objectVariablesData;
}
const variablesData = [...objectVariablesData];
for (const instanceVariableData of instanceVariablesData) {
const index = variablesData.indexOf(
(variableData) => variableData.name === instanceVariableData.name
);
if (index >= 0) {
variablesData[index] = instanceVariableData;
}
}
return variablesData;
}

/**
* Deep check equality between the two objects/arrays/primitives.
*
Expand Down
Loading

0 comments on commit 4961acf

Please sign in to comment.