| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- // 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>
- #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 <Atomic/UI/UIElement.h>
- #include "Javascript.h"
- #include "JSEvents.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),
- started_(false),
- destroyed_(false)
- {
- vm_ = JSVM::GetJSVM(NULL);
- ClearScriptMethods();
- }
- JSComponent::~JSComponent()
- {
- }
- void JSComponent::OnNodeSet(Node *node)
- {
- Component::OnNodeSet(node);
- if (node)
- {
- assert(node->JSGetHeapPtr());
- duk_context* ctx = vm_->GetJSContext();
- 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, node->JSGetHeapPtr());
- js_push_class_object_instance(ctx, node);
- duk_put_prop(ctx, -3);
- duk_pop_2(ctx);
- }
- }
- void JSComponent::RegisterObject(Context* context)
- {
- context->RegisterFactory<JSComponent>(LOGIC_CATEGORY);
- //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
- //REF_ACCESSOR_ATTRIBUTE(JSComponent, VAR_STRING, "Class Name", GetClassName, SetClassName, String, String::EMPTY, AM_DEFAULT);
- //ACCESSOR_ATTRIBUTE(JSComponent, VAR_RESOURCEREF, "Script File", GetScriptFileAttr, SetScriptFileAttr, ResourceRef, ResourceRef(JSFile::GetTypeStatic()), AM_DEFAULT);
- //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BUFFER, "Delayed Method Calls", GetDelayedCallsAttr, SetDelayedCallsAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
- //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BUFFER, "Script Data", GetScriptDataAttr, SetScriptDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
- //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BUFFER, "Script Network Data", GetScriptNetworkDataAttr, SetScriptNetworkDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_NET | AM_NOEDIT);
- }
- void JSComponent::ClearScriptMethods()
- {
- for (unsigned i = 0; i < MAX_JSSCRIPT_METHODS; ++i)
- methods_[i] = 0;
- //delayedCalls_.Clear();
- }
- void JSComponent::OnSetEnabled()
- {
- UpdateEventSubscription();
- }
- void JSComponent::ListenToEvent(Object* sender, StringHash eventType, JS_HEAP_PTR __duk_function)
- {
- 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;
- }
- void JSComponent::SetClassName(const String& className)
- {
- if (className == className_ && scriptObject_)
- return;
- ReleaseObject();
- className_ = className;
- CreateObject();
- MarkNetworkUpdate();
- }
- void JSComponent::ReleaseObject()
- {
- if (scriptObject_)
- {
- //if (methods_[JSMETHOD_STOP])
- // scriptFile_->Execute(scriptObject_, methods_[JSMETHOD_STOP]);
- PODVector<StringHash> exceptions;
- exceptions.Push(E_RELOADSTARTED);
- exceptions.Push(E_RELOADFINISHED);
- UnsubscribeFromAllEventsExcept(exceptions, false);
- if (node_)
- node_->RemoveListener(this);
- subscribed_ = false;
- subscribedPostFixed_ = false;
- ClearScriptMethods();
- scriptObject_ = 0;
- }
- }
- void JSComponent::SetScriptFile(JSFile* scriptFile)
- {
- ReleaseObject();
- CreateObject();
- MarkNetworkUpdate();
- }
- void JSComponent::CreateObject()
- {
- if (className_.Empty())
- 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());
- assert(duk_is_function(ctx, -1));
- js_push_class_object_instance(ctx, this);
- if (duk_pcall(ctx, 1) != 0)
- {
- vm_->SendJSErrorEvent();
- }
- else
- {
- scriptObject_ = this->JSGetHeapPtr();
- }
- if (scriptObject_)
- {
- GetScriptMethods();
- UpdateEventSubscription();
- }
- duk_pop_n(ctx, 2);
- }
- void JSComponent::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
- {
- if (!scriptObject_)
- return;
- assert(!destroyed_);
- assert(JSGetHeapPtr());
- using namespace SceneUpdate;
- float timeStep = eventData[P_TIMESTEP].GetFloat();
- duk_context* ctx = vm_->GetJSContext();
- if (!started_)
- {
- started_ = true;
- if (methods_[JSMETHOD_START])
- {
- duk_push_heapptr(ctx, methods_[JSMETHOD_START]);
- if (duk_pcall(ctx, 0) != 0)
- {
- vm_->SendJSErrorEvent();
- }
- duk_pop(ctx);
- }
- }
- if (methods_[JSMETHOD_UPDATE])
- {
- duk_push_heapptr(ctx, methods_[JSMETHOD_UPDATE]);
- duk_push_number(ctx, timeStep);
- if ( duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS)
- {
- if (duk_is_object(ctx, -1))
- {
- vm_->SendJSErrorEvent();
- }
- else
- {
- assert(0);
- }
- }
- duk_pop(ctx);
- }
- else
- {
- Scene* scene = GetScene();
- if (scene)
- UnsubscribeFromEvent(scene, E_SCENEUPDATE);
- subscribed_ = false;
- }
- }
- void JSComponent::UpdateEventSubscription()
- {
- Scene* scene = GetScene();
- if (!scene)
- {
- LOGWARNING("Node is detached from scene, can not subscribe script object to update events");
- return;
- }
- bool enabled = scriptObject_ && IsEnabledEffective();
- if (enabled)
- {
- // 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;
- }
- if (!subscribedPostFixed_)
- {
- if (methods_[JSMETHOD_POSTUPDATE])
- SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(JSComponent, HandleScenePostUpdate));
- #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;
- }
- if (methods_[JSMETHOD_TRANSFORMCHANGED])
- node_->AddListener(this);
- }
- else
- {
- if (subscribed_)
- {
- UnsubscribeFromEvent(scene, E_SCENEUPDATE);
- subscribed_ = false;
- }
- if (subscribedPostFixed_)
- {
- UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
- #ifdef ATOMIC_PHYSICS
- PhysicsWorld* world = scene->GetComponent<PhysicsWorld>();
- if (world)
- {
- UnsubscribeFromEvent(world, E_PHYSICSPRESTEP);
- UnsubscribeFromEvent(world, E_PHYSICSPOSTSTEP);
- }
- #endif
- subscribedPostFixed_ = false;
- }
- if (methods_[JSMETHOD_TRANSFORMCHANGED])
- node_->RemoveListener(this);
- }
- }
- void JSComponent::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
- {
- if (!scriptObject_)
- return;
- assert(!destroyed_);
- 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);
- }
- }
- #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);
- }
- }
- void JSComponent::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
- {
- if (!scriptObject_)
- return;
- assert(!destroyed_);
- using namespace PhysicsPostStep;
- VariantVector parameters;
- parameters.Push(eventData[P_TIMESTEP]);
- }
- #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_UIMOUSECLICK)
- {
- UIElement* clicked = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());
- if (clicked)
- {
- duk_push_heapptr(ctx, function);
- js_push_class_object_instance(ctx, clicked);
- if (duk_pcall(ctx, 1) != 0)
- {
- vm_->SendJSErrorEvent();
- }
- duk_pop(ctx);
- }
- }
- else 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);
- }
- 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);
- }
- }
- else
- {
- duk_push_heapptr(ctx, function);
- if (duk_pcall(ctx, 0) != 0)
- {
- vm_->SendJSErrorEvent();
- }
- duk_pop(ctx);
- }
- }
- }
- void JSComponent::GetScriptMethods()
- {
- 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);
- }
- }
|