ScriptInstance.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. //
  2. // Copyright (c) 2008-2017 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../AngelScript/Script.h"
  24. #include "../AngelScript/ScriptFile.h"
  25. #include "../AngelScript/ScriptInstance.h"
  26. #include "../AngelScript/Addons.h"
  27. #include "../AngelScript/APITemplates.h"
  28. #include "../Core/Context.h"
  29. #include "../Core/Profiler.h"
  30. #include "../IO/Log.h"
  31. #include "../IO/MemoryBuffer.h"
  32. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  33. #include "../Physics/PhysicsEvents.h"
  34. #endif
  35. #include "../Resource/ResourceCache.h"
  36. #include "../Resource/ResourceEvents.h"
  37. #include "../Scene/Scene.h"
  38. #include "../Scene/SceneEvents.h"
  39. #include <AngelScript/angelscript.h>
  40. #include "../DebugNew.h"
  41. namespace Urho3D
  42. {
  43. static const char* methodDeclarations[] = {
  44. "void Start()",
  45. "void Stop()",
  46. "void DelayedStart()",
  47. "void Update(float)",
  48. "void PostUpdate(float)",
  49. "void FixedUpdate(float)",
  50. "void FixedPostUpdate(float)",
  51. "void Load(Deserializer&)",
  52. "void Save(Serializer&)",
  53. "void ReadNetworkUpdate(Deserializer&)",
  54. "void WriteNetworkUpdate(Serializer&)",
  55. "void ApplyAttributes()",
  56. "void TransformChanged()"
  57. };
  58. ScriptInstance::ScriptInstance(Context* context) :
  59. Component(context),
  60. scriptObject_(nullptr),
  61. subscribed_(false),
  62. subscribedPostFixed_(false)
  63. {
  64. ClearScriptMethods();
  65. ClearScriptAttributes();
  66. }
  67. ScriptInstance::~ScriptInstance()
  68. {
  69. ReleaseObject();
  70. }
  71. void ScriptInstance::RegisterObject(Context* context)
  72. {
  73. context->RegisterFactory<ScriptInstance>(LOGIC_CATEGORY);
  74. URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
  75. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Delayed Method Calls", GetDelayedCallsAttr, SetDelayedCallsAttr, PODVector<unsigned char>,
  76. Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
  77. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Script File", GetScriptFileAttr, SetScriptFileAttr, ResourceRef,
  78. ResourceRef(ScriptFile::GetTypeStatic()), AM_DEFAULT);
  79. URHO3D_ACCESSOR_ATTRIBUTE("Class Name", GetClassName, SetClassName, String, String::EMPTY, AM_DEFAULT);
  80. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Script Data", GetScriptDataAttr, SetScriptDataAttr, PODVector<unsigned char>, Variant::emptyBuffer,
  81. AM_FILE | AM_NOEDIT);
  82. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Script Network Data", GetScriptNetworkDataAttr, SetScriptNetworkDataAttr, PODVector<unsigned char>,
  83. Variant::emptyBuffer, AM_NET | AM_NOEDIT);
  84. }
  85. void ScriptInstance::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
  86. {
  87. // Update stored script attributes if being manipulated while the actual object doesn't exist
  88. if (!scriptObject_ && storedAttributes_.Size())
  89. {
  90. HashMap<String, Variant>::Iterator i = storedAttributes_.Find(attr.name_);
  91. if (i != storedAttributes_.End())
  92. i->second_ = src;
  93. }
  94. if (attr.mode_ & (AM_NODEID | AM_COMPONENTID))
  95. {
  96. // The component / node to which the ID refers to may not be in the scene yet, and furthermore the ID must go through the
  97. // SceneResolver first. Delay searching for the object to ApplyAttributes
  98. AttributeInfo* attrPtr = const_cast<AttributeInfo*>(&attr);
  99. idAttributes_[attrPtr] = src.GetUInt();
  100. }
  101. else if (attr.type_ == VAR_RESOURCEREF && attr.ptr_)
  102. {
  103. Resource*& resourcePtr = *(reinterpret_cast<Resource**>(attr.ptr_));
  104. // Decrease reference count of the old object if any, then increment the new
  105. if (resourcePtr)
  106. resourcePtr->ReleaseRef();
  107. const ResourceRef& ref = src.GetResourceRef();
  108. resourcePtr = GetSubsystem<ResourceCache>()->GetResource(ref.type_, ref.name_);
  109. if (resourcePtr)
  110. resourcePtr->AddRef();
  111. }
  112. else if (attr.type_ == VAR_VARIANTVECTOR && attr.ptr_)
  113. {
  114. CScriptArray* arr = reinterpret_cast<CScriptArray*>(attr.ptr_);
  115. if (arr)
  116. {
  117. const Vector<Variant>& vector = src.GetVariantVector();
  118. unsigned size = vector.Size();
  119. arr->Resize(size);
  120. for (unsigned i = 0; i < size; i++)
  121. *(static_cast<Variant*>(arr->At(i))) = vector[i];
  122. }
  123. }
  124. else if (attr.type_ == VAR_STRINGVECTOR && attr.ptr_)
  125. {
  126. CScriptArray* arr = reinterpret_cast<CScriptArray*>(attr.ptr_);
  127. if (arr)
  128. {
  129. const Vector<String>& vector = src.GetStringVector();
  130. unsigned size = vector.Size();
  131. arr->Resize(size);
  132. for (unsigned i = 0; i < size; i++)
  133. *(static_cast<String*>(arr->At(i))) = vector[i];
  134. }
  135. }
  136. else
  137. Serializable::OnSetAttribute(attr, src);
  138. }
  139. void ScriptInstance::OnGetAttribute(const AttributeInfo& attr, Variant& dest) const
  140. {
  141. AttributeInfo* attrPtr = const_cast<AttributeInfo*>(&attr);
  142. // Get ID's for node / component handle attributes
  143. if (attr.mode_ & (AM_NODEID | AM_COMPONENTID))
  144. {
  145. // If a cached ID value has been stored, return it instead of querying from the actual object
  146. // (the object handle is likely null at that point)
  147. HashMap<AttributeInfo*, unsigned>::ConstIterator i = idAttributes_.Find(attrPtr);
  148. if (i != idAttributes_.End())
  149. dest = i->second_;
  150. else if (attr.mode_ & AM_NODEID)
  151. {
  152. Node* node = *(reinterpret_cast<Node**>(attr.ptr_));
  153. unsigned nodeID = node ? node->GetID() : 0;
  154. dest = nodeID;
  155. }
  156. else
  157. {
  158. Component* component = *(reinterpret_cast<Component**>(attr.ptr_));
  159. unsigned componentID = component ? component->GetID() : 0;
  160. dest = componentID;
  161. }
  162. }
  163. else if (attr.type_ == VAR_RESOURCEREF && attr.ptr_)
  164. {
  165. Resource* resource = *(reinterpret_cast<Resource**>(attr.ptr_));
  166. // If resource is non-null get its type and name hash. Otherwise get type from the default value
  167. dest = GetResourceRef(resource, attr.defaultValue_.GetResourceRef().type_);
  168. }
  169. else if (attr.type_ == VAR_VARIANTVECTOR && attr.ptr_)
  170. {
  171. CScriptArray* arr = reinterpret_cast<CScriptArray*>(attr.ptr_);
  172. if (arr)
  173. dest = ArrayToVector<Variant>(arr);
  174. }
  175. else if (attr.type_ == VAR_STRINGVECTOR && attr.ptr_)
  176. {
  177. CScriptArray* arr = reinterpret_cast<CScriptArray*>(attr.ptr_);
  178. if (arr)
  179. dest = ArrayToVector<String>(arr);
  180. } else
  181. Serializable::OnGetAttribute(attr, dest);
  182. }
  183. void ScriptInstance::ApplyAttributes()
  184. {
  185. // Apply node / component ID attributes now (find objects from the scene and assign to the object handles)
  186. for (HashMap<AttributeInfo*, unsigned>::Iterator i = idAttributes_.Begin(); i != idAttributes_.End(); ++i)
  187. {
  188. AttributeInfo& attr = *i->first_;
  189. if (attr.mode_ & AM_NODEID)
  190. {
  191. Node*& nodePtr = *(reinterpret_cast<Node**>(attr.ptr_));
  192. // Decrease reference count of the old object if any, then increment the new
  193. if (nodePtr)
  194. nodePtr->ReleaseRef();
  195. nodePtr = GetScene()->GetNode(i->second_);
  196. if (nodePtr)
  197. nodePtr->AddRef();
  198. }
  199. else if (attr.mode_ & AM_COMPONENTID)
  200. {
  201. Component*& componentPtr = *(reinterpret_cast<Component**>(attr.ptr_));
  202. if (componentPtr)
  203. componentPtr->ReleaseRef();
  204. componentPtr = GetScene()->GetComponent(i->second_);
  205. if (componentPtr)
  206. componentPtr->AddRef();
  207. }
  208. }
  209. idAttributes_.Clear();
  210. if (scriptObject_ && methods_[METHOD_APPLYATTRIBUTES])
  211. scriptFile_->Execute(scriptObject_, methods_[METHOD_APPLYATTRIBUTES]);
  212. }
  213. void ScriptInstance::OnSetEnabled()
  214. {
  215. UpdateEventSubscription();
  216. }
  217. bool ScriptInstance::CreateObject(ScriptFile* scriptFile, const String& className)
  218. {
  219. className_ = String::EMPTY; // Do not create object during SetScriptFile()
  220. SetScriptFile(scriptFile);
  221. SetClassName(className);
  222. return scriptObject_ != nullptr;
  223. }
  224. void ScriptInstance::SetScriptFile(ScriptFile* scriptFile)
  225. {
  226. if (scriptFile == scriptFile_ && scriptObject_)
  227. return;
  228. ReleaseObject();
  229. // Unsubscribe from the reload event of previous script file (if any), then subscribe to the new
  230. if (scriptFile_)
  231. {
  232. UnsubscribeFromEvent(scriptFile_, E_RELOADSTARTED);
  233. UnsubscribeFromEvent(scriptFile_, E_RELOADFINISHED);
  234. }
  235. if (scriptFile)
  236. {
  237. SubscribeToEvent(scriptFile, E_RELOADSTARTED, URHO3D_HANDLER(ScriptInstance, HandleScriptFileReload));
  238. SubscribeToEvent(scriptFile, E_RELOADFINISHED, URHO3D_HANDLER(ScriptInstance, HandleScriptFileReloadFinished));
  239. }
  240. scriptFile_ = scriptFile;
  241. CreateObject();
  242. MarkNetworkUpdate();
  243. }
  244. void ScriptInstance::SetClassName(const String& className)
  245. {
  246. if (className == className_ && scriptObject_)
  247. return;
  248. ReleaseObject();
  249. className_ = className;
  250. CreateObject();
  251. MarkNetworkUpdate();
  252. }
  253. bool ScriptInstance::Execute(const String& declaration, const VariantVector& parameters)
  254. {
  255. if (!scriptObject_)
  256. return false;
  257. asIScriptFunction* method = scriptFile_->GetMethod(scriptObject_, declaration);
  258. if (!method)
  259. {
  260. URHO3D_LOGERROR("Method " + declaration + " not found in class " + className_);
  261. return false;
  262. }
  263. return scriptFile_->Execute(scriptObject_, method, parameters);
  264. }
  265. bool ScriptInstance::Execute(asIScriptFunction* method, const VariantVector& parameters)
  266. {
  267. if (!method || !scriptObject_)
  268. return false;
  269. return scriptFile_->Execute(scriptObject_, method, parameters);
  270. }
  271. void ScriptInstance::DelayedExecute(float delay, bool repeat, const String& declaration, const VariantVector& parameters)
  272. {
  273. if (!scriptObject_)
  274. return;
  275. DelayedCall call;
  276. call.period_ = call.delay_ = Max(delay, 0.0f);
  277. call.repeat_ = repeat;
  278. call.declaration_ = declaration;
  279. call.parameters_ = parameters;
  280. delayedCalls_.Push(call);
  281. // Make sure we are registered to the scene update event, because delayed calls are executed there
  282. if (!subscribed_)
  283. UpdateEventSubscription();
  284. }
  285. void ScriptInstance::ClearDelayedExecute(const String& declaration)
  286. {
  287. if (declaration.Empty())
  288. delayedCalls_.Clear();
  289. else
  290. {
  291. for (Vector<DelayedCall>::Iterator i = delayedCalls_.Begin(); i != delayedCalls_.End();)
  292. {
  293. if (declaration == i->declaration_)
  294. i = delayedCalls_.Erase(i);
  295. else
  296. ++i;
  297. }
  298. }
  299. }
  300. void ScriptInstance::AddEventHandler(StringHash eventType, const String& handlerName)
  301. {
  302. if (!scriptObject_)
  303. return;
  304. String declaration = "void " + handlerName + "(StringHash, VariantMap&)";
  305. asIScriptFunction* method = scriptFile_->GetMethod(scriptObject_, declaration);
  306. if (!method)
  307. {
  308. // Retry with parameterless signature
  309. method = scriptFile_->GetMethod(scriptObject_, handlerName);
  310. if (!method)
  311. {
  312. URHO3D_LOGERROR("Event handler method " + handlerName + " not found in " + scriptFile_->GetName());
  313. return;
  314. }
  315. }
  316. SubscribeToEvent(eventType, URHO3D_HANDLER_USERDATA(ScriptInstance, HandleScriptEvent, (void*)method));
  317. }
  318. void ScriptInstance::AddEventHandler(Object* sender, StringHash eventType, const String& handlerName)
  319. {
  320. if (!scriptObject_)
  321. return;
  322. if (!sender)
  323. {
  324. URHO3D_LOGERROR("Null event sender for event " + String(eventType) + ", handler " + handlerName);
  325. return;
  326. }
  327. String declaration = "void " + handlerName + "(StringHash, VariantMap&)";
  328. asIScriptFunction* method = scriptFile_->GetMethod(scriptObject_, declaration);
  329. if (!method)
  330. {
  331. // Retry with parameterless signature
  332. method = scriptFile_->GetMethod(scriptObject_, handlerName);
  333. if (!method)
  334. {
  335. URHO3D_LOGERROR("Event handler method " + handlerName + " not found in " + scriptFile_->GetName());
  336. return;
  337. }
  338. }
  339. SubscribeToEvent(sender, eventType, URHO3D_HANDLER_USERDATA(ScriptInstance, HandleScriptEvent, (void*)method));
  340. }
  341. void ScriptInstance::RemoveEventHandler(StringHash eventType)
  342. {
  343. UnsubscribeFromEvent(eventType);
  344. }
  345. void ScriptInstance::RemoveEventHandler(Object* sender, StringHash eventType)
  346. {
  347. UnsubscribeFromEvent(sender, eventType);
  348. }
  349. void ScriptInstance::RemoveEventHandlers(Object* sender)
  350. {
  351. UnsubscribeFromEvents(sender);
  352. }
  353. void ScriptInstance::RemoveEventHandlers()
  354. {
  355. UnsubscribeFromAllEventsExcept(PODVector<StringHash>(), true);
  356. }
  357. void ScriptInstance::RemoveEventHandlersExcept(const PODVector<StringHash>& exceptions)
  358. {
  359. UnsubscribeFromAllEventsExcept(exceptions, true);
  360. }
  361. bool ScriptInstance::HasEventHandler(StringHash eventType) const
  362. {
  363. return HasSubscribedToEvent(eventType);
  364. }
  365. bool ScriptInstance::HasEventHandler(Object* sender, StringHash eventType) const
  366. {
  367. return HasSubscribedToEvent(sender, eventType);
  368. }
  369. bool ScriptInstance::IsA(const String& className) const
  370. {
  371. // Early out for the easiest case where that's what we are
  372. if (className_ == className)
  373. return true;
  374. if (scriptObject_)
  375. {
  376. asITypeInfo* myType = scriptObject_->GetObjectType();
  377. asITypeInfo* searchType = myType->GetModule()->GetTypeInfoByName(className.CString());
  378. return searchType && (searchType->GetTypeId() & asTYPEID_MASK_OBJECT) != 0 &&
  379. (myType->DerivesFrom(searchType) || myType->Implements(searchType));
  380. }
  381. return false;
  382. }
  383. bool ScriptInstance::HasMethod(const String& declaration) const
  384. {
  385. if (!scriptFile_ || !scriptObject_)
  386. return false;
  387. else
  388. return scriptFile_->GetMethod(scriptObject_, declaration) != nullptr;
  389. }
  390. void ScriptInstance::SetScriptFileAttr(const ResourceRef& value)
  391. {
  392. ResourceCache* cache = GetSubsystem<ResourceCache>();
  393. SetScriptFile(cache->GetResource<ScriptFile>(value.name_));
  394. }
  395. void ScriptInstance::SetDelayedCallsAttr(const PODVector<unsigned char>& value)
  396. {
  397. MemoryBuffer buf(value);
  398. delayedCalls_.Resize(buf.ReadVLE());
  399. for (Vector<DelayedCall>::Iterator i = delayedCalls_.Begin(); i != delayedCalls_.End(); ++i)
  400. {
  401. i->period_ = buf.ReadFloat();
  402. i->delay_ = buf.ReadFloat();
  403. i->repeat_ = buf.ReadBool();
  404. i->declaration_ = buf.ReadString();
  405. i->parameters_ = buf.ReadVariantVector();
  406. }
  407. if (scriptObject_ && delayedCalls_.Size() && !subscribed_)
  408. UpdateEventSubscription();
  409. }
  410. void ScriptInstance::SetScriptDataAttr(const PODVector<unsigned char>& data)
  411. {
  412. if (scriptObject_ && methods_[METHOD_LOAD])
  413. {
  414. MemoryBuffer buf(data);
  415. VariantVector parameters;
  416. parameters.Push(Variant((void*)static_cast<Deserializer*>(&buf)));
  417. scriptFile_->Execute(scriptObject_, methods_[METHOD_LOAD], parameters);
  418. }
  419. }
  420. void ScriptInstance::SetScriptNetworkDataAttr(const PODVector<unsigned char>& data)
  421. {
  422. if (scriptObject_ && methods_[METHOD_READNETWORKUPDATE])
  423. {
  424. MemoryBuffer buf(data);
  425. VariantVector parameters;
  426. parameters.Push(Variant((void*)static_cast<Deserializer*>(&buf)));
  427. scriptFile_->Execute(scriptObject_, methods_[METHOD_READNETWORKUPDATE], parameters);
  428. }
  429. }
  430. ResourceRef ScriptInstance::GetScriptFileAttr() const
  431. {
  432. return GetResourceRef(scriptFile_, ScriptFile::GetTypeStatic());
  433. }
  434. PODVector<unsigned char> ScriptInstance::GetDelayedCallsAttr() const
  435. {
  436. VectorBuffer buf;
  437. buf.WriteVLE(delayedCalls_.Size());
  438. for (Vector<DelayedCall>::ConstIterator i = delayedCalls_.Begin(); i != delayedCalls_.End(); ++i)
  439. {
  440. buf.WriteFloat(i->period_);
  441. buf.WriteFloat(i->delay_);
  442. buf.WriteBool(i->repeat_);
  443. buf.WriteString(i->declaration_);
  444. buf.WriteVariantVector(i->parameters_);
  445. }
  446. return buf.GetBuffer();
  447. }
  448. PODVector<unsigned char> ScriptInstance::GetScriptDataAttr() const
  449. {
  450. if (!scriptObject_ || !methods_[METHOD_SAVE])
  451. return PODVector<unsigned char>();
  452. else
  453. {
  454. VectorBuffer buf;
  455. VariantVector parameters;
  456. parameters.Push(Variant((void*)static_cast<Serializer*>(&buf)));
  457. scriptFile_->Execute(scriptObject_, methods_[METHOD_SAVE], parameters);
  458. return buf.GetBuffer();
  459. }
  460. }
  461. PODVector<unsigned char> ScriptInstance::GetScriptNetworkDataAttr() const
  462. {
  463. if (!scriptObject_ || !methods_[METHOD_WRITENETWORKUPDATE])
  464. return PODVector<unsigned char>();
  465. else
  466. {
  467. VectorBuffer buf;
  468. VariantVector parameters;
  469. parameters.Push(Variant((void*)static_cast<Serializer*>(&buf)));
  470. scriptFile_->Execute(scriptObject_, methods_[METHOD_WRITENETWORKUPDATE], parameters);
  471. return buf.GetBuffer();
  472. }
  473. }
  474. void ScriptInstance::OnSceneSet(Scene* scene)
  475. {
  476. if (scene)
  477. UpdateEventSubscription();
  478. else
  479. {
  480. UnsubscribeFromEvent(E_SCENEUPDATE);
  481. UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
  482. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  483. UnsubscribeFromEvent(E_PHYSICSPRESTEP);
  484. UnsubscribeFromEvent(E_PHYSICSPOSTSTEP);
  485. #endif
  486. subscribed_ = false;
  487. subscribedPostFixed_ = false;
  488. }
  489. }
  490. void ScriptInstance::OnMarkedDirty(Node* node)
  491. {
  492. // Script functions are not safe from worker threads
  493. Scene* scene = GetScene();
  494. if (scene && scene->IsThreadedUpdate())
  495. {
  496. scene->DelayedMarkedDirty(this);
  497. return;
  498. }
  499. if (scriptObject_ && methods_[METHOD_TRANSFORMCHANGED])
  500. scriptFile_->Execute(scriptObject_, methods_[METHOD_TRANSFORMCHANGED]);
  501. }
  502. void ScriptInstance::CreateObject()
  503. {
  504. if (!scriptFile_ || className_.Empty())
  505. return;
  506. URHO3D_PROFILE(CreateScriptObject);
  507. scriptObject_ = scriptFile_->CreateObject(className_);
  508. if (scriptObject_)
  509. {
  510. // Map script object to script instance with userdata
  511. scriptObject_->SetUserData(this);
  512. GetScriptMethods();
  513. GetScriptAttributes();
  514. UpdateEventSubscription();
  515. if (methods_[METHOD_START])
  516. scriptFile_->Execute(scriptObject_, methods_[METHOD_START]);
  517. }
  518. else
  519. URHO3D_LOGERROR("Failed to create object of class " + className_ + " from " + scriptFile_->GetName());
  520. }
  521. void ScriptInstance::ReleaseObject()
  522. {
  523. if (scriptObject_)
  524. {
  525. if (methods_[METHOD_STOP])
  526. scriptFile_->Execute(scriptObject_, methods_[METHOD_STOP]);
  527. PODVector<StringHash> exceptions;
  528. exceptions.Push(E_RELOADSTARTED);
  529. exceptions.Push(E_RELOADFINISHED);
  530. UnsubscribeFromAllEventsExcept(exceptions, false);
  531. if (node_)
  532. node_->RemoveListener(this);
  533. subscribed_ = false;
  534. subscribedPostFixed_ = false;
  535. ClearScriptMethods();
  536. ClearScriptAttributes();
  537. scriptObject_->SetUserData(nullptr);
  538. scriptObject_->Release();
  539. scriptObject_ = nullptr;
  540. }
  541. }
  542. void ScriptInstance::ClearScriptMethods()
  543. {
  544. for (unsigned i = 0; i < MAX_SCRIPT_METHODS; ++i)
  545. methods_[i] = nullptr;
  546. delayedCalls_.Clear();
  547. }
  548. void ScriptInstance::ClearScriptAttributes()
  549. {
  550. attributeInfos_ = *context_->GetAttributes(GetTypeStatic());
  551. idAttributes_.Clear();
  552. }
  553. void ScriptInstance::GetScriptMethods()
  554. {
  555. for (unsigned i = 0; i < MAX_SCRIPT_METHODS; ++i)
  556. methods_[i] = scriptFile_->GetMethod(scriptObject_, methodDeclarations[i]);
  557. }
  558. void ScriptInstance::GetScriptAttributes()
  559. {
  560. asIScriptEngine* engine = GetSubsystem<Script>()->GetScriptEngine();
  561. attributeInfos_ = *context_->GetAttributes(GetTypeStatic());
  562. unsigned numProperties = scriptObject_->GetPropertyCount();
  563. for (unsigned i = 0; i < numProperties; ++i)
  564. {
  565. const char* name = nullptr;
  566. int typeId = 0; // AngelScript void typeid
  567. bool isPrivate=false, isProtected=false, isHandle=false, isEnum=false;
  568. scriptObject_->GetObjectType()->GetProperty(i, &name, &typeId, &isPrivate, &isProtected);
  569. // Hide private variables or ones that begin with an underscore
  570. if (isPrivate || isProtected || name[0] == '_')
  571. continue;
  572. String typeName = engine->GetTypeDeclaration(typeId);
  573. isHandle = typeName.EndsWith("@");
  574. if (isHandle)
  575. typeName = typeName.Substring(0, typeName.Length() - 1);
  576. if (engine->GetTypeInfoById(typeId))
  577. isEnum = engine->GetTypeInfoById(typeId)->GetFlags() & asOBJ_ENUM;
  578. AttributeInfo info;
  579. info.mode_ = AM_FILE;
  580. info.name_ = name;
  581. info.ptr_ = scriptObject_->GetAddressOfProperty(i);
  582. if (isEnum)
  583. {
  584. info.type_ = VAR_INT;
  585. info.enumNames_ = GetSubsystem<Script>()->GetEnumValues(typeId);
  586. }
  587. else if (!isHandle)
  588. {
  589. switch (typeId)
  590. {
  591. case asTYPEID_BOOL:
  592. info.type_ = VAR_BOOL;
  593. break;
  594. case asTYPEID_INT32:
  595. case asTYPEID_UINT32:
  596. info.type_ = VAR_INT;
  597. break;
  598. case asTYPEID_FLOAT:
  599. info.type_ = VAR_FLOAT;
  600. break;
  601. default:
  602. if (typeName == "Variant[]")
  603. info.type_ = VAR_VARIANTVECTOR;
  604. else if (typeName == "String[]")
  605. info.type_ = VAR_STRINGVECTOR;
  606. else
  607. info.type_ = Variant::GetTypeFromName(typeName);
  608. break;
  609. }
  610. }
  611. else
  612. {
  613. // For a handle type, check if it's an Object subclass with a registered factory
  614. StringHash typeHash(typeName);
  615. const HashMap<StringHash, SharedPtr<ObjectFactory> >& factories = context_->GetObjectFactories();
  616. HashMap<StringHash, SharedPtr<ObjectFactory> >::ConstIterator j = factories.Find(typeHash);
  617. if (j != factories.End())
  618. {
  619. // Check base class type. Node & Component are supported as ID attributes, Resource as a resource reference
  620. const TypeInfo* typeInfo = j->second_->GetTypeInfo();
  621. if (typeInfo->IsTypeOf<Node>())
  622. {
  623. info.mode_ |= AM_NODEID;
  624. info.type_ = VAR_INT;
  625. }
  626. else if (typeInfo->IsTypeOf<Component>())
  627. {
  628. info.mode_ |= AM_COMPONENTID;
  629. info.type_ = VAR_INT;
  630. }
  631. else if (typeInfo->IsTypeOf<Resource>())
  632. {
  633. info.type_ = VAR_RESOURCEREF;
  634. info.defaultValue_ = ResourceRef(typeHash);
  635. }
  636. }
  637. }
  638. if (info.type_ != VAR_NONE)
  639. attributeInfos_.Push(info);
  640. }
  641. }
  642. void ScriptInstance::StoreScriptAttributes()
  643. {
  644. if (!scriptObject_)
  645. return;
  646. storedAttributes_.Clear();
  647. for (unsigned i = 0; i < attributeInfos_.Size(); ++i)
  648. {
  649. const AttributeInfo& attr = attributeInfos_[i];
  650. if (attr.ptr_)
  651. storedAttributes_[attr.name_] = GetAttribute(i);
  652. }
  653. }
  654. void ScriptInstance::RestoreScriptAttributes()
  655. {
  656. if (!scriptObject_)
  657. return;
  658. for (unsigned i = 0; i < attributeInfos_.Size(); ++i)
  659. {
  660. const AttributeInfo& attr = attributeInfos_[i];
  661. if (attr.ptr_)
  662. {
  663. HashMap<String, Variant>::ConstIterator j = storedAttributes_.Find(attr.name_);
  664. if (j != storedAttributes_.End())
  665. SetAttribute(i, j->second_);
  666. }
  667. }
  668. // Clear after restoring once. If the attributes are no longer in use (script code has changed) they are forgotten now
  669. storedAttributes_.Clear();
  670. }
  671. void ScriptInstance::UpdateEventSubscription()
  672. {
  673. Scene* scene = GetScene();
  674. if (!scene)
  675. {
  676. URHO3D_LOGWARNING("Node is detached from scene, can not subscribe script object to update events");
  677. return;
  678. }
  679. bool enabled = scriptObject_ && IsEnabledEffective();
  680. if (enabled)
  681. {
  682. if (!subscribed_ && (methods_[METHOD_UPDATE] || methods_[METHOD_DELAYEDSTART] || delayedCalls_.Size()))
  683. {
  684. SubscribeToEvent(scene, E_SCENEUPDATE, URHO3D_HANDLER(ScriptInstance, HandleSceneUpdate));
  685. subscribed_ = true;
  686. }
  687. if (!subscribedPostFixed_)
  688. {
  689. if (methods_[METHOD_POSTUPDATE])
  690. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(ScriptInstance, HandleScenePostUpdate));
  691. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  692. if (methods_[METHOD_FIXEDUPDATE] || methods_[METHOD_FIXEDPOSTUPDATE])
  693. {
  694. Component* world = GetFixedUpdateSource();
  695. if (world)
  696. {
  697. if (methods_[METHOD_FIXEDUPDATE])
  698. SubscribeToEvent(world, E_PHYSICSPRESTEP, URHO3D_HANDLER(ScriptInstance, HandlePhysicsPreStep));
  699. if (methods_[METHOD_FIXEDPOSTUPDATE])
  700. SubscribeToEvent(world, E_PHYSICSPOSTSTEP, URHO3D_HANDLER(ScriptInstance, HandlePhysicsPostStep));
  701. }
  702. else
  703. URHO3D_LOGERROR("No physics world, can not subscribe script object to fixed update events");
  704. }
  705. #endif
  706. subscribedPostFixed_ = true;
  707. }
  708. if (methods_[METHOD_TRANSFORMCHANGED])
  709. node_->AddListener(this);
  710. }
  711. else
  712. {
  713. if (subscribed_)
  714. {
  715. UnsubscribeFromEvent(scene, E_SCENEUPDATE);
  716. subscribed_ = false;
  717. }
  718. if (subscribedPostFixed_)
  719. {
  720. UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
  721. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  722. Component* world = GetFixedUpdateSource();
  723. if (world)
  724. {
  725. UnsubscribeFromEvent(world, E_PHYSICSPRESTEP);
  726. UnsubscribeFromEvent(world, E_PHYSICSPOSTSTEP);
  727. }
  728. #endif
  729. subscribedPostFixed_ = false;
  730. }
  731. if (methods_[METHOD_TRANSFORMCHANGED])
  732. node_->RemoveListener(this);
  733. }
  734. }
  735. void ScriptInstance::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
  736. {
  737. if (!scriptObject_)
  738. return;
  739. using namespace SceneUpdate;
  740. float timeStep = eventData[P_TIMESTEP].GetFloat();
  741. // Execute delayed calls
  742. for (unsigned i = 0; i < delayedCalls_.Size();)
  743. {
  744. DelayedCall& call = delayedCalls_[i];
  745. bool remove = false;
  746. call.delay_ -= timeStep;
  747. if (call.delay_ <= 0.0f)
  748. {
  749. if (!call.repeat_)
  750. remove = true;
  751. else
  752. call.delay_ += call.period_;
  753. Execute(call.declaration_, call.parameters_);
  754. }
  755. if (remove)
  756. delayedCalls_.Erase(i);
  757. else
  758. ++i;
  759. }
  760. // Execute delayed start before first update
  761. if (methods_[METHOD_DELAYEDSTART])
  762. {
  763. scriptFile_->Execute(scriptObject_, methods_[METHOD_DELAYEDSTART]);
  764. methods_[METHOD_DELAYEDSTART] = nullptr; // Only execute once
  765. }
  766. if (methods_[METHOD_UPDATE])
  767. {
  768. VariantVector parameters;
  769. parameters.Push(timeStep);
  770. scriptFile_->Execute(scriptObject_, methods_[METHOD_UPDATE], parameters);
  771. }
  772. }
  773. void ScriptInstance::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
  774. {
  775. if (!scriptObject_)
  776. return;
  777. using namespace ScenePostUpdate;
  778. VariantVector parameters;
  779. parameters.Push(eventData[P_TIMESTEP]);
  780. scriptFile_->Execute(scriptObject_, methods_[METHOD_POSTUPDATE], parameters);
  781. }
  782. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  783. void ScriptInstance::HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
  784. {
  785. if (!scriptObject_)
  786. return;
  787. // Execute delayed start before first fixed update if not called yet
  788. if (methods_[METHOD_DELAYEDSTART])
  789. {
  790. scriptFile_->Execute(scriptObject_, methods_[METHOD_DELAYEDSTART]);
  791. methods_[METHOD_DELAYEDSTART] = nullptr; // Only execute once
  792. }
  793. using namespace PhysicsPreStep;
  794. VariantVector parameters;
  795. parameters.Push(eventData[P_TIMESTEP]);
  796. scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDUPDATE], parameters);
  797. }
  798. void ScriptInstance::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
  799. {
  800. if (!scriptObject_)
  801. return;
  802. using namespace PhysicsPostStep;
  803. VariantVector parameters;
  804. parameters.Push(eventData[P_TIMESTEP]);
  805. scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDPOSTUPDATE], parameters);
  806. }
  807. #endif
  808. void ScriptInstance::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
  809. {
  810. if (!IsEnabledEffective() || !scriptFile_ || !scriptObject_)
  811. return;
  812. asIScriptFunction* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
  813. VariantVector parameters;
  814. if (method->GetParamCount() > 0)
  815. {
  816. parameters.Push(Variant((void*)&eventType));
  817. parameters.Push(Variant((void*)&eventData));
  818. }
  819. scriptFile_->Execute(scriptObject_, method, parameters);
  820. }
  821. void ScriptInstance::HandleScriptFileReload(StringHash eventType, VariantMap& eventData)
  822. {
  823. StoreScriptAttributes();
  824. ReleaseObject();
  825. }
  826. void ScriptInstance::HandleScriptFileReloadFinished(StringHash eventType, VariantMap& eventData)
  827. {
  828. if (!className_.Empty())
  829. {
  830. CreateObject();
  831. RestoreScriptAttributes();
  832. }
  833. }
  834. asIScriptContext* GetActiveASContext()
  835. {
  836. return asGetActiveContext();
  837. }
  838. Context* GetScriptContext()
  839. {
  840. asIScriptContext* context = asGetActiveContext();
  841. if (context)
  842. return static_cast<Script*>(context->GetEngine()->GetUserData())->GetContext();
  843. else
  844. return nullptr;
  845. }
  846. ScriptInstance* GetScriptContextInstance()
  847. {
  848. asIScriptContext* context = asGetActiveContext();
  849. asIScriptObject* object = context ? static_cast<asIScriptObject*>(context->GetThisPointer()) : nullptr;
  850. if (object)
  851. return static_cast<ScriptInstance*>(object->GetUserData());
  852. else
  853. return nullptr;
  854. }
  855. Node* GetScriptContextNode()
  856. {
  857. ScriptInstance* instance = GetScriptContextInstance();
  858. return instance ? instance->GetNode() : nullptr;
  859. }
  860. Scene* GetScriptContextScene()
  861. {
  862. Scene* scene = nullptr;
  863. Node* node = GetScriptContextNode();
  864. if (node)
  865. scene = node->GetScene();
  866. // If null, try to get the default scene
  867. if (!scene)
  868. scene = GetScriptContext()->GetSubsystem<Script>()->GetDefaultScene();
  869. return scene;
  870. }
  871. ScriptEventListener* GetScriptContextEventListener()
  872. {
  873. // If the context has an object and that object has user data set, try and get the ScriptInstance, otherwise try and get a ScriptFile.
  874. asIScriptContext* context = asGetActiveContext();
  875. if (context)
  876. {
  877. asIScriptObject* object = static_cast<asIScriptObject*>(context->GetThisPointer());
  878. if (object && object->GetUserData())
  879. return GetScriptContextInstance();
  880. else
  881. return GetScriptContextFile();
  882. }
  883. else
  884. return nullptr;
  885. }
  886. Object* GetScriptContextEventListenerObject()
  887. {
  888. return dynamic_cast<Object*>(GetScriptContextEventListener());
  889. }
  890. }