ScriptInstance.cpp 34 KB

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