ScriptInstance.cpp 33 KB

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