-
Notifications
You must be signed in to change notification settings - Fork 916
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a button to export all the objetcs of a scene to submit them to t…
…he asset store (#4848) * The dialog can be reached from the hidden drop-down menu of "Scene objects".
- Loading branch information
Showing
33 changed files
with
998 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
/* | ||
* GDevelop Core | ||
* Copyright 2008-2023 Florian Rival ([email protected]). All rights | ||
* reserved. This project is released under the MIT License. | ||
*/ | ||
#include "ObjectAssetSerializer.h" | ||
|
||
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h" | ||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h" | ||
#include "GDCore/Extensions/Metadata/MetadataProvider.h" | ||
#include "GDCore/Extensions/Platform.h" | ||
#include "GDCore/Extensions/PlatformExtension.h" | ||
#include "GDCore/IDE/Project/AssetResourcePathCleaner.h" | ||
#include "GDCore/IDE/Project/ResourcesInUseHelper.h" | ||
#include "GDCore/IDE/Project/ResourcesRenamer.h" | ||
#include "GDCore/Project/Behavior.h" | ||
#include "GDCore/Project/CustomBehavior.h" | ||
#include "GDCore/Project/EventsFunctionsExtension.h" | ||
#include "GDCore/Project/Layout.h" | ||
#include "GDCore/Project/Object.h" | ||
#include "GDCore/Project/Project.h" | ||
#include "GDCore/Project/PropertyDescriptor.h" | ||
#include "GDCore/Serialization/SerializerElement.h" | ||
#include "GDCore/Tools/Log.h" | ||
|
||
namespace gd { | ||
|
||
gd::String | ||
ObjectAssetSerializer::GetObjectExtensionName(const gd::Object &object) { | ||
const gd::String &type = object.GetType(); | ||
const auto separatorIndex = | ||
type.find(PlatformExtension::GetNamespaceSeparator()); | ||
return separatorIndex != std::string::npos ? type.substr(0, separatorIndex) | ||
: ""; | ||
} | ||
|
||
void ObjectAssetSerializer::SerializeTo( | ||
gd::Project &project, const gd::Object &object, | ||
const gd::String &objectFullName, SerializerElement &element, | ||
std::map<gd::String, gd::String> &resourcesFileNameMap) { | ||
auto cleanObject = object.Clone(); | ||
cleanObject->GetVariables().Clear(); | ||
cleanObject->GetEffects().Clear(); | ||
for (auto &&behaviorName : cleanObject->GetAllBehaviorNames()) { | ||
cleanObject->RemoveBehavior(behaviorName); | ||
} | ||
|
||
gd::String extensionName = GetObjectExtensionName(*cleanObject); | ||
|
||
std::map<gd::String, gd::String> resourcesNameReverseMap; | ||
gd::ObjectAssetSerializer::RenameObjectResourceFiles( | ||
project, *cleanObject, "", objectFullName, resourcesFileNameMap, | ||
resourcesNameReverseMap); | ||
|
||
element.SetAttribute("id", ""); | ||
element.SetAttribute("name", ""); | ||
element.SetAttribute("license", ""); | ||
if (project.HasEventsFunctionsExtensionNamed(extensionName)) { | ||
auto &extension = project.GetEventsFunctionsExtension(extensionName); | ||
element.SetAttribute("description", extension.GetShortDescription()); | ||
} | ||
element.SetAttribute("gdevelopVersion", ""); | ||
element.SetAttribute("version", ""); | ||
element.SetIntAttribute("animationsCount", 1); | ||
element.SetIntAttribute("maxFramesCount", 1); | ||
// TODO Find the right object dimensions. | ||
element.SetIntAttribute("width", 0); | ||
element.SetIntAttribute("height", 0); | ||
SerializerElement &authorsElement = element.AddChild("authors"); | ||
authorsElement.ConsiderAsArrayOf("author"); | ||
SerializerElement &tagsElement = element.AddChild("tags"); | ||
tagsElement.ConsiderAsArrayOf("tag"); | ||
|
||
SerializerElement &objectAssetsElement = element.AddChild("objectAssets"); | ||
objectAssetsElement.ConsiderAsArrayOf("objectAsset"); | ||
SerializerElement &objectAssetElement = | ||
objectAssetsElement.AddChild("objectAsset"); | ||
|
||
cleanObject->SerializeTo(objectAssetElement.AddChild("object")); | ||
|
||
SerializerElement &resourcesElement = | ||
objectAssetElement.AddChild("resources"); | ||
resourcesElement.ConsiderAsArrayOf("resource"); | ||
auto &resourcesManager = project.GetResourcesManager(); | ||
gd::ResourcesInUseHelper resourcesInUse(resourcesManager); | ||
cleanObject->GetConfiguration().ExposeResources(resourcesInUse); | ||
for (auto &&newResourceName : resourcesInUse.GetAllResources()) { | ||
if (newResourceName.length() == 0) { | ||
continue; | ||
} | ||
auto &resource = resourcesManager.GetResource( | ||
resourcesNameReverseMap.find(newResourceName) != | ||
resourcesNameReverseMap.end() | ||
? resourcesNameReverseMap[newResourceName] | ||
: newResourceName); | ||
SerializerElement &resourceElement = resourcesElement.AddChild("resource"); | ||
resource.SerializeTo(resourceElement); | ||
// Override name and file because the project and the asset don't use the | ||
// same one. | ||
resourceElement.SetAttribute("kind", resource.GetKind()); | ||
resourceElement.SetAttribute("name", newResourceName); | ||
auto &oldFilePath = resource.GetFile(); | ||
resourceElement.SetAttribute("file", | ||
resourcesFileNameMap.find(oldFilePath) != | ||
resourcesFileNameMap.end() | ||
? resourcesFileNameMap[oldFilePath] | ||
: oldFilePath); | ||
} | ||
|
||
SerializerElement &requiredExtensionsElement = | ||
objectAssetElement.AddChild("requiredExtensions"); | ||
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension"); | ||
if (project.HasEventsFunctionsExtensionNamed(extensionName)) { | ||
SerializerElement &requiredExtensionElement = | ||
requiredExtensionsElement.AddChild("requiredExtension"); | ||
requiredExtensionElement.SetAttribute("extensionName", extensionName); | ||
requiredExtensionElement.SetAttribute("extensionVersion", "1.0.0"); | ||
} | ||
|
||
// TODO This can be removed when the asset script no longer require it. | ||
SerializerElement &customizationElement = | ||
objectAssetElement.AddChild("customization"); | ||
customizationElement.ConsiderAsArrayOf("empty"); | ||
} | ||
|
||
void ObjectAssetSerializer::RenameObjectResourceFiles( | ||
gd::Project &project, gd::Object &object, | ||
const gd::String &destinationDirectory, const gd::String &objectFullName, | ||
std::map<gd::String, gd::String> &resourcesFileNameMap, | ||
std::map<gd::String, gd::String> &resourcesNameReverseMap) { | ||
gd::AssetResourcePathCleaner assetResourcePathCleaner( | ||
project.GetResourcesManager(), resourcesFileNameMap, | ||
resourcesNameReverseMap); | ||
object.GetConfiguration().ExposeResources(assetResourcePathCleaner); | ||
|
||
// Use asset store script naming conventions for sprite resource files. | ||
if (object.GetConfiguration().GetType() == "Sprite") { | ||
gd::SpriteObject &spriteConfiguration = | ||
dynamic_cast<gd::SpriteObject &>(object.GetConfiguration()); | ||
std::map<gd::String, gd::String> normalizedFileNames; | ||
|
||
for (std::size_t animationIndex = 0; | ||
animationIndex < spriteConfiguration.GetAnimationsCount(); | ||
animationIndex++) { | ||
auto &animation = spriteConfiguration.GetAnimation(animationIndex); | ||
auto &direction = animation.GetDirection(0); | ||
|
||
const gd::String &animationName = | ||
animation.GetName().empty() | ||
? gd::String::From(animationIndex) | ||
: animation.GetName().FindAndReplace("_", " ", true); | ||
|
||
// Search frames that share the same resource. | ||
std::map<gd::String, std::vector<int>> frameIndexes; | ||
for (std::size_t frameIndex = 0; frameIndex < direction.GetSpritesCount(); | ||
frameIndex++) { | ||
auto &frame = direction.GetSprite(frameIndex); | ||
|
||
if (frameIndexes.find(frame.GetImageName()) == frameIndexes.end()) { | ||
std::vector<int> emptyVector; | ||
frameIndexes[frame.GetImageName()] = emptyVector; | ||
} | ||
auto &indexes = frameIndexes[frame.GetImageName()]; | ||
indexes.push_back(frameIndex); | ||
} | ||
|
||
for (std::size_t frameIndex = 0; frameIndex < direction.GetSpritesCount(); | ||
frameIndex++) { | ||
auto &frame = direction.GetSprite(frameIndex); | ||
auto oldName = frame.GetImageName(); | ||
|
||
if (normalizedFileNames.find(oldName) != normalizedFileNames.end()) { | ||
gd::LogWarning("The resource \"" + oldName + | ||
"\" is shared by several animations."); | ||
continue; | ||
} | ||
|
||
gd::String newName = objectFullName; | ||
if (spriteConfiguration.GetAnimationsCount() > 1) { | ||
newName += "_" + animationName; | ||
} | ||
if (direction.GetSpritesCount() > 1) { | ||
newName += "_"; | ||
auto &indexes = frameIndexes[frame.GetImageName()]; | ||
for (size_t i = 0; i < indexes.size(); i++) { | ||
newName += gd::String::From(indexes.at(i) + 1); | ||
if (i < indexes.size() - 1) { | ||
newName += ";"; | ||
} | ||
} | ||
} | ||
gd::String extension = oldName.substr(oldName.find_last_of(".")); | ||
newName += extension; | ||
|
||
frame.SetImageName(newName); | ||
normalizedFileNames[oldName] = newName; | ||
} | ||
} | ||
for (std::map<gd::String, gd::String>::const_iterator it = | ||
resourcesFileNameMap.begin(); | ||
it != resourcesFileNameMap.end(); ++it) { | ||
if (!it->first.empty()) { | ||
gd::String originFile = it->first; | ||
gd::String destinationFile = it->second; | ||
resourcesFileNameMap[originFile] = normalizedFileNames[destinationFile]; | ||
} | ||
} | ||
auto clonedResourcesNameReverseMap = resourcesNameReverseMap; | ||
resourcesNameReverseMap.clear(); | ||
for (std::map<gd::String, gd::String>::const_iterator it = | ||
clonedResourcesNameReverseMap.begin(); | ||
it != clonedResourcesNameReverseMap.end(); ++it) { | ||
if (!it->first.empty()) { | ||
gd::String newResourceName = it->first; | ||
gd::String oldResourceName = it->second; | ||
resourcesNameReverseMap[normalizedFileNames[newResourceName]] = | ||
oldResourceName; | ||
} | ||
} | ||
} | ||
} | ||
} // namespace gd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* GDevelop Core | ||
* Copyright 2008-2023 Florian Rival ([email protected]). All rights | ||
* reserved. This project is released under the MIT License. | ||
*/ | ||
#pragma once | ||
#include <map> | ||
#include <vector> | ||
|
||
#include "GDCore/String.h" | ||
|
||
namespace gd { | ||
class Object; | ||
class ExtensionDependency; | ||
class PropertyDescriptor; | ||
class Project; | ||
class Layout; | ||
class ArbitraryResourceWorker; | ||
class InitialInstance; | ||
class SerializerElement; | ||
class EffectsContainer; | ||
class AbstractFileSystem; | ||
} // namespace gd | ||
|
||
namespace gd { | ||
|
||
/** | ||
* \brief Serialize objects into an asset for the store. | ||
* | ||
* \ingroup IDE | ||
*/ | ||
class GD_CORE_API ObjectAssetSerializer { | ||
public: | ||
/** | ||
* \brief Serialize an object into an asset. | ||
* | ||
* \param project The project that contains the object and its resources. | ||
* It's not actually modified. | ||
* \param object The object to serialize as an asset. | ||
* \param objectFullName The object name with spaces instead of PascalCase. | ||
* \param element The element where the asset is serialize. | ||
* \param resourcesFileNameMap The map from project resource file paths to | ||
* asset resource file paths. | ||
*/ | ||
static void | ||
SerializeTo(gd::Project &project, const gd::Object &object, | ||
const gd::String &objectFullName, SerializerElement &element, | ||
std::map<gd::String, gd::String> &resourcesFileNameMap); | ||
|
||
~ObjectAssetSerializer(){}; | ||
|
||
private: | ||
ObjectAssetSerializer(){}; | ||
|
||
static void RenameObjectResourceFiles( | ||
gd::Project &project, gd::Object &object, | ||
const gd::String &destinationDirectory, const gd::String &objectFullName, | ||
std::map<gd::String, gd::String> &resourcesFileNameMap, | ||
std::map<gd::String, gd::String> &resourcesNameReverseMap); | ||
|
||
static gd::String GetObjectExtensionName(const gd::Object &object); | ||
}; | ||
|
||
} // namespace gd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* GDevelop Core | ||
* Copyright 2008-2023 Florian Rival ([email protected]). All rights | ||
* reserved. This project is released under the MIT License. | ||
*/ | ||
|
||
#include "AssetResourcePathCleaner.h" | ||
#include "GDCore/Project/Project.h" | ||
#include "GDCore/Project/ResourcesManager.h" | ||
#include "GDCore/String.h" | ||
|
||
namespace gd { | ||
void AssetResourcePathCleaner::ExposeImage(gd::String &imageName) { | ||
ExposeResourceAsFile(imageName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeAudio(gd::String &audioName) { | ||
ExposeResourceAsFile(audioName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeFont(gd::String &fontName) { | ||
ExposeResourceAsFile(fontName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeJson(gd::String &jsonName) { | ||
ExposeResourceAsFile(jsonName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeTilemap(gd::String &tilemapName) { | ||
ExposeResourceAsFile(tilemapName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeTileset(gd::String &tilesetName) { | ||
ExposeResourceAsFile(tilesetName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeVideo(gd::String &videoName) { | ||
ExposeResourceAsFile(videoName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeBitmapFont(gd::String &bitmapFontName) { | ||
ExposeResourceAsFile(bitmapFontName); | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeResourceAsFile(gd::String &resourceName) { | ||
|
||
auto &resource = resourcesManager->GetResource(resourceName); | ||
gd::String file = resource.GetFile(); | ||
ExposeFile(file); | ||
|
||
resourcesNameReverseMap[file] = resourceName; | ||
resourceName = file; | ||
} | ||
|
||
void AssetResourcePathCleaner::ExposeFile(gd::String &resourceFilePath) { | ||
|
||
size_t slashPos = resourceFilePath.find_last_of("/"); | ||
size_t antiSlashPos = resourceFilePath.find_last_of("\\"); | ||
size_t baseNamePos = | ||
slashPos == String::npos | ||
? antiSlashPos == String::npos ? 0 : (antiSlashPos + 1) | ||
: antiSlashPos == String::npos ? (slashPos + 1) | ||
: slashPos > antiSlashPos ? (slashPos + 1) | ||
: (antiSlashPos + 1); | ||
gd::String baseName = | ||
baseNamePos != 0 | ||
? resourceFilePath.substr(baseNamePos, resourceFilePath.length()) | ||
: resourceFilePath; | ||
|
||
resourcesFileNameMap[resourceFilePath] = baseName; | ||
resourceFilePath = baseName; | ||
} | ||
|
||
} // namespace gd |
Oops, something went wrong.