Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Core/GDCore/Project/EventsBasedObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,10 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
}
}

void EventsBasedObject::UnserializeDefaultVariantFrom(
gd::Project &project, const SerializerElement &element) {
defaultVariant.UnserializeFrom(project, element);
defaultVariant.SetName("");
}

} // namespace gd
3 changes: 3 additions & 0 deletions Core/GDCore/Project/EventsBasedObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,9 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
void UnserializeFrom(gd::Project& project,
const SerializerElement& element) override;

void UnserializeDefaultVariantFrom(gd::Project &project,
const SerializerElement &element);

private:
gd::String defaultName;
gd::String assetStoreTag;
Expand Down
104 changes: 25 additions & 79 deletions Core/GDCore/Project/EventsFunctionsExtension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,25 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
}
}

void EventsFunctionsExtension::UnserializeExtensionDefaultVariantsFrom(
gd::Project& project,
const SerializerElement& element) {
auto &eventsBasedObjectsElement = element.GetChild("eventsBasedObjects");
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (std::size_t elementIndex = 0;
elementIndex < eventsBasedObjectsElement.GetChildrenCount();
++elementIndex) {
const SerializerElement& eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(elementIndex);
const gd::String& eventsBasedObjectName = eventsBasedObjectElement.GetStringAttribute("name");
size_t extensionIndex = eventsBasedObjects.GetPosition(
eventsBasedObjects.Get(eventsBasedObjectName));

eventsBasedObjects.at(extensionIndex)
.UnserializeDefaultVariantFrom(project, eventsBasedObjectElement);
}
}

void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(
gd::Project& project,
const SerializerElement& element) {
Expand All @@ -249,93 +268,20 @@ void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(

auto &eventsBasedObjectsElement = element.GetChild("eventsBasedObjects");
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (gd::String &eventsBasedObjectName :
GetUnserializingOrderEventsBasedObjectNames(eventsBasedObjectsElement)) {
for (std::size_t elementIndex = 0;
elementIndex < eventsBasedObjectsElement.GetChildrenCount();
++elementIndex) {
const SerializerElement& eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(elementIndex);
const gd::String& eventsBasedObjectName = eventsBasedObjectElement.GetStringAttribute("name");
size_t extensionIndex = eventsBasedObjects.GetPosition(
eventsBasedObjects.Get(eventsBasedObjectName));
const SerializerElement &eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(extensionIndex);

eventsBasedObjects.at(extensionIndex)
.UnserializeFrom(project, eventsBasedObjectElement);
}
}

std::vector<gd::String>
EventsFunctionsExtension::GetUnserializingOrderEventsBasedObjectNames(
const gd::SerializerElement &eventsBasedObjectsElement) {

// Child-objects need the event-based objects they use to be loaded completely
// before they are unserialized.

// At the beginning, everything is yet to be loaded.
std::vector<gd::String> remainingEventsBasedObjectNames(
eventsBasedObjects.size());
for (std::size_t i = 0; i < eventsBasedObjects.size(); ++i) {
remainingEventsBasedObjectNames[i] = eventsBasedObjects.at(i).GetName();
}

// Helper allowing to find if an object depends on at least one other object from
// the extension that is not loaded yet.
auto &extensionName = name;
auto isDependentFromRemainingEventsBasedObjects =
[&remainingEventsBasedObjectNames,
&extensionName](const gd::SerializerElement &eventsBasedObjectElement) {
auto &objectsElement = eventsBasedObjectElement.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");

for (std::size_t objectIndex = 0;
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
const gd::String &objectType =
objectsElement.GetChild(objectIndex).GetStringAttribute("type");

gd::String usedExtensionName =
PlatformExtension::GetExtensionFromFullObjectType(objectType);
if (usedExtensionName != extensionName) {
// The object comes from another extension: the project is already responsible
// for loading extensions in the proper order.
continue;
}
gd::String eventsBasedObjectName =
gd::PlatformExtension::GetObjectNameFromFullObjectType(
objectType);

if (std::find(remainingEventsBasedObjectNames.begin(),
remainingEventsBasedObjectNames.end(),
eventsBasedObjectName) !=
remainingEventsBasedObjectNames.end()) {
return true;
}
}
return false;
};

// Find the order of loading so that the objects are loaded when all the objects
// they depend on are already loaded.
std::vector<gd::String> loadOrderEventsBasedObjectNames;
bool foundAnyEventsBasedObject = true;
while (foundAnyEventsBasedObject) {
foundAnyEventsBasedObject = false;
for (std::size_t i = 0; i < remainingEventsBasedObjectNames.size(); ++i) {
auto eventsBasedObjectName = remainingEventsBasedObjectNames[i];
size_t extensionIndex = eventsBasedObjects.GetPosition(
eventsBasedObjects.Get(eventsBasedObjectName));
const SerializerElement &eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(extensionIndex);

if (!isDependentFromRemainingEventsBasedObjects(
eventsBasedObjectElement)) {
loadOrderEventsBasedObjectNames.push_back(eventsBasedObjectName);
remainingEventsBasedObjectNames.erase(
remainingEventsBasedObjectNames.begin() + i);
i--;
foundAnyEventsBasedObject = true;
}
}
}
return loadOrderEventsBasedObjectNames;
}

bool EventsFunctionsExtension::IsExtensionLifecycleEventsFunction(
const gd::String& eventsFunctionName) {
// The list of all supported lifecycle events function names.
Expand Down
9 changes: 6 additions & 3 deletions Core/GDCore/Project/EventsFunctionsExtension.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ class GD_CORE_API EventsFunctionsExtension {
gd::Project& project,
const gd::SerializerElement& element);

/**
* \brief Load default variants of all the events-based objects.
*/
void UnserializeExtensionDefaultVariantsFrom(gd::Project &project,
const SerializerElement &element);

/**
* \brief Load free functions, behaviors and objects implementation
* (in opposition to load just their "declaration" by reading their name).
Expand Down Expand Up @@ -394,9 +400,6 @@ class GD_CORE_API EventsFunctionsExtension {
return dependency;
}

std::vector<gd::String> GetUnserializingOrderEventsBasedObjectNames(
const gd::SerializerElement &eventsBasedObjectsElement);

gd::String version;
gd::String extensionNamespace;
gd::String shortDescription;
Expand Down
139 changes: 42 additions & 97 deletions Core/GDCore/Project/Project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,6 @@ void Project::UnserializeAndInsertExtensionsFrom(
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");

std::map<gd::String, size_t> extensionNameToElementIndex;
std::map<gd::String, gd::SerializerElement> objectTypeToVariantsElement;

// First, only unserialize behaviors and objects names.
Expand All @@ -946,7 +945,6 @@ void Project::UnserializeAndInsertExtensionsFrom(
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
const gd::String& name = eventsFunctionsExtensionElement.GetStringAttribute("name");
extensionNameToElementIndex[name] = i;

gd::EventsFunctionsExtension& eventsFunctionsExtension =
HasEventsFunctionsExtensionNamed(name)
Expand All @@ -967,9 +965,14 @@ void Project::UnserializeAndInsertExtensionsFrom(
*this, eventsFunctionsExtensionElement);
}

// Then unserialize functions, behaviors and objects content.
for (gd::String &extensionName :
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
// Then unserialize default variants to be able to parse legacy children
// overridings in the next step.
for (std::size_t elementIndex = 0;
elementIndex < eventsFunctionsExtensionsElement.GetChildrenCount();
++elementIndex) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(elementIndex);
const gd::String& extensionName = eventsFunctionsExtensionElement.GetStringAttribute("name");

size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
if (extensionIndex == gd::String::npos) {
Expand All @@ -978,19 +981,46 @@ void Project::UnserializeAndInsertExtensionsFrom(
continue;
}
auto& partiallyLoadedExtension = eventsFunctionsExtensions.at(extensionIndex);
partiallyLoadedExtension
->UnserializeExtensionDefaultVariantsFrom(
*this, eventsFunctionsExtensionElement);
}

if (extensionNameToElementIndex.find(extensionName) == extensionNameToElementIndex.end()) {
// Should never happen because the extension element is present.
gd::LogError("Can't find extension element to unserialize for " + extensionName + " in second pass of unserialization.");
continue;
}
size_t elementIndex = extensionNameToElementIndex[extensionName];
const SerializerElement &eventsFunctionsExtensionElement =
// Then unserialize functions, behaviors and objects content.
for (std::size_t elementIndex = 0;
elementIndex < eventsFunctionsExtensionsElement.GetChildrenCount();
++elementIndex) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(elementIndex);
const gd::String& extensionName = eventsFunctionsExtensionElement.GetStringAttribute("name");

size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
if (extensionIndex == gd::String::npos) {
// Should never happen because the extension was added in the first pass.
gd::LogError("Can't find extension " + extensionName + " in the list of extensions in second pass of unserialization.");
continue;
}
auto& partiallyLoadedExtension = eventsFunctionsExtensions.at(extensionIndex);
partiallyLoadedExtension
->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}

// Unserialize variants at the end in case their objects has children overriding.
for (std::size_t elementIndex = 0;
elementIndex < eventsFunctionsExtensionsElement.GetChildrenCount();
++elementIndex) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(elementIndex);
const gd::String& extensionName = eventsFunctionsExtensionElement.GetStringAttribute("name");

size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
if (extensionIndex == gd::String::npos) {
// Should never happen because the extension was added in the first pass.
gd::LogError("Can't find extension " + extensionName + " in the list of extensions in third pass of unserialization.");
continue;
}
auto& partiallyLoadedExtension = eventsFunctionsExtensions.at(extensionIndex);

for (auto &pair : objectTypeToVariantsElement) {
auto &objectType = pair.first;
Expand All @@ -1003,91 +1033,6 @@ void Project::UnserializeAndInsertExtensionsFrom(
}
}

std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");

// Some extension have custom objects, which have child objects coming from other extension.
// These child objects must be loaded completely before the parent custom obejct can be unserialized.
// This implies: an order on the extension unserialization (and no cycles).

// At the beginning, everything is yet to be loaded.
std::map<gd::String, size_t> extensionNameToElementIndex;
std::vector<gd::String> remainingExtensionNames(
eventsFunctionsExtensionsElement.GetChildrenCount());
for (std::size_t i = 0; i < eventsFunctionsExtensionsElement.GetChildrenCount(); ++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
const gd::String& name = eventsFunctionsExtensionElement.GetStringAttribute("name");

remainingExtensionNames[i] = name;
extensionNameToElementIndex[name] = i;
}

// Helper allowing to find if an extension has an object that depends on
// at least one other object from another extension that is not loaded yet.
auto isDependentFromRemainingExtensions =
[&remainingExtensionNames](
const gd::SerializerElement &eventsFunctionsExtensionElement) {
auto &eventsBasedObjectsElement =
eventsFunctionsExtensionElement.GetChild("eventsBasedObjects");
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (std::size_t eventsBasedObjectsIndex = 0;
eventsBasedObjectsIndex <
eventsBasedObjectsElement.GetChildrenCount();
++eventsBasedObjectsIndex) {
auto &objectsElement =
eventsBasedObjectsElement.GetChild(eventsBasedObjectsIndex)
.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");

for (std::size_t objectIndex = 0;
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
const gd::String &objectType =
objectsElement.GetChild(objectIndex).GetStringAttribute("type");

gd::String extensionName =
eventsFunctionsExtensionElement.GetStringAttribute("name");
gd::String usedExtensionName =
gd::PlatformExtension::GetExtensionFromFullObjectType(objectType);

if (usedExtensionName != extensionName &&
std::find(remainingExtensionNames.begin(),
remainingExtensionNames.end(),
usedExtensionName) != remainingExtensionNames.end()) {
return true;
}
}
}
return false;
};

// Find the order of loading so that the extensions are loaded when all the other
// extensions they depend on are already loaded.
std::vector<gd::String> loadOrderExtensionNames;
bool foundAnyExtension = true;
while (foundAnyExtension) {
foundAnyExtension = false;
for (std::size_t i = 0; i < remainingExtensionNames.size(); ++i) {
auto extensionName = remainingExtensionNames[i];

size_t elementIndex = extensionNameToElementIndex[extensionName];
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(elementIndex);

if (!isDependentFromRemainingExtensions(
eventsFunctionsExtensionElement)) {
loadOrderExtensionNames.push_back(extensionName);
remainingExtensionNames.erase(remainingExtensionNames.begin() + i);
i--;
foundAnyExtension = true;
}
}
}
return loadOrderExtensionNames;
}

void Project::SerializeTo(SerializerElement& element) const {
SerializerElement& versionElement = element.AddChild("gdVersion");
versionElement.SetAttribute("major", gd::VersionWrapper::Major());
Expand Down
12 changes: 0 additions & 12 deletions Core/GDCore/Project/Project.h
Original file line number Diff line number Diff line change
Expand Up @@ -1101,18 +1101,6 @@ class GD_CORE_API Project {
return wholeProjectDiagnosticReport;
}

/**
* @brief Get the project extensions names in the order they have to be
* unserialized.
*
* Child-objects need the event-based objects they use to be loaded completely
* before they are unserialized.
*
* \warning This is only public to allow testing - don't use it in the editor.
*/
static std::vector<gd::String> GetUnserializingOrderExtensionNames(
const gd::SerializerElement& eventsFunctionsExtensionsElement);

private:
/**
* Initialize from another game. Used by copy-ctor and assign-op.
Expand Down
Loading
Loading