ScriptInstance.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  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. auto* 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. auto* 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. auto* 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;
  566. int typeId;
  567. bool isPrivate, isProtected, isHandle;
  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. AttributeInfo info;
  577. info.mode_ = AM_FILE;
  578. info.name_ = name;
  579. info.ptr_ = scriptObject_->GetAddressOfProperty(i);
  580. if (!isHandle)
  581. {
  582. switch (typeId)
  583. {
  584. case asTYPEID_BOOL:
  585. info.type_ = VAR_BOOL;
  586. break;
  587. case asTYPEID_INT32:
  588. case asTYPEID_UINT32:
  589. info.type_ = VAR_INT;
  590. break;
  591. case asTYPEID_FLOAT:
  592. info.type_ = VAR_FLOAT;
  593. break;
  594. default:
  595. if (typeName == "Variant[]")
  596. info.type_ = VAR_VARIANTVECTOR;
  597. else if (typeName == "String[]")
  598. info.type_ = VAR_STRINGVECTOR;
  599. else
  600. info.type_ = Variant::GetTypeFromName(typeName);
  601. break;
  602. }
  603. }
  604. else
  605. {
  606. // For a handle type, check if it's an Object subclass with a registered factory
  607. StringHash typeHash(typeName);
  608. const HashMap<StringHash, SharedPtr<ObjectFactory> >& factories = context_->GetObjectFactories();
  609. HashMap<StringHash, SharedPtr<ObjectFactory> >::ConstIterator j = factories.Find(typeHash);
  610. if (j != factories.End())
  611. {
  612. // Check base class type. Node & Component are supported as ID attributes, Resource as a resource reference
  613. const TypeInfo* typeInfo = j->second_->GetTypeInfo();
  614. if (typeInfo->IsTypeOf<Node>())
  615. {
  616. info.mode_ |= AM_NODEID;
  617. info.type_ = VAR_INT;
  618. }
  619. else if (typeInfo->IsTypeOf<Component>())
  620. {
  621. info.mode_ |= AM_COMPONENTID;
  622. info.type_ = VAR_INT;
  623. }
  624. else if (typeInfo->IsTypeOf<Resource>())
  625. {
  626. info.type_ = VAR_RESOURCEREF;
  627. info.defaultValue_ = ResourceRef(typeHash);
  628. }
  629. }
  630. }
  631. if (info.type_ != VAR_NONE)
  632. attributeInfos_.Push(info);
  633. }
  634. }
  635. void ScriptInstance::StoreScriptAttributes()
  636. {
  637. if (!scriptObject_)
  638. return;
  639. storedAttributes_.Clear();
  640. for (unsigned i = 0; i < attributeInfos_.Size(); ++i)
  641. {
  642. const AttributeInfo& attr = attributeInfos_[i];
  643. if (attr.ptr_)
  644. storedAttributes_[attr.name_] = GetAttribute(i);
  645. }
  646. }
  647. void ScriptInstance::RestoreScriptAttributes()
  648. {
  649. if (!scriptObject_)
  650. return;
  651. for (unsigned i = 0; i < attributeInfos_.Size(); ++i)
  652. {
  653. const AttributeInfo& attr = attributeInfos_[i];
  654. if (attr.ptr_)
  655. {
  656. HashMap<String, Variant>::ConstIterator j = storedAttributes_.Find(attr.name_);
  657. if (j != storedAttributes_.End())
  658. SetAttribute(i, j->second_);
  659. }
  660. }
  661. // Clear after restoring once. If the attributes are no longer in use (script code has changed) they are forgotten now
  662. storedAttributes_.Clear();
  663. }
  664. void ScriptInstance::UpdateEventSubscription()
  665. {
  666. Scene* scene = GetScene();
  667. if (!scene)
  668. {
  669. URHO3D_LOGWARNING("Node is detached from scene, can not subscribe script object to update events");
  670. return;
  671. }
  672. bool enabled = scriptObject_ && IsEnabledEffective();
  673. if (enabled)
  674. {
  675. if (!subscribed_ && (methods_[METHOD_UPDATE] || methods_[METHOD_DELAYEDSTART] || delayedCalls_.Size()))
  676. {
  677. SubscribeToEvent(scene, E_SCENEUPDATE, URHO3D_HANDLER(ScriptInstance, HandleSceneUpdate));
  678. subscribed_ = true;
  679. }
  680. if (!subscribedPostFixed_)
  681. {
  682. if (methods_[METHOD_POSTUPDATE])
  683. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(ScriptInstance, HandleScenePostUpdate));
  684. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  685. if (methods_[METHOD_FIXEDUPDATE] || methods_[METHOD_FIXEDPOSTUPDATE])
  686. {
  687. Component* world = GetFixedUpdateSource();
  688. if (world)
  689. {
  690. if (methods_[METHOD_FIXEDUPDATE])
  691. SubscribeToEvent(world, E_PHYSICSPRESTEP, URHO3D_HANDLER(ScriptInstance, HandlePhysicsPreStep));
  692. if (methods_[METHOD_FIXEDPOSTUPDATE])
  693. SubscribeToEvent(world, E_PHYSICSPOSTSTEP, URHO3D_HANDLER(ScriptInstance, HandlePhysicsPostStep));
  694. }
  695. else
  696. URHO3D_LOGERROR("No physics world, can not subscribe script object to fixed update events");
  697. }
  698. #endif
  699. subscribedPostFixed_ = true;
  700. }
  701. if (methods_[METHOD_TRANSFORMCHANGED])
  702. node_->AddListener(this);
  703. }
  704. else
  705. {
  706. if (subscribed_)
  707. {
  708. UnsubscribeFromEvent(scene, E_SCENEUPDATE);
  709. subscribed_ = false;
  710. }
  711. if (subscribedPostFixed_)
  712. {
  713. UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
  714. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  715. Component* world = GetFixedUpdateSource();
  716. if (world)
  717. {
  718. UnsubscribeFromEvent(world, E_PHYSICSPRESTEP);
  719. UnsubscribeFromEvent(world, E_PHYSICSPOSTSTEP);
  720. }
  721. #endif
  722. subscribedPostFixed_ = false;
  723. }
  724. if (methods_[METHOD_TRANSFORMCHANGED])
  725. node_->RemoveListener(this);
  726. }
  727. }
  728. void ScriptInstance::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
  729. {
  730. if (!scriptObject_)
  731. return;
  732. using namespace SceneUpdate;
  733. float timeStep = eventData[P_TIMESTEP].GetFloat();
  734. // Execute delayed calls
  735. for (unsigned i = 0; i < delayedCalls_.Size();)
  736. {
  737. DelayedCall& call = delayedCalls_[i];
  738. bool remove = false;
  739. call.delay_ -= timeStep;
  740. if (call.delay_ <= 0.0f)
  741. {
  742. if (!call.repeat_)
  743. remove = true;
  744. else
  745. call.delay_ += call.period_;
  746. Execute(call.declaration_, call.parameters_);
  747. }
  748. if (remove)
  749. delayedCalls_.Erase(i);
  750. else
  751. ++i;
  752. }
  753. // Execute delayed start before first update
  754. if (methods_[METHOD_DELAYEDSTART])
  755. {
  756. scriptFile_->Execute(scriptObject_, methods_[METHOD_DELAYEDSTART]);
  757. methods_[METHOD_DELAYEDSTART] = nullptr; // Only execute once
  758. }
  759. if (methods_[METHOD_UPDATE])
  760. {
  761. VariantVector parameters;
  762. parameters.Push(timeStep);
  763. scriptFile_->Execute(scriptObject_, methods_[METHOD_UPDATE], parameters);
  764. }
  765. }
  766. void ScriptInstance::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
  767. {
  768. if (!scriptObject_)
  769. return;
  770. using namespace ScenePostUpdate;
  771. VariantVector parameters;
  772. parameters.Push(eventData[P_TIMESTEP]);
  773. scriptFile_->Execute(scriptObject_, methods_[METHOD_POSTUPDATE], parameters);
  774. }
  775. #if defined(URHO3D_PHYSICS) || defined(URHO3D_URHO2D)
  776. void ScriptInstance::HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
  777. {
  778. if (!scriptObject_)
  779. return;
  780. // Execute delayed start before first fixed update if not called yet
  781. if (methods_[METHOD_DELAYEDSTART])
  782. {
  783. scriptFile_->Execute(scriptObject_, methods_[METHOD_DELAYEDSTART]);
  784. methods_[METHOD_DELAYEDSTART] = nullptr; // Only execute once
  785. }
  786. using namespace PhysicsPreStep;
  787. VariantVector parameters;
  788. parameters.Push(eventData[P_TIMESTEP]);
  789. scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDUPDATE], parameters);
  790. }
  791. void ScriptInstance::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
  792. {
  793. if (!scriptObject_)
  794. return;
  795. using namespace PhysicsPostStep;
  796. VariantVector parameters;
  797. parameters.Push(eventData[P_TIMESTEP]);
  798. scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDPOSTUPDATE], parameters);
  799. }
  800. #endif
  801. void ScriptInstance::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
  802. {
  803. if (!IsEnabledEffective() || !scriptFile_ || !scriptObject_)
  804. return;
  805. auto* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
  806. VariantVector parameters;
  807. if (method->GetParamCount() > 0)
  808. {
  809. parameters.Push(Variant((void*)&eventType));
  810. parameters.Push(Variant((void*)&eventData));
  811. }
  812. scriptFile_->Execute(scriptObject_, method, parameters);
  813. }
  814. void ScriptInstance::HandleScriptFileReload(StringHash eventType, VariantMap& eventData)
  815. {
  816. StoreScriptAttributes();
  817. ReleaseObject();
  818. }
  819. void ScriptInstance::HandleScriptFileReloadFinished(StringHash eventType, VariantMap& eventData)
  820. {
  821. if (!className_.Empty())
  822. {
  823. CreateObject();
  824. RestoreScriptAttributes();
  825. }
  826. }
  827. asIScriptContext* GetActiveASContext()
  828. {
  829. return asGetActiveContext();
  830. }
  831. Context* GetScriptContext()
  832. {
  833. asIScriptContext* context = asGetActiveContext();
  834. if (context)
  835. return static_cast<Script*>(context->GetEngine()->GetUserData())->GetContext();
  836. else
  837. return nullptr;
  838. }
  839. ScriptInstance* GetScriptContextInstance()
  840. {
  841. asIScriptContext* context = asGetActiveContext();
  842. asIScriptObject* object = context ? static_cast<asIScriptObject*>(context->GetThisPointer()) : nullptr;
  843. if (object)
  844. return static_cast<ScriptInstance*>(object->GetUserData());
  845. else
  846. return nullptr;
  847. }
  848. Node* GetScriptContextNode()
  849. {
  850. ScriptInstance* instance = GetScriptContextInstance();
  851. return instance ? instance->GetNode() : nullptr;
  852. }
  853. Scene* GetScriptContextScene()
  854. {
  855. Scene* scene = nullptr;
  856. Node* node = GetScriptContextNode();
  857. if (node)
  858. scene = node->GetScene();
  859. // If null, try to get the default scene
  860. if (!scene)
  861. scene = GetScriptContext()->GetSubsystem<Script>()->GetDefaultScene();
  862. return scene;
  863. }
  864. ScriptEventListener* GetScriptContextEventListener()
  865. {
  866. // If the context has an object and that object has user data set, try and get the ScriptInstance, otherwise try and get a ScriptFile.
  867. asIScriptContext* context = asGetActiveContext();
  868. if (context)
  869. {
  870. auto* object = static_cast<asIScriptObject*>(context->GetThisPointer());
  871. if (object && object->GetUserData())
  872. return GetScriptContextInstance();
  873. else
  874. return GetScriptContextFile();
  875. }
  876. else
  877. return nullptr;
  878. }
  879. Object* GetScriptContextEventListenerObject()
  880. {
  881. return dynamic_cast<Object*>(GetScriptContextEventListener());
  882. }
  883. }