158 lines
5.8 KiB
C++
158 lines
5.8 KiB
C++
#pragma once
|
|
|
|
#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>
|
|
|
|
namespace XCEngine {
|
|
namespace Scripting {
|
|
|
|
class ScriptComponent;
|
|
|
|
class ScriptEngine {
|
|
public:
|
|
static ScriptEngine& Get();
|
|
static constexpr float DefaultFixedDeltaTime = 1.0f / 50.0f;
|
|
|
|
void SetRuntime(IScriptRuntime* runtime);
|
|
IScriptRuntime* GetRuntime() const { return m_runtime; }
|
|
void SetRuntimeFixedDeltaTime(float fixedDeltaTime);
|
|
float GetRuntimeFixedDeltaTime() const { return m_runtimeFixedDeltaTime; }
|
|
|
|
void OnRuntimeStart(Components::Scene* scene);
|
|
void OnRuntimeStop();
|
|
void OnRuntimeSceneReplaced(Components::Scene* scene);
|
|
|
|
void OnFixedUpdate(float fixedDeltaTime);
|
|
void OnUpdate(float deltaTime);
|
|
void OnLateUpdate(float deltaTime);
|
|
|
|
void OnScriptComponentEnabled(ScriptComponent* component);
|
|
void OnScriptComponentDisabled(ScriptComponent* component);
|
|
void OnScriptComponentDestroyed(ScriptComponent* component);
|
|
void OnScriptComponentClassChanged(ScriptComponent* component);
|
|
|
|
bool IsRuntimeRunning() const { return m_runtimeRunning; }
|
|
Components::Scene* GetRuntimeScene() const { return m_runtimeScene; }
|
|
|
|
bool HasTrackedScriptComponent(const ScriptComponent* component) const;
|
|
bool HasRuntimeInstance(const ScriptComponent* component) const;
|
|
size_t GetTrackedScriptCount() const { return m_scriptOrder.size(); }
|
|
bool TryGetAvailableScriptClasses(
|
|
std::vector<ScriptClassDescriptor>& outClasses,
|
|
const std::string& assemblyName = std::string()) const;
|
|
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 {
|
|
uint64_t gameObjectUUID = 0;
|
|
uint64_t scriptComponentUUID = 0;
|
|
|
|
bool operator==(const ScriptInstanceKey& other) const {
|
|
return gameObjectUUID == other.gameObjectUUID
|
|
&& scriptComponentUUID == other.scriptComponentUUID;
|
|
}
|
|
};
|
|
|
|
struct ScriptInstanceKeyHasher {
|
|
size_t operator()(const ScriptInstanceKey& key) const;
|
|
};
|
|
|
|
struct ScriptInstanceState {
|
|
ScriptRuntimeContext context;
|
|
bool instanceCreated = false;
|
|
bool awakeCalled = false;
|
|
bool enabled = false;
|
|
bool startCalled = false;
|
|
bool startPending = false;
|
|
};
|
|
|
|
ScriptEngine() = default;
|
|
|
|
void CollectScriptComponents(Components::GameObject* gameObject);
|
|
void EnsureTrackedScriptsReady(Components::GameObject* gameObject);
|
|
void HandleGameObjectCreated(Components::GameObject* gameObject);
|
|
ScriptInstanceState* TrackScriptComponent(ScriptComponent* component);
|
|
ScriptInstanceState* FindState(const ScriptComponent* component);
|
|
const ScriptInstanceState* FindState(const ScriptComponent* component) const;
|
|
void RemoveTrackedScriptComponent(const ScriptComponent* component);
|
|
|
|
bool ShouldScriptRun(const ScriptInstanceState& state) const;
|
|
bool EnsureScriptReady(ScriptInstanceState& state, bool invokeEnableIfNeeded);
|
|
void InvokeLifecycleMethod(ScriptInstanceState& state, ScriptLifecycleMethod method, float deltaTime = 0.0f);
|
|
void StopTrackingScript(ScriptInstanceState& state, bool runtimeStopping);
|
|
|
|
NullScriptRuntime m_nullRuntime;
|
|
IScriptRuntime* m_runtime = &m_nullRuntime;
|
|
Components::Scene* m_runtimeScene = nullptr;
|
|
bool m_runtimeRunning = false;
|
|
float m_runtimeFixedDeltaTime = DefaultFixedDeltaTime;
|
|
uint64_t m_runtimeSceneCreatedSubscription = 0;
|
|
|
|
std::unordered_map<ScriptInstanceKey, ScriptInstanceState, ScriptInstanceKeyHasher> m_scriptStates;
|
|
std::vector<ScriptInstanceKey> m_scriptOrder;
|
|
};
|
|
|
|
} // namespace Scripting
|
|
} // namespace XCEngine
|