JSComponent.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. #include "Precompiled.h"
  2. #include "../Core/Context.h"
  3. #include "../IO/Log.h"
  4. #ifdef ATOMIC_PHYSICS
  5. #include "../Physics/PhysicsEvents.h"
  6. #include "../Physics/PhysicsWorld.h"
  7. #endif
  8. #include "../Core/Profiler.h"
  9. #include "../IO/MemoryBuffer.h"
  10. #include "../Resource/ResourceCache.h"
  11. #include "../Resource/ResourceEvents.h"
  12. #include "../Scene/Scene.h"
  13. #include "../Scene/SceneEvents.h"
  14. #include "../Javascript/Javascript.h"
  15. #include "../Atomic2D/PhysicsEvents2D.h"
  16. #include "../Atomic2D/PhysicsWorld2D.h"
  17. #include "../Atomic2D/RigidBody2D.h"
  18. #include "../Javascript/JSEvents.h"
  19. #include "../Javascript/JSComponent.h"
  20. #include "../Javascript/JSAPI.h"
  21. #include "../UI/UIEvents.h"
  22. #include "../UI/UIElement.h"
  23. namespace Atomic
  24. {
  25. static const char* methodDeclarations[] = {
  26. "start",
  27. "stop",
  28. "delayedStart",
  29. "update",
  30. "postUpdate",
  31. "fixedUpdate",
  32. "fixedPostUpdate",
  33. "load",
  34. "save",
  35. "readNetworkUpdate",
  36. "writeNetworkUpdate",
  37. "applyAttributes",
  38. "transformChanged"
  39. };
  40. extern const char* LOGIC_CATEGORY;
  41. JSComponent::JSComponent(Context* context) :
  42. Component(context),
  43. script_(GetSubsystem<Javascript>()),
  44. scriptObject_(0),
  45. subscribed_(false),
  46. subscribedPostFixed_(false),
  47. started_(false),
  48. destroyed_(false)
  49. {
  50. vm_ = JSVM::GetJSVM(NULL);
  51. ClearScriptMethods();
  52. }
  53. JSComponent::~JSComponent()
  54. {
  55. }
  56. void JSComponent::OnNodeSet(Node *node)
  57. {
  58. Component::OnNodeSet(node);
  59. if (node)
  60. {
  61. assert(node->JSGetHeapPtr());
  62. duk_context* ctx = vm_->GetJSContext();
  63. duk_push_global_stash(ctx);
  64. duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_INDEX_NODE_REGISTRY);
  65. // can't use instance as key, as this coerces to [Object] for
  66. // string property, pointer will be string representation of
  67. // address, so, unique key
  68. duk_push_pointer(ctx, node->JSGetHeapPtr());
  69. js_push_class_object_instance(ctx, node);
  70. duk_put_prop(ctx, -3);
  71. duk_pop_2(ctx);
  72. }
  73. }
  74. void JSComponent::RegisterObject(Context* context)
  75. {
  76. context->RegisterFactory<JSComponent>(LOGIC_CATEGORY);
  77. //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
  78. //REF_ACCESSOR_ATTRIBUTE(JSComponent, VAR_STRING, "Class Name", GetClassName, SetClassName, String, String::EMPTY, AM_DEFAULT);
  79. //ACCESSOR_ATTRIBUTE(JSComponent, VAR_RESOURCEREF, "Script File", GetScriptFileAttr, SetScriptFileAttr, ResourceRef, ResourceRef(JSFile::GetTypeStatic()), AM_DEFAULT);
  80. //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BUFFER, "Delayed Method Calls", GetDelayedCallsAttr, SetDelayedCallsAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
  81. //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BUFFER, "Script Data", GetScriptDataAttr, SetScriptDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
  82. //ACCESSOR_ATTRIBUTE(JSComponent, VAR_BUFFER, "Script Network Data", GetScriptNetworkDataAttr, SetScriptNetworkDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_NET | AM_NOEDIT);
  83. }
  84. void JSComponent::ClearScriptMethods()
  85. {
  86. for (unsigned i = 0; i < MAX_JSSCRIPT_METHODS; ++i)
  87. methods_[i] = 0;
  88. //delayedCalls_.Clear();
  89. }
  90. void JSComponent::OnSetEnabled()
  91. {
  92. UpdateEventSubscription();
  93. }
  94. void JSComponent::ListenToEvent(Object* sender, StringHash eventType, JS_HEAP_PTR __duk_function)
  95. {
  96. duk_context* ctx = vm_->GetJSContext();
  97. duk_push_heapptr(ctx, __duk_function);
  98. assert(duk_is_function(ctx, -1));
  99. duk_pop(ctx);
  100. scriptEventFunctions_[eventType] = __duk_function;
  101. if (sender)
  102. SubscribeToEvent(sender, eventType, HANDLER(JSComponent, HandleScriptEvent));
  103. else
  104. SubscribeToEvent(eventType, HANDLER(JSComponent, HandleScriptEvent));
  105. }
  106. bool JSComponent::CreateObject(JSFile* scriptFile, const String& className)
  107. {
  108. className_ = String::EMPTY; // Do not create object during SetScriptFile()
  109. SetScriptFile(scriptFile);
  110. SetClassName(className);
  111. return scriptObject_ != 0;
  112. }
  113. void JSComponent::SetClassName(const String& className)
  114. {
  115. if (className == className_ && scriptObject_)
  116. return;
  117. ReleaseObject();
  118. className_ = className;
  119. CreateObject();
  120. MarkNetworkUpdate();
  121. }
  122. void JSComponent::ReleaseObject()
  123. {
  124. if (scriptObject_)
  125. {
  126. //if (methods_[JSMETHOD_STOP])
  127. // scriptFile_->Execute(scriptObject_, methods_[JSMETHOD_STOP]);
  128. PODVector<StringHash> exceptions;
  129. exceptions.Push(E_RELOADSTARTED);
  130. exceptions.Push(E_RELOADFINISHED);
  131. UnsubscribeFromAllEventsExcept(exceptions, false);
  132. if (node_)
  133. node_->RemoveListener(this);
  134. subscribed_ = false;
  135. subscribedPostFixed_ = false;
  136. ClearScriptMethods();
  137. scriptObject_ = 0;
  138. }
  139. }
  140. void JSComponent::SetScriptFile(JSFile* scriptFile)
  141. {
  142. ReleaseObject();
  143. CreateObject();
  144. MarkNetworkUpdate();
  145. }
  146. void JSComponent::CreateObject()
  147. {
  148. if (className_.Empty())
  149. return;
  150. PROFILE(CreateScriptObject);
  151. duk_context* ctx = vm_->GetJSContext();
  152. duk_push_global_stash(ctx);
  153. duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_INDEX_COMPONENTS);
  154. duk_get_prop_string(ctx, -1, className_.CString());
  155. assert(duk_is_function(ctx, -1));
  156. js_push_class_object_instance(ctx, this);
  157. if (duk_pcall(ctx, 1) != 0)
  158. {
  159. vm_->SendJSErrorEvent();
  160. }
  161. else
  162. {
  163. scriptObject_ = this->JSGetHeapPtr();
  164. }
  165. if (scriptObject_)
  166. {
  167. GetScriptMethods();
  168. UpdateEventSubscription();
  169. }
  170. duk_pop_n(ctx, 2);
  171. }
  172. void JSComponent::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
  173. {
  174. if (!scriptObject_)
  175. return;
  176. assert(!destroyed_);
  177. assert(JSGetHeapPtr());
  178. using namespace SceneUpdate;
  179. float timeStep = eventData[P_TIMESTEP].GetFloat();
  180. duk_context* ctx = vm_->GetJSContext();
  181. if (!started_)
  182. {
  183. started_ = true;
  184. if (methods_[JSMETHOD_START])
  185. {
  186. duk_push_heapptr(ctx, methods_[JSMETHOD_START]);
  187. if (duk_pcall(ctx, 0) != 0)
  188. {
  189. vm_->SendJSErrorEvent();
  190. }
  191. duk_pop(ctx);
  192. }
  193. }
  194. if (methods_[JSMETHOD_UPDATE])
  195. {
  196. duk_push_heapptr(ctx, methods_[JSMETHOD_UPDATE]);
  197. duk_push_number(ctx, timeStep);
  198. if ( duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS)
  199. {
  200. if (duk_is_object(ctx, -1))
  201. {
  202. vm_->SendJSErrorEvent();
  203. }
  204. else
  205. {
  206. assert(0);
  207. }
  208. }
  209. duk_pop(ctx);
  210. }
  211. }
  212. void JSComponent::UpdateEventSubscription()
  213. {
  214. Scene* scene = GetScene();
  215. if (!scene)
  216. {
  217. LOGWARNING("Node is detached from scene, can not subscribe script object to update events");
  218. return;
  219. }
  220. bool enabled = scriptObject_ && IsEnabledEffective();
  221. if (enabled)
  222. {
  223. if (!subscribed_ && (methods_[JSMETHOD_UPDATE] || methods_[JSMETHOD_DELAYEDSTART] ))
  224. {
  225. SubscribeToEvent(scene, E_SCENEUPDATE, HANDLER(JSComponent, HandleSceneUpdate));
  226. subscribed_ = true;
  227. }
  228. if (!subscribedPostFixed_)
  229. {
  230. if (methods_[JSMETHOD_POSTUPDATE])
  231. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(JSComponent, HandleScenePostUpdate));
  232. #ifdef ATOMIC_PHYSICS
  233. if (methods_[JSMETHOD_FIXEDUPDATE] || methods_[JSMETHOD_FIXEDPOSTUPDATE])
  234. {
  235. PhysicsWorld* world = scene->GetOrCreateComponent<PhysicsWorld>();
  236. if (world)
  237. {
  238. if (methods_[JSMETHOD_FIXEDUPDATE])
  239. SubscribeToEvent(world, E_PHYSICSPRESTEP, HANDLER(JSComponent, HandlePhysicsPreStep));
  240. if (methods_[JSMETHOD_FIXEDPOSTUPDATE])
  241. SubscribeToEvent(world, E_PHYSICSPOSTSTEP, HANDLER(JSComponent, HandlePhysicsPostStep));
  242. }
  243. else
  244. LOGERROR("No physics world, can not subscribe script object to fixed update events");
  245. }
  246. #endif
  247. subscribedPostFixed_ = true;
  248. }
  249. if (methods_[JSMETHOD_TRANSFORMCHANGED])
  250. node_->AddListener(this);
  251. }
  252. else
  253. {
  254. if (subscribed_)
  255. {
  256. UnsubscribeFromEvent(scene, E_SCENEUPDATE);
  257. subscribed_ = false;
  258. }
  259. if (subscribedPostFixed_)
  260. {
  261. UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
  262. #ifdef ATOMIC_PHYSICS
  263. PhysicsWorld* world = scene->GetComponent<PhysicsWorld>();
  264. if (world)
  265. {
  266. UnsubscribeFromEvent(world, E_PHYSICSPRESTEP);
  267. UnsubscribeFromEvent(world, E_PHYSICSPOSTSTEP);
  268. }
  269. #endif
  270. subscribedPostFixed_ = false;
  271. }
  272. if (methods_[JSMETHOD_TRANSFORMCHANGED])
  273. node_->RemoveListener(this);
  274. }
  275. }
  276. void JSComponent::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
  277. {
  278. if (!scriptObject_)
  279. return;
  280. assert(!destroyed_);
  281. using namespace ScenePostUpdate;
  282. if (methods_[JSMETHOD_POSTUPDATE])
  283. {
  284. duk_context* ctx = vm_->GetJSContext();
  285. duk_push_heapptr(ctx, methods_[JSMETHOD_POSTUPDATE]);
  286. duk_push_number(ctx, eventData[P_TIMESTEP].GetFloat());
  287. duk_pcall(ctx, 1);
  288. duk_pop(ctx);
  289. }
  290. }
  291. #ifdef ATOMIC_PHYSICS
  292. void JSComponent::HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
  293. {
  294. if (!scriptObject_)
  295. return;
  296. assert(!destroyed_);
  297. using namespace PhysicsPreStep;
  298. float timeStep = eventData[P_TIMESTEP].GetFloat();
  299. if (methods_[JSMETHOD_FIXEDUPDATE])
  300. {
  301. duk_context* ctx = vm_->GetJSContext();
  302. duk_push_heapptr(ctx, methods_[JSMETHOD_FIXEDUPDATE]);
  303. duk_push_number(ctx, timeStep);
  304. duk_pcall(ctx, 1);
  305. duk_pop(ctx);
  306. }
  307. }
  308. void JSComponent::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
  309. {
  310. if (!scriptObject_)
  311. return;
  312. assert(!destroyed_);
  313. using namespace PhysicsPostStep;
  314. VariantVector parameters;
  315. parameters.Push(eventData[P_TIMESTEP]);
  316. }
  317. #endif
  318. void JSComponent::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
  319. {
  320. if (!IsEnabledEffective() || !scriptObject_)
  321. return;
  322. assert(!destroyed_);
  323. if (scriptEventFunctions_.Contains(eventType))
  324. {
  325. duk_context* ctx = vm_->GetJSContext();
  326. JS_HEAP_PTR function = scriptEventFunctions_[eventType];
  327. if (eventType == E_UIMOUSECLICK)
  328. {
  329. UIElement* clicked = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());
  330. if (clicked)
  331. {
  332. duk_push_heapptr(ctx, function);
  333. js_push_class_object_instance(ctx, clicked);
  334. if (duk_pcall(ctx, 1) != 0)
  335. {
  336. vm_->SendJSErrorEvent();
  337. }
  338. duk_pop(ctx);
  339. }
  340. }
  341. else if (eventType == E_PHYSICSBEGINCONTACT2D || E_PHYSICSENDCONTACT2D)
  342. {
  343. using namespace PhysicsBeginContact2D;
  344. PhysicsWorld2D* world = static_cast<PhysicsWorld2D*>(eventData[P_WORLD].GetPtr());
  345. RigidBody2D* bodyA = static_cast<RigidBody2D*>(eventData[P_BODYA].GetPtr());
  346. RigidBody2D* bodyB = static_cast<RigidBody2D*>(eventData[P_BODYB].GetPtr());
  347. Node* nodeA = static_cast<Node*>(eventData[P_NODEA].GetPtr());
  348. Node* nodeB = static_cast<Node*>(eventData[P_NODEB].GetPtr());
  349. duk_push_heapptr(ctx, function);
  350. js_push_class_object_instance(ctx, world);
  351. js_push_class_object_instance(ctx, bodyA);
  352. js_push_class_object_instance(ctx, bodyB);
  353. js_push_class_object_instance(ctx, nodeA);
  354. js_push_class_object_instance(ctx, nodeB);
  355. if (duk_pcall(ctx, 5) != 0)
  356. {
  357. vm_->SendJSErrorEvent();
  358. }
  359. duk_pop(ctx);
  360. }
  361. else if (eventType == E_NODECOLLISION)
  362. {
  363. // Check collision contacts and see if character is standing on ground (look for a contact that has near vertical normal)
  364. using namespace NodeCollision;
  365. MemoryBuffer contacts(eventData[P_CONTACTS].GetBuffer());
  366. while (!contacts.IsEof())
  367. {
  368. Vector3 contactPosition = contacts.ReadVector3();
  369. Vector3 contactNormal = contacts.ReadVector3();
  370. float contactDistance = contacts.ReadFloat();
  371. float contactImpulse = contacts.ReadFloat();
  372. duk_push_heapptr(ctx, function);
  373. duk_push_array(ctx);
  374. duk_push_number(ctx, contactPosition.x_);
  375. duk_put_prop_index(ctx, -2, 0);
  376. duk_push_number(ctx, contactPosition.y_);
  377. duk_put_prop_index(ctx, -2, 1);
  378. duk_push_number(ctx, contactPosition.z_);
  379. duk_put_prop_index(ctx, -2, 2);
  380. duk_push_array(ctx);
  381. duk_push_number(ctx, contactNormal.x_);
  382. duk_put_prop_index(ctx, -2, 0);
  383. duk_push_number(ctx, contactNormal.y_);
  384. duk_put_prop_index(ctx, -2, 1);
  385. duk_push_number(ctx, contactNormal.z_);
  386. duk_put_prop_index(ctx, -2, 2);
  387. duk_call(ctx, 2);
  388. duk_pop(ctx);
  389. }
  390. }
  391. }
  392. }
  393. void JSComponent::GetScriptMethods()
  394. {
  395. if (!scriptObject_)
  396. return;
  397. duk_context* ctx = vm_->GetJSContext();
  398. duk_push_heapptr(ctx, scriptObject_);
  399. for (unsigned i = 0; i < MAX_JSSCRIPT_METHODS; ++i)
  400. {
  401. duk_get_prop_string(ctx, -1, methodDeclarations[i]);
  402. if (duk_is_function(ctx, -1))
  403. {
  404. methods_[i] = duk_get_heapptr(ctx, -1);
  405. }
  406. duk_pop(ctx);
  407. }
  408. duk_pop(ctx);
  409. }
  410. }