feat(scripting): add field model editing and defaults support

This commit is contained in:
2026-03-28 15:09:42 +08:00
parent 4717b595c4
commit 14c7fd69ec
13 changed files with 2113 additions and 0 deletions

View File

@@ -1,6 +1,10 @@
#pragma once
#include <XCEngine/Scripting/ScriptField.h>
#include <cstdint>
#include <string>
#include <vector>
namespace XCEngine {
@@ -39,6 +43,29 @@ public:
virtual void OnRuntimeStart(Components::Scene* scene) = 0;
virtual void OnRuntimeStop(Components::Scene* scene) = 0;
virtual bool TryGetClassFieldMetadata(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className,
std::vector<ScriptFieldMetadata>& outFields) const = 0;
virtual bool TryGetClassFieldDefaultValues(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className,
std::vector<ScriptFieldDefaultValue>& outFields) const = 0;
virtual bool TrySetManagedFieldValue(
const ScriptRuntimeContext& context,
const std::string& fieldName,
const ScriptFieldValue& value) = 0;
virtual bool TryGetManagedFieldValue(
const ScriptRuntimeContext& context,
const std::string& fieldName,
ScriptFieldValue& outValue) const = 0;
virtual void SyncManagedFieldsToStorage(const ScriptRuntimeContext& context) = 0;
virtual bool CreateScriptInstance(const ScriptRuntimeContext& context) = 0;
virtual void DestroyScriptInstance(const ScriptRuntimeContext& context) = 0;

View File

@@ -50,6 +50,16 @@ public:
const std::string& namespaceName,
const std::string& className) const;
std::vector<std::string> GetScriptClassNames(const std::string& assemblyName = std::string()) const;
bool TryGetClassFieldMetadata(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className,
std::vector<ScriptFieldMetadata>& outFields) const override;
bool TryGetClassFieldDefaultValues(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className,
std::vector<ScriptFieldDefaultValue>& outFields) const override;
bool HasManagedInstance(const ScriptComponent* component) const;
size_t GetManagedInstanceCount() const { return m_instances.size(); }
@@ -79,6 +89,15 @@ public:
void OnRuntimeStart(Components::Scene* scene) override;
void OnRuntimeStop(Components::Scene* scene) override;
bool TrySetManagedFieldValue(
const ScriptRuntimeContext& context,
const std::string& fieldName,
const ScriptFieldValue& value) override;
bool TryGetManagedFieldValue(
const ScriptRuntimeContext& context,
const std::string& fieldName,
ScriptFieldValue& outValue) const override;
void SyncManagedFieldsToStorage(const ScriptRuntimeContext& context) override;
bool CreateScriptInstance(const ScriptRuntimeContext& context) override;
void DestroyScriptInstance(const ScriptRuntimeContext& context) override;
void InvokeMethod(const ScriptRuntimeContext& context, ScriptLifecycleMethod method, float deltaTime) override;

View File

@@ -9,6 +9,25 @@ class NullScriptRuntime : public IScriptRuntime {
public:
void OnRuntimeStart(Components::Scene* scene) override;
void OnRuntimeStop(Components::Scene* scene) override;
bool TryGetClassFieldMetadata(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className,
std::vector<ScriptFieldMetadata>& outFields) const override;
bool TryGetClassFieldDefaultValues(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className,
std::vector<ScriptFieldDefaultValue>& outFields) const override;
bool TrySetManagedFieldValue(
const ScriptRuntimeContext& context,
const std::string& fieldName,
const ScriptFieldValue& value) override;
bool TryGetManagedFieldValue(
const ScriptRuntimeContext& context,
const std::string& fieldName,
ScriptFieldValue& outValue) const override;
void SyncManagedFieldsToStorage(const ScriptRuntimeContext& context) override;
bool CreateScriptInstance(const ScriptRuntimeContext& context) override;
void DestroyScriptInstance(const ScriptRuntimeContext& context) override;

View File

@@ -2,9 +2,12 @@
#include <XCEngine/Scripting/IScriptRuntime.h>
#include <XCEngine/Scripting/NullScriptRuntime.h>
#include <XCEngine/Scripting/ScriptFieldStorage.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -37,6 +40,60 @@ public:
bool HasTrackedScriptComponent(const ScriptComponent* component) const;
bool HasRuntimeInstance(const ScriptComponent* component) const;
size_t GetTrackedScriptCount() const { return m_scriptOrder.size(); }
bool TrySetScriptFieldValue(
ScriptComponent* component,
const std::string& fieldName,
ScriptFieldType type,
const ScriptFieldValue& value);
bool TryGetScriptFieldValue(
const ScriptComponent* component,
const std::string& fieldName,
ScriptFieldValue& outValue) const;
bool ApplyScriptFieldWrites(
ScriptComponent* component,
const std::vector<ScriptFieldWriteRequest>& requests,
std::vector<ScriptFieldWriteResult>& outResults);
bool ClearScriptFieldOverrides(
ScriptComponent* component,
const std::vector<ScriptFieldClearRequest>& requests,
std::vector<ScriptFieldClearResult>& outResults);
bool TryGetScriptFieldModel(
const ScriptComponent* component,
ScriptFieldModel& outModel) const;
bool TryGetScriptFieldSnapshots(
const ScriptComponent* component,
std::vector<ScriptFieldSnapshot>& outFields) const;
template<typename T>
bool TrySetScriptFieldValue(ScriptComponent* component, const std::string& fieldName, const T& value) {
using ValueType = std::decay_t<T>;
return TrySetScriptFieldValue(
component,
fieldName,
ScriptFieldTypeResolver<ValueType>::value,
ScriptFieldValue(ValueType(value)));
}
bool TrySetScriptFieldValue(ScriptComponent* component, const std::string& fieldName, const char* value) {
return TrySetScriptFieldValue(component, fieldName, std::string(value ? value : ""));
}
template<typename T>
bool TryGetScriptFieldValue(const ScriptComponent* component, const std::string& fieldName, T& outValue) const {
ScriptFieldValue value;
if (!TryGetScriptFieldValue(component, fieldName, value)) {
return false;
}
using ValueType = std::decay_t<T>;
const ValueType* typedValue = std::get_if<ValueType>(&value);
if (!typedValue) {
return false;
}
outValue = *typedValue;
return true;
}
private:
struct ScriptInstanceKey {

View File

@@ -7,6 +7,7 @@
#include <cstdint>
#include <string>
#include <variant>
#include <vector>
namespace XCEngine {
namespace Scripting {
@@ -37,6 +38,56 @@ struct GameObjectReference {
}
};
struct ScriptFieldMetadata {
std::string name;
ScriptFieldType type = ScriptFieldType::None;
bool operator==(const ScriptFieldMetadata& other) const {
return name == other.name && type == other.type;
}
bool operator!=(const ScriptFieldMetadata& other) const {
return !(*this == other);
}
};
enum class ScriptFieldClassStatus {
Unassigned = 0,
Available,
Missing
};
enum class ScriptFieldValueSource {
None = 0,
DefaultValue,
StoredValue,
ManagedValue
};
enum class ScriptFieldIssue {
None = 0,
StoredOnly,
TypeMismatch
};
enum class ScriptFieldWriteStatus {
Applied = 0,
EmptyFieldName,
UnknownField,
InvalidValue,
TypeMismatch,
StoredOnlyField,
ApplyFailed
};
enum class ScriptFieldClearStatus {
Applied = 0,
EmptyFieldName,
UnknownField,
NoValueToClear,
ApplyFailed
};
using ScriptFieldValue = std::variant<
std::monostate,
float,
@@ -50,6 +101,80 @@ using ScriptFieldValue = std::variant<
Math::Vector4,
GameObjectReference>;
struct ScriptFieldDefaultValue {
std::string fieldName;
ScriptFieldType type = ScriptFieldType::None;
ScriptFieldValue value = std::monostate{};
bool operator==(const ScriptFieldDefaultValue& other) const {
return fieldName == other.fieldName
&& type == other.type
&& value == other.value;
}
bool operator!=(const ScriptFieldDefaultValue& other) const {
return !(*this == other);
}
};
struct ScriptFieldSnapshot {
ScriptFieldMetadata metadata;
bool declaredInClass = false;
bool hasDefaultValue = false;
ScriptFieldValue defaultValue = std::monostate{};
bool hasValue = false;
ScriptFieldValue value = std::monostate{};
ScriptFieldValueSource valueSource = ScriptFieldValueSource::None;
ScriptFieldIssue issue = ScriptFieldIssue::None;
bool hasStoredValue = false;
ScriptFieldType storedType = ScriptFieldType::None;
ScriptFieldValue storedValue = std::monostate{};
bool operator==(const ScriptFieldSnapshot& other) const {
return metadata == other.metadata
&& declaredInClass == other.declaredInClass
&& hasDefaultValue == other.hasDefaultValue
&& defaultValue == other.defaultValue
&& hasValue == other.hasValue
&& value == other.value
&& valueSource == other.valueSource
&& issue == other.issue
&& hasStoredValue == other.hasStoredValue
&& storedType == other.storedType
&& storedValue == other.storedValue;
}
bool operator!=(const ScriptFieldSnapshot& other) const {
return !(*this == other);
}
};
struct ScriptFieldModel {
ScriptFieldClassStatus classStatus = ScriptFieldClassStatus::Unassigned;
std::vector<ScriptFieldSnapshot> fields;
};
struct ScriptFieldWriteRequest {
std::string fieldName;
ScriptFieldType type = ScriptFieldType::None;
ScriptFieldValue value = std::monostate{};
};
struct ScriptFieldWriteResult {
std::string fieldName;
ScriptFieldType type = ScriptFieldType::None;
ScriptFieldWriteStatus status = ScriptFieldWriteStatus::ApplyFailed;
};
struct ScriptFieldClearRequest {
std::string fieldName;
};
struct ScriptFieldClearResult {
std::string fieldName;
ScriptFieldClearStatus status = ScriptFieldClearStatus::ApplyFailed;
};
std::string ScriptFieldTypeToString(ScriptFieldType type);
bool TryParseScriptFieldType(const std::string& value, ScriptFieldType& outType);