|
|
@@ -1,115 +1,71 @@
|
|
|
+//
|
|
|
+// Copyright (c) 2008-2014 the Urho3D project.
|
|
|
+//
|
|
|
+// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
+// of this software and associated documentation files (the "Software"), to deal
|
|
|
+// in the Software without restriction, including without limitation the rights
|
|
|
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
+// copies of the Software, and to permit persons to whom the Software is
|
|
|
+// furnished to do so, subject to the following conditions:
|
|
|
+//
|
|
|
+// The above copyright notice and this permission notice shall be included in
|
|
|
+// all copies or substantial portions of the Software.
|
|
|
+//
|
|
|
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
+// THE SOFTWARE.
|
|
|
+//
|
|
|
+
|
|
|
// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
|
|
|
// Please see LICENSE.md in repository root for license information
|
|
|
// https://github.com/AtomicGameEngine/AtomicGameEngine
|
|
|
|
|
|
-
|
|
|
-#include <Atomic/Core/Context.h>
|
|
|
#include <Atomic/IO/Log.h>
|
|
|
+#include <Atomic/IO/FileSystem.h>
|
|
|
+#include <Atomic/Core/Context.h>
|
|
|
+#include <Atomic/Resource/ResourceCache.h>
|
|
|
+
|
|
|
#ifdef ATOMIC_PHYSICS
|
|
|
#include <Atomic/Physics/PhysicsEvents.h>
|
|
|
#include <Atomic/Physics/PhysicsWorld.h>
|
|
|
#endif
|
|
|
-
|
|
|
-#include <Atomic/Core/Profiler.h>
|
|
|
-#include <Atomic/IO/MemoryBuffer.h>
|
|
|
-#include <Atomic/Resource/ResourceCache.h>
|
|
|
-#include <Atomic/Resource/ResourceEvents.h>
|
|
|
#include <Atomic/Scene/Scene.h>
|
|
|
#include <Atomic/Scene/SceneEvents.h>
|
|
|
-#include <Atomic/Atomic2D/PhysicsEvents2D.h>
|
|
|
-#include <Atomic/Atomic2D/PhysicsWorld2D.h>
|
|
|
-#include <Atomic/Atomic2D/RigidBody2D.h>
|
|
|
-#include <Atomic/UI/UIEvents.h>
|
|
|
|
|
|
-#include "Javascript.h"
|
|
|
-#include "JSEvents.h"
|
|
|
+#include "JSVM.h"
|
|
|
+#include "JSComponentFile.h"
|
|
|
#include "JSComponent.h"
|
|
|
-#include "JSAPI.h"
|
|
|
|
|
|
namespace Atomic
|
|
|
{
|
|
|
|
|
|
-static const char* methodDeclarations[] = {
|
|
|
- "start",
|
|
|
- "stop",
|
|
|
- "delayedStart",
|
|
|
- "update",
|
|
|
- "postUpdate",
|
|
|
- "fixedUpdate",
|
|
|
- "fixedPostUpdate",
|
|
|
- "load",
|
|
|
- "save",
|
|
|
- "readNetworkUpdate",
|
|
|
- "writeNetworkUpdate",
|
|
|
- "applyAttributes",
|
|
|
- "transformChanged"
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
extern const char* LOGIC_CATEGORY;
|
|
|
|
|
|
JSComponent::JSComponent(Context* context) :
|
|
|
Component(context),
|
|
|
- script_(GetSubsystem<Javascript>()),
|
|
|
- scriptObject_(0),
|
|
|
- subscribed_(false),
|
|
|
- subscribedPostFixed_(false),
|
|
|
+ updateEventMask_(USE_UPDATE | USE_POSTUPDATE | USE_FIXEDUPDATE | USE_FIXEDPOSTUPDATE),
|
|
|
+ currentEventMask_(0),
|
|
|
started_(false),
|
|
|
- destroyed_(false)
|
|
|
+ delayedStartCalled_(false),
|
|
|
+ loading_(false)
|
|
|
{
|
|
|
vm_ = JSVM::GetJSVM(NULL);
|
|
|
- ClearScriptMethods();
|
|
|
}
|
|
|
|
|
|
JSComponent::~JSComponent()
|
|
|
{
|
|
|
-}
|
|
|
-
|
|
|
-void JSComponent::OnNodeSet(Node *node)
|
|
|
-{
|
|
|
- Component::OnNodeSet(node);
|
|
|
|
|
|
- if (node && node->JSGetHeapPtr())
|
|
|
- {
|
|
|
- //assert(node->JSGetHeapPtr());
|
|
|
-
|
|
|
- duk_context* ctx = vm_->GetJSContext();
|
|
|
- int top = duk_get_top(ctx);
|
|
|
- duk_push_global_stash(ctx);
|
|
|
- duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_INDEX_NODE_REGISTRY);
|
|
|
- // can't use instance as key, as this coerces to [Object] for
|
|
|
- // string property, pointer will be string representation of
|
|
|
- // address, so, unique key
|
|
|
- duk_push_pointer(ctx, (void*) node);
|
|
|
- js_push_class_object_instance(ctx, node);
|
|
|
- duk_put_prop(ctx, -3);
|
|
|
- duk_pop_2(ctx);
|
|
|
- assert(duk_get_top(ctx) == top);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
void JSComponent::RegisterObject(Context* context)
|
|
|
{
|
|
|
context->RegisterFactory<JSComponent>(LOGIC_CATEGORY);
|
|
|
ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
|
|
|
- ACCESSOR_ATTRIBUTE("Component Name", GetClassNameProperty, SetClassNameProperty, String, String::EMPTY, AM_DEFAULT);
|
|
|
-}
|
|
|
-
|
|
|
-void JSComponent::ApplyAttributes()
|
|
|
-{
|
|
|
- Component::ApplyAttributes();
|
|
|
-
|
|
|
- if (classNameProperty_.Length())
|
|
|
- SetClassName(classNameProperty_);
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-void JSComponent::ClearScriptMethods()
|
|
|
-{
|
|
|
- for (unsigned i = 0; i < MAX_JSSCRIPT_METHODS; ++i)
|
|
|
- methods_[i] = 0;
|
|
|
-
|
|
|
- //delayedCalls_.Clear();
|
|
|
+ MIXED_ACCESSOR_ATTRIBUTE("ComponentFile", GetScriptAttr, SetScriptAttr, ResourceRef, ResourceRef(JSComponentFile::GetTypeStatic()), AM_DEFAULT);
|
|
|
}
|
|
|
|
|
|
void JSComponent::OnSetEnabled()
|
|
|
@@ -117,423 +73,350 @@ void JSComponent::OnSetEnabled()
|
|
|
UpdateEventSubscription();
|
|
|
}
|
|
|
|
|
|
-void JSComponent::ListenToEvent(Object* sender, StringHash eventType, JS_HEAP_PTR __duk_function)
|
|
|
+void JSComponent::SetUpdateEventMask(unsigned char mask)
|
|
|
{
|
|
|
- duk_context* ctx = vm_->GetJSContext();
|
|
|
- duk_push_heapptr(ctx, __duk_function);
|
|
|
- assert(duk_is_function(ctx, -1));
|
|
|
- duk_pop(ctx);
|
|
|
-
|
|
|
- scriptEventFunctions_[eventType] = __duk_function;
|
|
|
- if (sender)
|
|
|
- SubscribeToEvent(sender, eventType, HANDLER(JSComponent, HandleScriptEvent));
|
|
|
- else
|
|
|
- SubscribeToEvent(eventType, HANDLER(JSComponent, HandleScriptEvent));
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-bool JSComponent::CreateObject(JSFile* scriptFile, const String& className)
|
|
|
-{
|
|
|
- className_ = String::EMPTY; // Do not create object during SetScriptFile()
|
|
|
- SetScriptFile(scriptFile);
|
|
|
- SetClassName(className);
|
|
|
- return scriptObject_ != 0;
|
|
|
+ if (updateEventMask_ != mask)
|
|
|
+ {
|
|
|
+ updateEventMask_ = mask;
|
|
|
+ UpdateEventSubscription();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-void JSComponent::SetClassName(const String& className)
|
|
|
+void JSComponent::UpdateReferences(bool remove)
|
|
|
{
|
|
|
- assert(className.Length());
|
|
|
-
|
|
|
- if (className == className_ && scriptObject_)
|
|
|
- return;
|
|
|
+ duk_context* ctx = vm_->GetJSContext();
|
|
|
|
|
|
- ReleaseObject();
|
|
|
+ int top = duk_get_top(ctx);
|
|
|
|
|
|
- className_ = className;
|
|
|
- CreateObject();
|
|
|
- MarkNetworkUpdate();
|
|
|
-}
|
|
|
+ duk_push_global_stash(ctx);
|
|
|
+ duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_INDEX_NODE_REGISTRY);
|
|
|
|
|
|
+ // can't use instance as key, as this coerces to [Object] for
|
|
|
+ // string property, pointer will be string representation of
|
|
|
+ // address, so, unique key
|
|
|
|
|
|
-void JSComponent::ReleaseObject()
|
|
|
-{
|
|
|
- if (scriptObject_)
|
|
|
- {
|
|
|
- //if (methods_[JSMETHOD_STOP])
|
|
|
- // scriptFile_->Execute(scriptObject_, methods_[JSMETHOD_STOP]);
|
|
|
+ duk_push_pointer(ctx, (void*) node_);
|
|
|
+ if (remove)
|
|
|
+ duk_push_undefined(ctx);
|
|
|
+ else
|
|
|
+ js_push_class_object_instance(ctx, node_);
|
|
|
|
|
|
- PODVector<StringHash> exceptions;
|
|
|
- exceptions.Push(E_RELOADSTARTED);
|
|
|
- exceptions.Push(E_RELOADFINISHED);
|
|
|
- UnsubscribeFromAllEventsExcept(exceptions, false);
|
|
|
+ duk_put_prop(ctx, -3);
|
|
|
|
|
|
- if (node_)
|
|
|
- node_->RemoveListener(this);
|
|
|
+ duk_push_pointer(ctx, (void*) this);
|
|
|
+ if (remove)
|
|
|
+ duk_push_undefined(ctx);
|
|
|
+ else
|
|
|
+ js_push_class_object_instance(ctx, this);
|
|
|
|
|
|
- subscribed_ = false;
|
|
|
- subscribedPostFixed_ = false;
|
|
|
+ duk_put_prop(ctx, -3);
|
|
|
|
|
|
- ClearScriptMethods();
|
|
|
+ duk_pop_2(ctx);
|
|
|
|
|
|
- scriptObject_ = 0;
|
|
|
- }
|
|
|
+ assert(duk_get_top(ctx) == top);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-void JSComponent::SetScriptFile(JSFile* scriptFile)
|
|
|
+void JSComponent::ApplyAttributes()
|
|
|
{
|
|
|
|
|
|
- ReleaseObject();
|
|
|
-
|
|
|
- CreateObject();
|
|
|
- MarkNetworkUpdate();
|
|
|
}
|
|
|
|
|
|
-void JSComponent::CreateObject()
|
|
|
+void JSComponent::InitModule()
|
|
|
{
|
|
|
- if (className_.Empty())
|
|
|
+ if (context_->GetEditorContext() || componentFile_.Null())
|
|
|
return;
|
|
|
|
|
|
- PROFILE(CreateScriptObject);
|
|
|
-
|
|
|
duk_context* ctx = vm_->GetJSContext();
|
|
|
|
|
|
- duk_push_global_stash(ctx);
|
|
|
- duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_INDEX_COMPONENTS);
|
|
|
- duk_get_prop_string(ctx, -1, className_.CString());
|
|
|
+ const String& path = componentFile_->GetName();
|
|
|
+ String pathName, fileName, ext;
|
|
|
+ SplitPath(path, pathName, fileName, ext);
|
|
|
|
|
|
- // FIXME: This is happening in editor
|
|
|
- if (!duk_is_function(ctx, -1))
|
|
|
- {
|
|
|
- duk_pop_n(ctx, 3);
|
|
|
- return;
|
|
|
- }
|
|
|
+ pathName += "/" + fileName;
|
|
|
+
|
|
|
+ duk_idx_t top = duk_get_top(ctx);
|
|
|
|
|
|
- js_push_class_object_instance(ctx, this);
|
|
|
+ duk_get_global_string(ctx, "require");
|
|
|
+ duk_push_string(ctx, pathName.CString());
|
|
|
|
|
|
if (duk_pcall(ctx, 1) != 0)
|
|
|
{
|
|
|
vm_->SendJSErrorEvent();
|
|
|
+ return;
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ if (!duk_is_object(ctx, -1))
|
|
|
{
|
|
|
- scriptObject_ = this->JSGetHeapPtr();
|
|
|
+ duk_set_top(ctx, top);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (scriptObject_)
|
|
|
+ duk_get_prop_string(ctx, -1, "component");
|
|
|
+ if (!duk_is_function(ctx, -1))
|
|
|
{
|
|
|
- GetScriptMethods();
|
|
|
- UpdateEventSubscription();
|
|
|
+ duk_set_top(ctx, top);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
+ // call with self
|
|
|
+ js_push_class_object_instance(ctx, this, "JSComponent");
|
|
|
+
|
|
|
+ if (duk_pcall(ctx, 1) != 0)
|
|
|
+ {
|
|
|
+ vm_->SendJSErrorEvent();
|
|
|
+ duk_set_top(ctx, top);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ UpdateReferences();
|
|
|
+
|
|
|
+ duk_set_top(ctx, top);
|
|
|
|
|
|
- duk_pop_n(ctx, 2);
|
|
|
}
|
|
|
|
|
|
-void JSComponent::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
|
|
|
+void JSComponent::CallScriptMethod(const String& name, bool passValue, float value)
|
|
|
{
|
|
|
- if (!scriptObject_)
|
|
|
- return;
|
|
|
+ void* heapptr = JSGetHeapPtr();
|
|
|
|
|
|
- assert(!destroyed_);
|
|
|
+ if (!heapptr)
|
|
|
+ return;
|
|
|
|
|
|
- assert(JSGetHeapPtr());
|
|
|
+ duk_context* ctx = vm_->GetJSContext();
|
|
|
|
|
|
- using namespace SceneUpdate;
|
|
|
+ duk_idx_t top = duk_get_top(ctx);
|
|
|
|
|
|
- float timeStep = eventData[P_TIMESTEP].GetFloat();
|
|
|
+ duk_push_heapptr(ctx, heapptr);
|
|
|
|
|
|
- duk_context* ctx = vm_->GetJSContext();
|
|
|
+ duk_get_prop_string(ctx, -1, name.CString());
|
|
|
|
|
|
- if (!started_)
|
|
|
+ if (!duk_is_function(ctx, -1))
|
|
|
{
|
|
|
- started_ = true;
|
|
|
+ duk_set_top(ctx, top);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (methods_[JSMETHOD_START])
|
|
|
- {
|
|
|
- duk_push_heapptr(ctx, methods_[JSMETHOD_START]);
|
|
|
- if (duk_pcall(ctx, 0) != 0)
|
|
|
- {
|
|
|
- vm_->SendJSErrorEvent();
|
|
|
- }
|
|
|
+ if (passValue)
|
|
|
+ duk_push_number(ctx, value);
|
|
|
|
|
|
- duk_pop(ctx);
|
|
|
- }
|
|
|
+ if (duk_pcall(ctx, passValue ? 1 : 0) != 0)
|
|
|
+ {
|
|
|
+ vm_->SendJSErrorEvent();
|
|
|
+ duk_set_top(ctx, top);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (methods_[JSMETHOD_UPDATE])
|
|
|
- {
|
|
|
- duk_push_heapptr(ctx, methods_[JSMETHOD_UPDATE]);
|
|
|
- duk_push_number(ctx, timeStep);
|
|
|
+ duk_set_top(ctx, top);
|
|
|
+}
|
|
|
|
|
|
- if ( duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS)
|
|
|
- {
|
|
|
- if (duk_is_object(ctx, -1))
|
|
|
- {
|
|
|
- vm_->SendJSErrorEvent();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- assert(0);
|
|
|
- }
|
|
|
- }
|
|
|
+void JSComponent::Start()
|
|
|
+{
|
|
|
+ static String name = "start";
|
|
|
+ CallScriptMethod(name);
|
|
|
+}
|
|
|
+
|
|
|
+void JSComponent::DelayedStart()
|
|
|
+{
|
|
|
+ static String name = "delayedStart";
|
|
|
+ CallScriptMethod(name);
|
|
|
+}
|
|
|
+
|
|
|
+void JSComponent::Update(float timeStep)
|
|
|
+{
|
|
|
+ static String name = "update";
|
|
|
+ CallScriptMethod(name, true, timeStep);
|
|
|
+}
|
|
|
+
|
|
|
+void JSComponent::PostUpdate(float timeStep)
|
|
|
+{
|
|
|
+ static String name = "postUpdate";
|
|
|
+ CallScriptMethod(name, true, timeStep);
|
|
|
+}
|
|
|
+
|
|
|
+void JSComponent::FixedUpdate(float timeStep)
|
|
|
+{
|
|
|
+ static String name = "fixedUpdate";
|
|
|
+ CallScriptMethod(name, true, timeStep);
|
|
|
+}
|
|
|
|
|
|
- duk_pop(ctx);
|
|
|
+void JSComponent::FixedPostUpdate(float timeStep)
|
|
|
+{
|
|
|
+ static String name = "fixedPostUpdate";
|
|
|
+ CallScriptMethod(name, true, timeStep);
|
|
|
+}
|
|
|
+
|
|
|
+void JSComponent::OnNodeSet(Node* node)
|
|
|
+{
|
|
|
+ if (node)
|
|
|
+ {
|
|
|
+ // We have been attached to a node. Set initial update event subscription state
|
|
|
+ UpdateEventSubscription();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- Scene* scene = GetScene();
|
|
|
- if (scene)
|
|
|
- UnsubscribeFromEvent(scene, E_SCENEUPDATE);
|
|
|
- subscribed_ = false;
|
|
|
+ // We are being detached from a node: execute user-defined stop function and prepare for destruction
|
|
|
+ UpdateReferences(true);
|
|
|
+ Stop();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void JSComponent::UpdateEventSubscription()
|
|
|
{
|
|
|
+ // If scene node is not assigned yet, no need to update subscription
|
|
|
+ if (!node_)
|
|
|
+ return;
|
|
|
+
|
|
|
Scene* scene = GetScene();
|
|
|
if (!scene)
|
|
|
{
|
|
|
- LOGWARNING("Node is detached from scene, can not subscribe script object to update events");
|
|
|
+ LOGWARNING("Node is detached from scene, can not subscribe to update events");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- bool enabled = scriptObject_ && IsEnabledEffective();
|
|
|
+ bool enabled = IsEnabledEffective();
|
|
|
|
|
|
- if (enabled)
|
|
|
+ bool needUpdate = enabled && ((updateEventMask_ & USE_UPDATE) || !delayedStartCalled_);
|
|
|
+ if (needUpdate && !(currentEventMask_ & USE_UPDATE))
|
|
|
{
|
|
|
- // we get at least one scene update if not started
|
|
|
- if (!subscribed_ && (!started_ || (methods_[JSMETHOD_UPDATE] || methods_[JSMETHOD_DELAYEDSTART] )))
|
|
|
- {
|
|
|
- SubscribeToEvent(scene, E_SCENEUPDATE, HANDLER(JSComponent, HandleSceneUpdate));
|
|
|
- subscribed_ = true;
|
|
|
- }
|
|
|
+ SubscribeToEvent(scene, E_SCENEUPDATE, HANDLER(JSComponent, HandleSceneUpdate));
|
|
|
+ currentEventMask_ |= USE_UPDATE;
|
|
|
+ }
|
|
|
+ else if (!needUpdate && (currentEventMask_ & USE_UPDATE))
|
|
|
+ {
|
|
|
+ UnsubscribeFromEvent(scene, E_SCENEUPDATE);
|
|
|
+ currentEventMask_ &= ~USE_UPDATE;
|
|
|
+ }
|
|
|
|
|
|
- if (!subscribedPostFixed_)
|
|
|
- {
|
|
|
- if (methods_[JSMETHOD_POSTUPDATE])
|
|
|
- SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(JSComponent, HandleScenePostUpdate));
|
|
|
+ bool needPostUpdate = enabled && (updateEventMask_ & USE_POSTUPDATE);
|
|
|
+ if (needPostUpdate && !(currentEventMask_ & USE_POSTUPDATE))
|
|
|
+ {
|
|
|
+ SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(JSComponent, HandleScenePostUpdate));
|
|
|
+ currentEventMask_ |= USE_POSTUPDATE;
|
|
|
+ }
|
|
|
+ else if (!needUpdate && (currentEventMask_ & USE_POSTUPDATE))
|
|
|
+ {
|
|
|
+ UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
|
|
|
+ currentEventMask_ &= ~USE_POSTUPDATE;
|
|
|
+ }
|
|
|
|
|
|
#ifdef ATOMIC_PHYSICS
|
|
|
- if (methods_[JSMETHOD_FIXEDUPDATE] || methods_[JSMETHOD_FIXEDPOSTUPDATE])
|
|
|
- {
|
|
|
- PhysicsWorld* world = scene->GetOrCreateComponent<PhysicsWorld>();
|
|
|
- if (world)
|
|
|
- {
|
|
|
- if (methods_[JSMETHOD_FIXEDUPDATE])
|
|
|
- SubscribeToEvent(world, E_PHYSICSPRESTEP, HANDLER(JSComponent, HandlePhysicsPreStep));
|
|
|
- if (methods_[JSMETHOD_FIXEDPOSTUPDATE])
|
|
|
- SubscribeToEvent(world, E_PHYSICSPOSTSTEP, HANDLER(JSComponent, HandlePhysicsPostStep));
|
|
|
- }
|
|
|
- else
|
|
|
- LOGERROR("No physics world, can not subscribe script object to fixed update events");
|
|
|
- }
|
|
|
-#endif
|
|
|
- subscribedPostFixed_ = true;
|
|
|
- }
|
|
|
+ PhysicsWorld* world = scene->GetComponent<PhysicsWorld>();
|
|
|
+ if (!world)
|
|
|
+ return;
|
|
|
|
|
|
- if (methods_[JSMETHOD_TRANSFORMCHANGED])
|
|
|
- node_->AddListener(this);
|
|
|
+ bool needFixedUpdate = enabled && (updateEventMask_ & USE_FIXEDUPDATE);
|
|
|
+ if (needFixedUpdate && !(currentEventMask_ & USE_FIXEDUPDATE))
|
|
|
+ {
|
|
|
+ SubscribeToEvent(world, E_PHYSICSPRESTEP, HANDLER(JSComponent, HandlePhysicsPreStep));
|
|
|
+ currentEventMask_ |= USE_FIXEDUPDATE;
|
|
|
}
|
|
|
- else
|
|
|
+ else if (!needFixedUpdate && (currentEventMask_ & USE_FIXEDUPDATE))
|
|
|
{
|
|
|
- if (subscribed_)
|
|
|
- {
|
|
|
- UnsubscribeFromEvent(scene, E_SCENEUPDATE);
|
|
|
- subscribed_ = false;
|
|
|
- }
|
|
|
+ UnsubscribeFromEvent(world, E_PHYSICSPRESTEP);
|
|
|
+ currentEventMask_ &= ~USE_FIXEDUPDATE;
|
|
|
+ }
|
|
|
|
|
|
- if (subscribedPostFixed_)
|
|
|
- {
|
|
|
- UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
|
|
|
-#ifdef ATOMIC_PHYSICS
|
|
|
- PhysicsWorld* world = scene->GetComponent<PhysicsWorld>();
|
|
|
- if (world)
|
|
|
- {
|
|
|
- UnsubscribeFromEvent(world, E_PHYSICSPRESTEP);
|
|
|
- UnsubscribeFromEvent(world, E_PHYSICSPOSTSTEP);
|
|
|
- }
|
|
|
+ bool needFixedPostUpdate = enabled && (updateEventMask_ & USE_FIXEDPOSTUPDATE);
|
|
|
+ if (needFixedPostUpdate && !(currentEventMask_ & USE_FIXEDPOSTUPDATE))
|
|
|
+ {
|
|
|
+ SubscribeToEvent(world, E_PHYSICSPOSTSTEP, HANDLER(JSComponent, HandlePhysicsPostStep));
|
|
|
+ currentEventMask_ |= USE_FIXEDPOSTUPDATE;
|
|
|
+ }
|
|
|
+ else if (!needFixedPostUpdate && (currentEventMask_ & USE_FIXEDPOSTUPDATE))
|
|
|
+ {
|
|
|
+ UnsubscribeFromEvent(world, E_PHYSICSPOSTSTEP);
|
|
|
+ currentEventMask_ &= ~USE_FIXEDPOSTUPDATE;
|
|
|
+ }
|
|
|
#endif
|
|
|
+}
|
|
|
|
|
|
- subscribedPostFixed_ = false;
|
|
|
- }
|
|
|
+void JSComponent::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
|
|
|
+{
|
|
|
+ using namespace SceneUpdate;
|
|
|
|
|
|
- if (methods_[JSMETHOD_TRANSFORMCHANGED])
|
|
|
- node_->RemoveListener(this);
|
|
|
+ if (!started_)
|
|
|
+ {
|
|
|
+ started_ = true;
|
|
|
+ InitModule();
|
|
|
+ Start();
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
+ // Execute user-defined delayed start function before first update
|
|
|
+ if (!delayedStartCalled_)
|
|
|
+ {
|
|
|
+ DelayedStart();
|
|
|
+ delayedStartCalled_ = true;
|
|
|
|
|
|
-void JSComponent::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
|
|
|
-{
|
|
|
- if (!scriptObject_)
|
|
|
- return;
|
|
|
+ // If did not need actual update events, unsubscribe now
|
|
|
+ if (!(updateEventMask_ & USE_UPDATE))
|
|
|
+ {
|
|
|
+ UnsubscribeFromEvent(GetScene(), E_SCENEUPDATE);
|
|
|
+ currentEventMask_ &= ~USE_UPDATE;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- assert(!destroyed_);
|
|
|
+ // Then execute user-defined update function
|
|
|
+ Update(eventData[P_TIMESTEP].GetFloat());
|
|
|
+}
|
|
|
|
|
|
+void JSComponent::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
|
|
|
+{
|
|
|
using namespace ScenePostUpdate;
|
|
|
|
|
|
- if (methods_[JSMETHOD_POSTUPDATE])
|
|
|
- {
|
|
|
- duk_context* ctx = vm_->GetJSContext();
|
|
|
- duk_push_heapptr(ctx, methods_[JSMETHOD_POSTUPDATE]);
|
|
|
- duk_push_number(ctx, eventData[P_TIMESTEP].GetFloat());
|
|
|
- duk_pcall(ctx, 1);
|
|
|
- duk_pop(ctx);
|
|
|
- }
|
|
|
+ // Execute user-defined post-update function
|
|
|
+ PostUpdate(eventData[P_TIMESTEP].GetFloat());
|
|
|
}
|
|
|
|
|
|
#ifdef ATOMIC_PHYSICS
|
|
|
void JSComponent::HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
|
|
|
{
|
|
|
- if (!scriptObject_)
|
|
|
- return;
|
|
|
-
|
|
|
- assert(!destroyed_);
|
|
|
-
|
|
|
using namespace PhysicsPreStep;
|
|
|
|
|
|
- float timeStep = eventData[P_TIMESTEP].GetFloat();
|
|
|
-
|
|
|
- if (methods_[JSMETHOD_FIXEDUPDATE])
|
|
|
- {
|
|
|
- duk_context* ctx = vm_->GetJSContext();
|
|
|
- duk_push_heapptr(ctx, methods_[JSMETHOD_FIXEDUPDATE]);
|
|
|
- duk_push_number(ctx, timeStep);
|
|
|
- duk_pcall(ctx, 1);
|
|
|
- duk_pop(ctx);
|
|
|
- }
|
|
|
+ // Execute user-defined fixed update function
|
|
|
+ FixedUpdate(eventData[P_TIMESTEP].GetFloat());
|
|
|
}
|
|
|
|
|
|
void JSComponent::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
|
|
|
{
|
|
|
- if (!scriptObject_)
|
|
|
- return;
|
|
|
-
|
|
|
- assert(!destroyed_);
|
|
|
-
|
|
|
using namespace PhysicsPostStep;
|
|
|
|
|
|
- VariantVector parameters;
|
|
|
- parameters.Push(eventData[P_TIMESTEP]);
|
|
|
+ // Execute user-defined fixed post-update function
|
|
|
+ FixedPostUpdate(eventData[P_TIMESTEP].GetFloat());
|
|
|
}
|
|
|
#endif
|
|
|
-void JSComponent::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
|
|
|
-{
|
|
|
- if (!IsEnabledEffective() || !scriptObject_)
|
|
|
- return;
|
|
|
-
|
|
|
- assert(!destroyed_);
|
|
|
-
|
|
|
- if (scriptEventFunctions_.Contains(eventType))
|
|
|
- {
|
|
|
-
|
|
|
- duk_context* ctx = vm_->GetJSContext();
|
|
|
- JS_HEAP_PTR function = scriptEventFunctions_[eventType];
|
|
|
-
|
|
|
- if (eventType == E_PHYSICSBEGINCONTACT2D || E_PHYSICSENDCONTACT2D)
|
|
|
- {
|
|
|
- using namespace PhysicsBeginContact2D;
|
|
|
- PhysicsWorld2D* world = static_cast<PhysicsWorld2D*>(eventData[P_WORLD].GetPtr());
|
|
|
- RigidBody2D* bodyA = static_cast<RigidBody2D*>(eventData[P_BODYA].GetPtr());
|
|
|
- RigidBody2D* bodyB = static_cast<RigidBody2D*>(eventData[P_BODYB].GetPtr());
|
|
|
- Node* nodeA = static_cast<Node*>(eventData[P_NODEA].GetPtr());
|
|
|
- Node* nodeB = static_cast<Node*>(eventData[P_NODEB].GetPtr());
|
|
|
-
|
|
|
- duk_push_heapptr(ctx, function);
|
|
|
- js_push_class_object_instance(ctx, world);
|
|
|
- js_push_class_object_instance(ctx, bodyA);
|
|
|
- js_push_class_object_instance(ctx, bodyB);
|
|
|
- js_push_class_object_instance(ctx, nodeA);
|
|
|
- js_push_class_object_instance(ctx, nodeB);
|
|
|
-
|
|
|
- if (duk_pcall(ctx, 5) != 0)
|
|
|
- {
|
|
|
- vm_->SendJSErrorEvent();
|
|
|
- }
|
|
|
-
|
|
|
- duk_pop(ctx);
|
|
|
-
|
|
|
- }
|
|
|
-#ifdef ATOMIC_PHYSICS
|
|
|
- else if (eventType == E_NODECOLLISION)
|
|
|
- {
|
|
|
- // Check collision contacts and see if character is standing on ground (look for a contact that has near vertical normal)
|
|
|
- using namespace NodeCollision;
|
|
|
- MemoryBuffer contacts(eventData[P_CONTACTS].GetBuffer());
|
|
|
-
|
|
|
- while (!contacts.IsEof())
|
|
|
- {
|
|
|
- Vector3 contactPosition = contacts.ReadVector3();
|
|
|
- Vector3 contactNormal = contacts.ReadVector3();
|
|
|
- float contactDistance = contacts.ReadFloat();
|
|
|
- float contactImpulse = contacts.ReadFloat();
|
|
|
-
|
|
|
- duk_push_heapptr(ctx, function);
|
|
|
-
|
|
|
- duk_push_array(ctx);
|
|
|
- duk_push_number(ctx, contactPosition.x_);
|
|
|
- duk_put_prop_index(ctx, -2, 0);
|
|
|
- duk_push_number(ctx, contactPosition.y_);
|
|
|
- duk_put_prop_index(ctx, -2, 1);
|
|
|
- duk_push_number(ctx, contactPosition.z_);
|
|
|
- duk_put_prop_index(ctx, -2, 2);
|
|
|
-
|
|
|
- duk_push_array(ctx);
|
|
|
- duk_push_number(ctx, contactNormal.x_);
|
|
|
- duk_put_prop_index(ctx, -2, 0);
|
|
|
- duk_push_number(ctx, contactNormal.y_);
|
|
|
- duk_put_prop_index(ctx, -2, 1);
|
|
|
- duk_push_number(ctx, contactNormal.z_);
|
|
|
- duk_put_prop_index(ctx, -2, 2);
|
|
|
-
|
|
|
- duk_call(ctx, 2);
|
|
|
- duk_pop(ctx);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-#endif
|
|
|
- else
|
|
|
- {
|
|
|
- duk_push_heapptr(ctx, function);
|
|
|
- if (duk_pcall(ctx, 0) != 0)
|
|
|
- {
|
|
|
- vm_->SendJSErrorEvent();
|
|
|
- }
|
|
|
|
|
|
- duk_pop(ctx);
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
+bool JSComponent::Load(Deserializer& source, bool setInstanceDefault)
|
|
|
+{
|
|
|
+ loading_ = true;
|
|
|
+ bool success = Component::Load(source, setInstanceDefault);
|
|
|
+ loading_ = false;
|
|
|
|
|
|
+ return success;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-void JSComponent::GetScriptMethods()
|
|
|
+bool JSComponent::LoadXML(const XMLElement& source, bool setInstanceDefault)
|
|
|
{
|
|
|
- if (!scriptObject_)
|
|
|
- return;
|
|
|
-
|
|
|
- duk_context* ctx = vm_->GetJSContext();
|
|
|
- duk_push_heapptr(ctx, scriptObject_);
|
|
|
-
|
|
|
- for (unsigned i = 0; i < MAX_JSSCRIPT_METHODS; ++i)
|
|
|
- {
|
|
|
- duk_get_prop_string(ctx, -1, methodDeclarations[i]);
|
|
|
- if (duk_is_function(ctx, -1))
|
|
|
- {
|
|
|
- methods_[i] = duk_get_heapptr(ctx, -1);
|
|
|
- }
|
|
|
-
|
|
|
- duk_pop(ctx);
|
|
|
- }
|
|
|
-
|
|
|
- duk_pop(ctx);
|
|
|
+ loading_ = true;
|
|
|
+ bool success = Component::LoadXML(source, setInstanceDefault);
|
|
|
+ loading_ = false;
|
|
|
|
|
|
+ return success;
|
|
|
}
|
|
|
|
|
|
+void JSComponent::SetComponentFile(JSComponentFile* cfile, bool loading)
|
|
|
+{
|
|
|
+ componentFile_ = cfile;
|
|
|
+}
|
|
|
|
|
|
+ResourceRef JSComponent::GetScriptAttr() const
|
|
|
+{
|
|
|
+ return GetResourceRef(componentFile_, JSComponentFile::GetTypeStatic());
|
|
|
+}
|
|
|
|
|
|
+void JSComponent::SetScriptAttr(const ResourceRef& value)
|
|
|
+{
|
|
|
+ ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
|
+ SetComponentFile(cache->GetResource<JSComponentFile>(value.name_), loading_);
|
|
|
+}
|
|
|
|
|
|
}
|