#include "Scripting/ScriptEngine.h" #include "Components/GameObject.h" #include "Scene/Scene.h" #include "Scripting/ScriptComponent.h" #include namespace XCEngine { namespace Scripting { namespace { std::unordered_map CollectClassDefaultValues( const IScriptRuntime* runtime, const ScriptComponent* component, ScriptFieldClassStatus classStatus) { std::unordered_map defaultValues; if (!runtime || !component || classStatus != ScriptFieldClassStatus::Available || !component->HasScriptClass()) { return defaultValues; } std::vector fields; if (!runtime->TryGetClassFieldDefaultValues( component->GetAssemblyName(), component->GetNamespaceName(), component->GetClassName(), fields)) { return defaultValues; } defaultValues.reserve(fields.size()); for (const ScriptFieldDefaultValue& field : fields) { if (field.fieldName.empty() || !IsScriptFieldValueCompatible(field.type, field.value)) { continue; } defaultValues.emplace(field.fieldName, field.value); } return defaultValues; } ScriptFieldValue ResolveScriptFieldDefaultValue( const ScriptFieldMetadata& metadata, const std::unordered_map& defaultValues) { const auto it = defaultValues.find(metadata.name); if (it != defaultValues.end() && IsScriptFieldValueCompatible(metadata.type, it->second)) { return it->second; } return CreateDefaultScriptFieldValue(metadata.type); } } // namespace ScriptEngine& ScriptEngine::Get() { static ScriptEngine instance; return instance; } void ScriptEngine::SetRuntime(IScriptRuntime* runtime) { m_runtime = runtime ? runtime : &m_nullRuntime; } void ScriptEngine::SetRuntimeFixedDeltaTime(float fixedDeltaTime) { if (fixedDeltaTime > 0.0f) { m_runtimeFixedDeltaTime = fixedDeltaTime; return; } m_runtimeFixedDeltaTime = DefaultFixedDeltaTime; } void ScriptEngine::OnRuntimeStart(Components::Scene* scene) { const float configuredFixedDeltaTime = m_runtimeFixedDeltaTime; Physics::PhysicsWorld* configuredPhysicsWorld = m_runtimePhysicsWorld; OnRuntimeStop(); m_runtimeFixedDeltaTime = configuredFixedDeltaTime; if (!scene) { return; } m_runtimePhysicsWorld = configuredPhysicsWorld; m_runtimeScene = scene; m_runtimeRunning = true; m_runtime->OnRuntimeStart(scene); m_runtimeSceneCreatedSubscription = scene->OnGameObjectCreated().Subscribe( [this](Components::GameObject* gameObject) { HandleGameObjectCreated(gameObject); }); for (Components::GameObject* root : scene->GetRootGameObjects()) { CollectScriptComponents(root); } const std::vector startupKeys = m_scriptOrder; for (const ScriptInstanceKey& key : startupKeys) { auto it = m_scriptStates.find(key); if (it == m_scriptStates.end()) { continue; } ScriptInstanceState& state = it->second; if (!ShouldScriptRun(state)) { continue; } EnsureScriptReady(state, true); } } void ScriptEngine::OnRuntimeStop() { if (m_runtimeScene && m_runtimeSceneCreatedSubscription != 0) { m_runtimeScene->OnGameObjectCreated().Unsubscribe(m_runtimeSceneCreatedSubscription); m_runtimeScene->OnGameObjectCreated().ProcessUnsubscribes(); m_runtimeSceneCreatedSubscription = 0; } if (!m_runtimeRunning) { m_runtimeScene = nullptr; m_runtimePhysicsWorld = nullptr; m_scriptStates.clear(); m_scriptOrder.clear(); m_runtimeFixedDeltaTime = DefaultFixedDeltaTime; return; } std::vector keys = m_scriptOrder; for (const ScriptInstanceKey& key : keys) { auto it = m_scriptStates.find(key); if (it != m_scriptStates.end()) { StopTrackingScript(it->second, true); } } Components::Scene* stoppedScene = m_runtimeScene; m_scriptStates.clear(); m_scriptOrder.clear(); m_runtimeRunning = false; m_runtimeScene = nullptr; m_runtimePhysicsWorld = nullptr; m_runtimeFixedDeltaTime = DefaultFixedDeltaTime; m_runtime->OnRuntimeStop(stoppedScene); } void ScriptEngine::OnRuntimeSceneReplaced(Components::Scene* scene) { if (!m_runtimeRunning) { m_runtimeScene = nullptr; m_runtimePhysicsWorld = nullptr; m_runtimeSceneCreatedSubscription = 0; m_scriptStates.clear(); m_scriptOrder.clear(); return; } const float configuredFixedDeltaTime = m_runtimeFixedDeltaTime; m_runtime->OnRuntimeStop(nullptr); m_runtimeFixedDeltaTime = configuredFixedDeltaTime; m_runtimeScene = scene; m_runtimeSceneCreatedSubscription = 0; m_scriptStates.clear(); m_scriptOrder.clear(); if (!m_runtimeScene) { return; } m_runtime->OnRuntimeStart(m_runtimeScene); m_runtimeSceneCreatedSubscription = m_runtimeScene->OnGameObjectCreated().Subscribe( [this](Components::GameObject* gameObject) { HandleGameObjectCreated(gameObject); }); for (Components::GameObject* root : m_runtimeScene->GetRootGameObjects()) { CollectScriptComponents(root); } const std::vector startupKeys = m_scriptOrder; for (const ScriptInstanceKey& key : startupKeys) { auto it = m_scriptStates.find(key); if (it == m_scriptStates.end()) { continue; } ScriptInstanceState& state = it->second; if (!ShouldScriptRun(state)) { continue; } EnsureScriptReady(state, true); } } void ScriptEngine::OnFixedUpdate(float fixedDeltaTime) { if (!m_runtimeRunning) { return; } const std::vector updateKeys = m_scriptOrder; for (const ScriptInstanceKey& key : updateKeys) { auto it = m_scriptStates.find(key); if (it == m_scriptStates.end()) { continue; } ScriptInstanceState& state = it->second; if (!ShouldScriptRun(state) || !EnsureScriptReady(state, true) || !state.enabled) { continue; } InvokeLifecycleMethod(state, ScriptLifecycleMethod::FixedUpdate, fixedDeltaTime); } } void ScriptEngine::OnUpdate(float deltaTime) { if (!m_runtimeRunning) { return; } const std::vector updateKeys = m_scriptOrder; for (const ScriptInstanceKey& key : updateKeys) { auto it = m_scriptStates.find(key); if (it == m_scriptStates.end()) { continue; } ScriptInstanceState& state = it->second; if (!ShouldScriptRun(state) || !EnsureScriptReady(state, true) || !state.enabled) { continue; } if (state.startPending && !state.startCalled) { InvokeLifecycleMethod(state, ScriptLifecycleMethod::Start); state.startCalled = true; state.startPending = false; } InvokeLifecycleMethod(state, ScriptLifecycleMethod::Update, deltaTime); } } void ScriptEngine::OnLateUpdate(float deltaTime) { if (!m_runtimeRunning) { return; } const std::vector updateKeys = m_scriptOrder; for (const ScriptInstanceKey& key : updateKeys) { auto it = m_scriptStates.find(key); if (it == m_scriptStates.end()) { continue; } ScriptInstanceState& state = it->second; if (!ShouldScriptRun(state) || !EnsureScriptReady(state, true) || !state.enabled) { continue; } InvokeLifecycleMethod(state, ScriptLifecycleMethod::LateUpdate, deltaTime); } } void ScriptEngine::DispatchPhysicsMessage( Components::GameObject* gameObject, ScriptPhysicsMessage message, Components::GameObject* other) { if (!m_runtimeRunning || !gameObject) { return; } for (ScriptComponent* component : gameObject->GetComponents()) { if (!component) { continue; } ScriptInstanceState* state = FindState(component); if (!state || !ShouldScriptRun(*state) || !EnsureScriptReady(*state, true) || !state->enabled) { continue; } InvokePhysicsMessage(*state, message, other); } } void ScriptEngine::OnScriptComponentEnabled(ScriptComponent* component) { if (!m_runtimeRunning || !component) { return; } ScriptInstanceState* state = TrackScriptComponent(component); if (!state) { return; } if (ShouldScriptRun(*state)) { EnsureScriptReady(*state, true); } } void ScriptEngine::OnScriptComponentDisabled(ScriptComponent* component) { if (!m_runtimeRunning || !component) { return; } ScriptInstanceState* state = FindState(component); if (!state || !state->enabled) { return; } InvokeLifecycleMethod(*state, ScriptLifecycleMethod::OnDisable); state->enabled = false; } void ScriptEngine::OnScriptComponentDestroyed(ScriptComponent* component) { if (!m_runtimeRunning || !component) { return; } ScriptInstanceState* state = FindState(component); if (!state) { return; } StopTrackingScript(*state, false); } void ScriptEngine::OnScriptComponentClassChanged(ScriptComponent* component) { if (!component) { return; } if (!m_runtimeRunning) { return; } if (ScriptInstanceState* state = FindState(component)) { StopTrackingScript(*state, false); } if (!component->HasScriptClass()) { return; } ScriptInstanceState* state = TrackScriptComponent(component); if (!state) { return; } if (ShouldScriptRun(*state)) { EnsureScriptReady(*state, true); } } bool ScriptEngine::HasTrackedScriptComponent(const ScriptComponent* component) const { return FindState(component) != nullptr; } bool ScriptEngine::HasRuntimeInstance(const ScriptComponent* component) const { const ScriptInstanceState* state = FindState(component); return state && state->instanceCreated; } bool ScriptEngine::TryGetAvailableScriptClasses( std::vector& outClasses, const std::string& assemblyName) const { outClasses.clear(); std::vector runtimeClasses; if (!m_runtime->TryGetAvailableScriptClasses(runtimeClasses)) { return false; } outClasses.reserve(runtimeClasses.size()); for (const ScriptClassDescriptor& descriptor : runtimeClasses) { if (!assemblyName.empty() && descriptor.assemblyName != assemblyName) { continue; } if (descriptor.className.empty()) { continue; } outClasses.push_back(descriptor); } std::sort( outClasses.begin(), outClasses.end(), [](const ScriptClassDescriptor& lhs, const ScriptClassDescriptor& rhs) { if (lhs.assemblyName != rhs.assemblyName) { return lhs.assemblyName < rhs.assemblyName; } if (lhs.namespaceName != rhs.namespaceName) { return lhs.namespaceName < rhs.namespaceName; } return lhs.className < rhs.className; }); return true; } bool ScriptEngine::TrySetScriptFieldValue( ScriptComponent* component, const std::string& fieldName, ScriptFieldType type, const ScriptFieldValue& value) { if (!component || fieldName.empty() || !IsScriptFieldValueCompatible(type, value)) { return false; } if (component->HasScriptClass()) { std::vector fields; if (m_runtime->TryGetClassFieldMetadata( component->GetAssemblyName(), component->GetNamespaceName(), component->GetClassName(), fields)) { const auto fieldIt = std::find_if( fields.begin(), fields.end(), [&fieldName](const ScriptFieldMetadata& metadata) { return metadata.name == fieldName; }); if (fieldIt == fields.end() || fieldIt->type != type) { return false; } } } ScriptInstanceState* state = m_runtimeRunning ? FindState(component) : nullptr; if (state && state->instanceCreated && !m_runtime->TrySetManagedFieldValue(state->context, fieldName, value)) { return false; } return component->GetFieldStorage().SetFieldValue(fieldName, type, value); } bool ScriptEngine::TryGetScriptFieldValue( const ScriptComponent* component, const std::string& fieldName, ScriptFieldValue& outValue) const { if (!component || fieldName.empty()) { return false; } const ScriptInstanceState* state = m_runtimeRunning ? FindState(component) : nullptr; if (state && state->instanceCreated && m_runtime->TryGetManagedFieldValue(state->context, fieldName, outValue)) { return true; } const StoredScriptField* storedField = component->GetFieldStorage().FindField(fieldName); if (!storedField) { return false; } outValue = storedField->value; return true; } bool ScriptEngine::ApplyScriptFieldWrites( ScriptComponent* component, const std::vector& requests, std::vector& outResults) { outResults.clear(); if (!component) { return false; } ScriptFieldModel model; if (!TryGetScriptFieldModel(component, model)) { return false; } const std::unordered_map defaultValues = CollectClassDefaultValues(m_runtime, component, model.classStatus); std::unordered_map fieldByName; fieldByName.reserve(model.fields.size()); for (const ScriptFieldSnapshot& field : model.fields) { fieldByName.emplace(field.metadata.name, &field); } outResults.reserve(requests.size()); bool allApplied = true; for (const ScriptFieldWriteRequest& request : requests) { ScriptFieldWriteResult result; result.fieldName = request.fieldName; result.type = request.type; if (request.fieldName.empty()) { result.status = ScriptFieldWriteStatus::EmptyFieldName; allApplied = false; outResults.push_back(std::move(result)); continue; } if (!IsScriptFieldValueCompatible(request.type, request.value)) { result.status = ScriptFieldWriteStatus::InvalidValue; allApplied = false; outResults.push_back(std::move(result)); continue; } const auto fieldIt = fieldByName.find(request.fieldName); if (fieldIt == fieldByName.end()) { result.status = ScriptFieldWriteStatus::UnknownField; allApplied = false; outResults.push_back(std::move(result)); continue; } const ScriptFieldSnapshot& field = *fieldIt->second; if (field.metadata.type != request.type) { result.status = ScriptFieldWriteStatus::TypeMismatch; allApplied = false; outResults.push_back(std::move(result)); continue; } if (model.classStatus == ScriptFieldClassStatus::Available && !field.declaredInClass) { result.status = ScriptFieldWriteStatus::StoredOnlyField; allApplied = false; outResults.push_back(std::move(result)); continue; } const bool applied = field.declaredInClass ? TrySetScriptFieldValue(component, request.fieldName, request.type, request.value) : component->GetFieldStorage().SetFieldValue(request.fieldName, request.type, request.value); if (!applied) { result.status = ScriptFieldWriteStatus::ApplyFailed; allApplied = false; outResults.push_back(std::move(result)); continue; } result.status = ScriptFieldWriteStatus::Applied; outResults.push_back(std::move(result)); } return allApplied; } bool ScriptEngine::ClearScriptFieldOverrides( ScriptComponent* component, const std::vector& requests, std::vector& outResults) { outResults.clear(); if (!component) { return false; } ScriptFieldModel model; if (!TryGetScriptFieldModel(component, model)) { return false; } const std::unordered_map defaultValues = CollectClassDefaultValues(m_runtime, component, model.classStatus); std::unordered_map fieldByName; fieldByName.reserve(model.fields.size()); for (const ScriptFieldSnapshot& field : model.fields) { fieldByName.emplace(field.metadata.name, &field); } ScriptInstanceState* state = m_runtimeRunning ? FindState(component) : nullptr; const bool hasLiveInstance = state && state->instanceCreated; outResults.reserve(requests.size()); bool allApplied = true; for (const ScriptFieldClearRequest& request : requests) { ScriptFieldClearResult result; result.fieldName = request.fieldName; if (request.fieldName.empty()) { result.status = ScriptFieldClearStatus::EmptyFieldName; allApplied = false; outResults.push_back(std::move(result)); continue; } const auto fieldIt = fieldByName.find(request.fieldName); if (fieldIt == fieldByName.end()) { result.status = ScriptFieldClearStatus::UnknownField; allApplied = false; outResults.push_back(std::move(result)); continue; } const ScriptFieldSnapshot& field = *fieldIt->second; bool resetManagedValue = false; if (field.declaredInClass && hasLiveInstance) { if (!m_runtime->TrySetManagedFieldValue( state->context, field.metadata.name, ResolveScriptFieldDefaultValue(field.metadata, defaultValues))) { result.status = ScriptFieldClearStatus::ApplyFailed; allApplied = false; outResults.push_back(std::move(result)); continue; } resetManagedValue = true; } bool removedStoredValue = false; if (field.hasStoredValue) { removedStoredValue = component->GetFieldStorage().Remove(field.metadata.name); if (!removedStoredValue) { result.status = ScriptFieldClearStatus::ApplyFailed; allApplied = false; outResults.push_back(std::move(result)); continue; } } if (!removedStoredValue && !resetManagedValue) { result.status = ScriptFieldClearStatus::NoValueToClear; allApplied = false; outResults.push_back(std::move(result)); continue; } result.status = ScriptFieldClearStatus::Applied; outResults.push_back(std::move(result)); } return allApplied; } bool ScriptEngine::TryGetScriptFieldModel( const ScriptComponent* component, ScriptFieldModel& outModel) const { outModel = ScriptFieldModel{}; if (!component) { return false; } std::vector metadataFields; if (!component->HasScriptClass()) { outModel.classStatus = ScriptFieldClassStatus::Unassigned; } else if (m_runtime->TryGetClassFieldMetadata( component->GetAssemblyName(), component->GetNamespaceName(), component->GetClassName(), metadataFields)) { outModel.classStatus = ScriptFieldClassStatus::Available; } else { outModel.classStatus = ScriptFieldClassStatus::Missing; } const std::unordered_map defaultValues = CollectClassDefaultValues(m_runtime, component, outModel.classStatus); const ScriptInstanceState* state = m_runtimeRunning ? FindState(component) : nullptr; std::unordered_map fieldIndexByName; fieldIndexByName.reserve(metadataFields.size()); for (const ScriptFieldMetadata& metadata : metadataFields) { ScriptFieldSnapshot snapshot; snapshot.metadata = metadata; snapshot.declaredInClass = true; snapshot.hasDefaultValue = true; snapshot.defaultValue = ResolveScriptFieldDefaultValue(metadata, defaultValues); snapshot.value = snapshot.defaultValue; snapshot.valueSource = ScriptFieldValueSource::DefaultValue; const StoredScriptField* storedField = component->GetFieldStorage().FindField(metadata.name); if (storedField) { snapshot.hasStoredValue = true; snapshot.storedType = storedField->type; snapshot.storedValue = storedField->value; if (storedField->type != metadata.type) { snapshot.issue = ScriptFieldIssue::TypeMismatch; } } if (state && state->instanceCreated && m_runtime->TryGetManagedFieldValue(state->context, metadata.name, snapshot.value)) { snapshot.hasValue = true; snapshot.valueSource = ScriptFieldValueSource::ManagedValue; } else if (storedField && storedField->type == metadata.type) { snapshot.hasValue = true; snapshot.value = storedField->value; snapshot.valueSource = ScriptFieldValueSource::StoredValue; } fieldIndexByName.emplace(metadata.name, outModel.fields.size()); outModel.fields.push_back(std::move(snapshot)); } for (const std::string& fieldName : component->GetFieldStorage().GetFieldNames()) { const StoredScriptField* storedField = component->GetFieldStorage().FindField(fieldName); if (!storedField) { continue; } const auto fieldIndexIt = fieldIndexByName.find(fieldName); if (fieldIndexIt != fieldIndexByName.end()) { continue; } ScriptFieldSnapshot snapshot; snapshot.metadata = ScriptFieldMetadata{fieldName, storedField->type}; snapshot.hasValue = true; snapshot.value = storedField->value; snapshot.valueSource = ScriptFieldValueSource::StoredValue; snapshot.issue = ScriptFieldIssue::StoredOnly; snapshot.hasStoredValue = true; snapshot.storedType = storedField->type; snapshot.storedValue = storedField->value; outModel.fields.push_back(std::move(snapshot)); } if (outModel.classStatus != ScriptFieldClassStatus::Available) { std::sort( outModel.fields.begin(), outModel.fields.end(), [](const ScriptFieldSnapshot& lhs, const ScriptFieldSnapshot& rhs) { return lhs.metadata.name < rhs.metadata.name; }); } return true; } bool ScriptEngine::TryGetScriptFieldSnapshots( const ScriptComponent* component, std::vector& outFields) const { ScriptFieldModel model; if (!TryGetScriptFieldModel(component, model)) { outFields.clear(); return false; } outFields = std::move(model.fields); return !outFields.empty(); } size_t ScriptEngine::ScriptInstanceKeyHasher::operator()(const ScriptInstanceKey& key) const { const size_t h1 = std::hash{}(key.gameObjectUUID); const size_t h2 = std::hash{}(key.scriptComponentUUID); return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2)); } void ScriptEngine::CollectScriptComponents(Components::GameObject* gameObject) { if (!gameObject) { return; } for (ScriptComponent* component : gameObject->GetComponents()) { TrackScriptComponent(component); } for (Components::GameObject* child : gameObject->GetChildren()) { CollectScriptComponents(child); } } void ScriptEngine::EnsureTrackedScriptsReady(Components::GameObject* gameObject) { if (!gameObject) { return; } for (ScriptComponent* component : gameObject->GetComponents()) { ScriptInstanceState* state = FindState(component); if (!state || !ShouldScriptRun(*state)) { continue; } EnsureScriptReady(*state, true); } for (Components::GameObject* child : gameObject->GetChildren()) { EnsureTrackedScriptsReady(child); } } void ScriptEngine::HandleGameObjectCreated(Components::GameObject* gameObject) { if (!m_runtimeRunning || !gameObject || gameObject->GetScene() != m_runtimeScene) { return; } CollectScriptComponents(gameObject); EnsureTrackedScriptsReady(gameObject); } ScriptEngine::ScriptInstanceState* ScriptEngine::TrackScriptComponent(ScriptComponent* component) { if (!component || !component->GetGameObject()) { return nullptr; } const ScriptInstanceKey key{ component->GetGameObject()->GetUUID(), component->GetScriptComponentUUID() }; auto it = m_scriptStates.find(key); if (it != m_scriptStates.end()) { it->second.context.scene = component->GetScene(); it->second.context.gameObject = component->GetGameObject(); it->second.context.component = component; it->second.context.gameObjectUUID = component->GetGameObject()->GetUUID(); it->second.context.scriptComponentUUID = component->GetScriptComponentUUID(); return &it->second; } ScriptInstanceState state; state.context.scene = component->GetScene(); state.context.gameObject = component->GetGameObject(); state.context.component = component; state.context.gameObjectUUID = component->GetGameObject()->GetUUID(); state.context.scriptComponentUUID = component->GetScriptComponentUUID(); auto [insertedIt, inserted] = m_scriptStates.emplace(key, std::move(state)); if (inserted) { m_scriptOrder.push_back(key); } return &insertedIt->second; } ScriptEngine::ScriptInstanceState* ScriptEngine::FindState(const ScriptComponent* component) { if (!component || !component->GetGameObject()) { return nullptr; } const ScriptInstanceKey key{ component->GetGameObject()->GetUUID(), component->GetScriptComponentUUID() }; auto it = m_scriptStates.find(key); return it != m_scriptStates.end() ? &it->second : nullptr; } const ScriptEngine::ScriptInstanceState* ScriptEngine::FindState(const ScriptComponent* component) const { if (!component || !component->GetGameObject()) { return nullptr; } const ScriptInstanceKey key{ component->GetGameObject()->GetUUID(), component->GetScriptComponentUUID() }; auto it = m_scriptStates.find(key); return it != m_scriptStates.end() ? &it->second : nullptr; } void ScriptEngine::RemoveTrackedScriptComponent(const ScriptComponent* component) { if (!component || !component->GetGameObject()) { return; } const ScriptInstanceKey key{ component->GetGameObject()->GetUUID(), component->GetScriptComponentUUID() }; m_scriptStates.erase(key); m_scriptOrder.erase( std::remove(m_scriptOrder.begin(), m_scriptOrder.end(), key), m_scriptOrder.end()); } bool ScriptEngine::ShouldScriptRun(const ScriptInstanceState& state) const { return m_runtimeRunning && state.context.scene == m_runtimeScene && state.context.scene != nullptr && state.context.scene->IsActive() && state.context.gameObject != nullptr && state.context.gameObject->IsActiveInHierarchy() && state.context.component != nullptr && state.context.component->IsEnabled() && state.context.component->HasScriptClass(); } bool ScriptEngine::EnsureScriptReady(ScriptInstanceState& state, bool invokeEnableIfNeeded) { if (!state.context.component || !state.context.gameObject || !state.context.scene || !state.context.component->HasScriptClass()) { return false; } if (!state.instanceCreated) { if (!m_runtime->CreateScriptInstance(state.context)) { return false; } state.instanceCreated = true; } if (!state.awakeCalled) { InvokeLifecycleMethod(state, ScriptLifecycleMethod::Awake); state.awakeCalled = true; } if (invokeEnableIfNeeded && !state.enabled && ShouldScriptRun(state)) { InvokeLifecycleMethod(state, ScriptLifecycleMethod::OnEnable); state.enabled = true; if (!state.startCalled) { state.startPending = true; } } return true; } void ScriptEngine::InvokeLifecycleMethod(ScriptInstanceState& state, ScriptLifecycleMethod method, float deltaTime) { m_runtime->InvokeMethod(state.context, method, deltaTime); m_runtime->SyncManagedFieldsToStorage(state.context); } void ScriptEngine::InvokePhysicsMessage( ScriptInstanceState& state, ScriptPhysicsMessage message, Components::GameObject* other) { m_runtime->InvokePhysicsMessage(state.context, message, other); m_runtime->SyncManagedFieldsToStorage(state.context); } void ScriptEngine::StopTrackingScript(ScriptInstanceState& state, bool runtimeStopping) { if (state.enabled) { InvokeLifecycleMethod(state, ScriptLifecycleMethod::OnDisable); state.enabled = false; } if (state.instanceCreated) { InvokeLifecycleMethod(state, ScriptLifecycleMethod::OnDestroy); m_runtime->DestroyScriptInstance(state.context); state.instanceCreated = false; } state.startPending = false; if (!runtimeStopping) { RemoveTrackedScriptComponent(state.context.component); } } } // namespace Scripting } // namespace XCEngine