BsManagedEditorCommand.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsManagedEditorCommand.h"
  4. #include "BsScriptMeta.h"
  5. #include "BsMonoField.h"
  6. #include "BsMonoClass.h"
  7. #include "BsMonoMethod.h"
  8. #include "BsMonoManager.h"
  9. #include "BsMonoUtil.h"
  10. #include "Serialization/BsScriptAssemblyManager.h"
  11. #include "Serialization/BsManagedSerializableObject.h"
  12. #include "Serialization/BsBinarySerializer.h"
  13. #include "FileSystem/BsDataStream.h"
  14. namespace bs
  15. {
  16. MonoMethod* ScriptCmdManaged::sCommitMethod = nullptr;
  17. MonoMethod* ScriptCmdManaged::sRevertMethod = nullptr;
  18. ScriptCmdManaged::ScriptCmdManaged(MonoObject* managedInstance)
  19. :ScriptObject(managedInstance)
  20. {
  21. MonoUtil::getClassName(managedInstance, mNamespace, mType);
  22. if(!ScriptAssemblyManager::instance().hasSerializableObjectInfo(mNamespace, mType))
  23. {
  24. BS_LOG(Warning, Editor, "UndoableCommand created without [SerializeObject] attribute. The command will not be "
  25. "able to persist assembly refresh.");
  26. }
  27. mManagedCommand = bs_shared_ptr(new (bs_alloc<CmdManaged>()) CmdManaged(this));
  28. mGCHandle = MonoUtil::newWeakGCHandle(managedInstance);
  29. mInUndoRedoStack = false;
  30. }
  31. ScriptCmdManaged::~ScriptCmdManaged()
  32. {
  33. if(mManagedCommand != nullptr)
  34. {
  35. mManagedCommand->notifyScriptInstanceDestroyed();
  36. mManagedCommand = nullptr;
  37. }
  38. }
  39. void ScriptCmdManaged::initRuntimeData()
  40. {
  41. metaData.scriptClass->addInternalCall("Internal_CreateInstance", (void*)&ScriptCmdManaged::internal_CreateInstance);
  42. sCommitMethod = metaData.scriptClass->getMethod("Commit");
  43. sRevertMethod = metaData.scriptClass->getMethod("Revert");
  44. }
  45. void ScriptCmdManaged::internal_CreateInstance(MonoObject* managedInstance)
  46. {
  47. new (bs_alloc<ScriptCmdManaged>()) ScriptCmdManaged(managedInstance);
  48. }
  49. void ScriptCmdManaged::triggerCommit()
  50. {
  51. if (sCommitMethod == nullptr)
  52. return;
  53. MonoObject* obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
  54. sCommitMethod->invokeVirtual(obj, nullptr);
  55. }
  56. void ScriptCmdManaged::triggerRevert()
  57. {
  58. if (sRevertMethod == nullptr)
  59. return;
  60. MonoObject* obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
  61. sRevertMethod->invokeVirtual(obj, nullptr);
  62. }
  63. void ScriptCmdManaged::notifyCommandDestroyed()
  64. {
  65. mManagedCommand = nullptr;
  66. }
  67. void ScriptCmdManaged::notifyStackAdded()
  68. {
  69. if (!mInUndoRedoStack)
  70. {
  71. MonoObject* obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
  72. mGCHandle = MonoUtil::newGCHandle(obj, false);
  73. mInUndoRedoStack = true;
  74. }
  75. }
  76. void ScriptCmdManaged::notifyStackRemoved()
  77. {
  78. MonoObject* obj = nullptr;
  79. if (mGCHandle)
  80. obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
  81. if (mInUndoRedoStack && mGCHandle != 0)
  82. {
  83. MonoUtil::freeGCHandle(mGCHandle);
  84. mGCHandle = 0;
  85. }
  86. // Note: Re-creating the weak handle might not be necessary as the command shouldn't be allowed to be re-added
  87. // after it has been removed from the undo-redo stack (which should be the only place this method is called from).
  88. if(obj)
  89. mGCHandle = MonoUtil::newWeakGCHandle(obj);
  90. mInUndoRedoStack = false;
  91. }
  92. MonoObject* ScriptCmdManaged::_createManagedInstance(bool construct)
  93. {
  94. SPtr<ManagedSerializableObjectInfo> currentObjInfo = nullptr;
  95. // If not in undo-redo stack then this object should have been deleted
  96. assert(mInUndoRedoStack);
  97. // See if this type even still exists
  98. if (!ScriptAssemblyManager::instance().getSerializableObjectInfo(mNamespace, mType, currentObjInfo))
  99. {
  100. mTypeMissing = true;
  101. return nullptr;
  102. }
  103. MonoObject* instance = currentObjInfo->mMonoClass->createInstance(construct);
  104. mGCHandle = MonoUtil::newGCHandle(instance, false);
  105. mTypeMissing = false;
  106. return instance;
  107. }
  108. void ScriptCmdManaged::_clearManagedInstance()
  109. {
  110. if(mInUndoRedoStack)
  111. {
  112. MonoUtil::freeGCHandle(mGCHandle);
  113. mGCHandle = 0;
  114. }
  115. }
  116. ScriptObjectBackup ScriptCmdManaged::beginRefresh()
  117. {
  118. RawBackupData backupData;
  119. if(mInUndoRedoStack)
  120. {
  121. // If type is not missing, restore saved data, otherwise keep the data for later
  122. SPtr<ManagedSerializableObject> serializableObject;
  123. if(!mTypeMissing)
  124. {
  125. MonoObject* instance = MonoUtil::getObjectFromGCHandle(mGCHandle);
  126. serializableObject = ManagedSerializableObject::createFromExisting(instance);
  127. }
  128. else
  129. serializableObject = mSerializedObjectData;
  130. if (serializableObject != nullptr)
  131. {
  132. SPtr<MemoryDataStream> stream = bs_shared_ptr_new<MemoryDataStream>();
  133. BinarySerializer bs;
  134. bs.encode(serializableObject.get(), stream);
  135. backupData.size = (UINT32)stream->size();
  136. backupData.data = stream->disownMemory();
  137. }
  138. }
  139. return ScriptObjectBackup(backupData);
  140. }
  141. void ScriptCmdManaged::endRefresh(const ScriptObjectBackup& backupData)
  142. {
  143. const RawBackupData& data = any_cast_ref<RawBackupData>(backupData.data);
  144. BinarySerializer bs;
  145. SPtr<ManagedSerializableObject> serializableObject = std::static_pointer_cast<ManagedSerializableObject>(
  146. bs.decode(bs_shared_ptr_new<MemoryDataStream>(data.data, data.size), data.size));
  147. if(!mTypeMissing)
  148. {
  149. SPtr<ManagedSerializableObjectInfo> objInfo;
  150. ScriptAssemblyManager::instance().getSerializableObjectInfo(mNamespace, mType, objInfo);
  151. MonoObject* instance = MonoUtil::getObjectFromGCHandle(mGCHandle);
  152. serializableObject->deserialize(instance, objInfo);
  153. mSerializedObjectData = nullptr;
  154. }
  155. else
  156. mSerializedObjectData = serializableObject;
  157. }
  158. void ScriptCmdManaged::_onManagedInstanceDeleted(bool assemblyRefresh)
  159. {
  160. if(!mInUndoRedoStack || !assemblyRefresh)
  161. ScriptObjectBase::_onManagedInstanceDeleted(assemblyRefresh);
  162. }
  163. CmdManaged::CmdManaged(ScriptCmdManaged* scriptObj)
  164. : EditorCommand(""), mScriptObj(scriptObj), mRefCount(0)
  165. {
  166. }
  167. CmdManaged::~CmdManaged()
  168. {
  169. if (mScriptObj != nullptr)
  170. mScriptObj->notifyCommandDestroyed();
  171. }
  172. void CmdManaged::commit()
  173. {
  174. if(mScriptObj == nullptr)
  175. {
  176. BS_LOG(Warning, Editor, "Trying to execute a managed undo/redo command whose managed object has been "
  177. "destroyed, ignoring.");
  178. // Note: This can most likely happen if managed undo commands are queued on a global undo/redo stack. When
  179. // assembly refresh happens those commands will be rebuilt, but this can fail if the user removed the command
  180. // type from the assembly, or the command isn't serializable.
  181. return;
  182. }
  183. mScriptObj->triggerCommit();
  184. }
  185. void CmdManaged::revert()
  186. {
  187. if (mScriptObj == nullptr)
  188. {
  189. BS_LOG(Warning, Editor, "Trying to execute a managed undo/redo command whose managed object has been "
  190. "destroyed, ignoring.");
  191. // Note: This can most likely happen if managed undo commands are queued on a global undo/redo stack. When
  192. // assembly refresh happens those commands will be rebuilt, but this can fail if the user removed the command
  193. // type from the assembly, or the command isn't serializable.
  194. return;
  195. }
  196. mScriptObj->triggerRevert();
  197. }
  198. void CmdManaged::onCommandAdded()
  199. {
  200. if(mScriptObj)
  201. mScriptObj->notifyStackAdded();
  202. mRefCount++;
  203. }
  204. void CmdManaged::onCommandRemoved()
  205. {
  206. assert(mRefCount > 0);
  207. mRefCount--;
  208. if (mRefCount == 0)
  209. mScriptObj->notifyStackRemoved();
  210. }
  211. void CmdManaged::notifyScriptInstanceDestroyed()
  212. {
  213. mScriptObj = nullptr;
  214. }
  215. }