Ensure behaviors are compatible with the object before pasting them (#7343)

This commit is contained in:
D8H
2025-02-03 13:02:41 +01:00
committed by GitHub
parent 6440149fc8
commit 97e989dee0
8 changed files with 121 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
/*
* GDevelop Core
* Copyright 2008-2025 Florian Rival ([email protected]). All rights
* reserved. This project is released under the MIT License.
*/
#include "ObjectTools.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
namespace gd {
bool ObjectTools::IsBehaviorCompatibleWithObject(
const gd::Platform &platform, const gd::String &objectType,
const gd::String &behaviorType,
std::unordered_set<gd::String> coveredBehaviorType) {
bool isBehaviorTypeAlreadyCovered =
!coveredBehaviorType.insert(behaviorType).second;
if (isBehaviorTypeAlreadyCovered) {
return true;
}
const gd::BehaviorMetadata &behaviorMetadata =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
// Should not happen because the behavior was added successfully (so its
// metadata are valid) - but double check anyway and bail out if the
// behavior metadata are invalid.
return false;
}
if (!behaviorMetadata.GetObjectType().empty() &&
behaviorMetadata.GetObjectType() != objectType) {
return false;
}
for (const gd::String &requiredBehaviorType :
behaviorMetadata.GetRequiredBehaviorTypes()) {
const gd::BehaviorMetadata &requiredBehaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(platform, requiredBehaviorType);
if (requiredBehaviorMetadata.IsHidden()) {
const gd::ObjectMetadata &objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (objectMetadata.GetDefaultBehaviors().find(requiredBehaviorType) ==
objectMetadata.GetDefaultBehaviors().end()) {
// A capability is missing in the object.
return false;
}
}
if (!gd::ObjectTools::IsBehaviorCompatibleWithObject(
platform, objectType, requiredBehaviorType, coveredBehaviorType)) {
return false;
}
}
return true;
}
} // namespace gd
+35
View File
@@ -0,0 +1,35 @@
/*
* GDevelop Core
* Copyright 2008-2025 Florian Rival ([email protected]). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/String.h"
#include <unordered_set>
namespace gd {
class Platform;
class Object;
} // namespace gd
namespace gd {
class GD_CORE_API ObjectTools {
public:
static bool IsBehaviorCompatibleWithObject(const gd::Platform &platform,
const gd::String &objectType,
const gd::String &behaviorType) {
std::unordered_set<gd::String> coveredBehaviorType;
return IsBehaviorCompatibleWithObject(platform, objectType, behaviorType,
coveredBehaviorType);
}
private:
static bool IsBehaviorCompatibleWithObject(
const gd::Platform &platform, const gd::String &objectType,
const gd::String &behaviorType,
std::unordered_set<gd::String> coveredBehaviorType);
};
} // namespace gd
+7
View File
@@ -2786,6 +2786,13 @@ interface WholeProjectRefactorer {
[Value] SetString STATIC_FindAllLeaderboardIds([Ref] Project project);
};
interface ObjectTools {
boolean STATIC_IsBehaviorCompatibleWithObject(
[Const, Ref] Platform platform,
[Const] DOMString objectType,
[Const] DOMString behaviorType);
};
interface EventsBasedObjectDependencyFinder {
boolean STATIC_IsDependentFromEventsBasedObject(
[Const, Ref] Project project,
+2
View File
@@ -78,6 +78,7 @@
#include <GDCore/Project/MeasurementUnitElement.h>
#include <GDCore/Project/NamedPropertyDescriptor.h>
#include <GDCore/Project/Object.h>
#include <GDCore/Project/ObjectTools.h>
#include <GDCore/Project/ObjectFolderOrObject.h>
#include <GDCore/Project/ObjectConfiguration.h>
#include <GDCore/Project/Project.h>
@@ -659,6 +660,7 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
#define STATIC_FindInvalidRequiredBehaviorProperties \
FindInvalidRequiredBehaviorProperties
#define STATIC_GetBehaviorsWithType GetBehaviorsWithType
#define STATIC_IsBehaviorCompatibleWithObject IsBehaviorCompatibleWithObject
#define STATIC_FixInvalidRequiredBehaviorProperties \
FixInvalidRequiredBehaviorProperties
#define STATIC_RemoveLayerInScene RemoveLayerInScene
+4
View File
@@ -1985,6 +1985,10 @@ export class WholeProjectRefactorer extends EmscriptenObject {
static findAllLeaderboardIds(project: Project): SetString;
}
export class ObjectTools extends EmscriptenObject {
static isBehaviorCompatibleWithObject(platform: Platform, objectType: string, behaviorType: string): boolean;
}
export class EventsBasedObjectDependencyFinder extends EmscriptenObject {
static isDependentFromEventsBasedObject(project: Project, eventsBasedObject: EventsBasedObject, dependency: EventsBasedObject): boolean;
}
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdObjectTools {
static isBehaviorCompatibleWithObject(platform: gdPlatform, objectType: string, behaviorType: string): boolean;
delete(): void;
ptr: number;
};
+1
View File
@@ -184,6 +184,7 @@ declare class libGDevelop {
ResourceExposer: Class<gdResourceExposer>;
VariablesChangeset: Class<gdVariablesChangeset>;
WholeProjectRefactorer: Class<gdWholeProjectRefactorer>;
ObjectTools: Class<gdObjectTools>;
EventsBasedObjectDependencyFinder: Class<gdEventsBasedObjectDependencyFinder>;
PropertyFunctionGenerator: Class<gdPropertyFunctionGenerator>;
UsedExtensionsResult: Class<gdUsedExtensionsResult>;
+9
View File
@@ -477,6 +477,15 @@ export const useManageObjectBehaviors = ({
if (!name || !type || !serializedBehavior) {
return;
}
if (
!gd.ObjectTools.isBehaviorCompatibleWithObject(
project.getCurrentPlatform(),
object.getType(),
type
)
) {
return;
}
const behaviorMetadata = gd.MetadataProvider.getBehaviorMetadata(
project.getCurrentPlatform(),