JSComponent.cpp 15 KB

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