| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "BsManagedEditorCommand.h"
- #include "BsScriptMeta.h"
- #include "BsMonoField.h"
- #include "BsMonoClass.h"
- #include "BsMonoMethod.h"
- #include "BsMonoManager.h"
- #include "BsMonoUtil.h"
- #include "Serialization/BsScriptAssemblyManager.h"
- #include "Serialization/BsMemorySerializer.h"
- #include "Serialization/BsManagedSerializableObject.h"
- namespace bs
- {
- MonoMethod* ScriptCmdManaged::sCommitMethod = nullptr;
- MonoMethod* ScriptCmdManaged::sRevertMethod = nullptr;
- ScriptCmdManaged::ScriptCmdManaged(MonoObject* managedInstance)
- :ScriptObject(managedInstance)
- {
- MonoUtil::getClassName(managedInstance, mNamespace, mType);
- if(!ScriptAssemblyManager::instance().hasSerializableObjectInfo(mNamespace, mType))
- {
- BS_LOG(Warning, Editor, "UndoableCommand created without [SerializeObject] attribute. The command will not be "
- "able to persist assembly refresh.");
- }
- mManagedCommand = bs_shared_ptr(new (bs_alloc<CmdManaged>()) CmdManaged(this));
- mGCHandle = MonoUtil::newWeakGCHandle(managedInstance);
- mInUndoRedoStack = false;
- }
- ScriptCmdManaged::~ScriptCmdManaged()
- {
- if(mManagedCommand != nullptr)
- {
- mManagedCommand->notifyScriptInstanceDestroyed();
- mManagedCommand = nullptr;
- }
- }
- void ScriptCmdManaged::initRuntimeData()
- {
- metaData.scriptClass->addInternalCall("Internal_CreateInstance", (void*)&ScriptCmdManaged::internal_CreateInstance);
- sCommitMethod = metaData.scriptClass->getMethod("Commit");
- sRevertMethod = metaData.scriptClass->getMethod("Revert");
- }
- void ScriptCmdManaged::internal_CreateInstance(MonoObject* managedInstance)
- {
- new (bs_alloc<ScriptCmdManaged>()) ScriptCmdManaged(managedInstance);
- }
- void ScriptCmdManaged::triggerCommit()
- {
- if (sCommitMethod == nullptr)
- return;
- MonoObject* obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
- sCommitMethod->invokeVirtual(obj, nullptr);
- }
- void ScriptCmdManaged::triggerRevert()
- {
- if (sRevertMethod == nullptr)
- return;
- MonoObject* obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
- sRevertMethod->invokeVirtual(obj, nullptr);
- }
- void ScriptCmdManaged::notifyCommandDestroyed()
- {
- mManagedCommand = nullptr;
- }
- void ScriptCmdManaged::notifyStackAdded()
- {
- if (!mInUndoRedoStack)
- {
- MonoObject* obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
- mGCHandle = MonoUtil::newGCHandle(obj, false);
- mInUndoRedoStack = true;
- }
- }
- void ScriptCmdManaged::notifyStackRemoved()
- {
- MonoObject* obj = nullptr;
- if (mGCHandle)
- obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
- if (mInUndoRedoStack && mGCHandle != 0)
- {
- MonoUtil::freeGCHandle(mGCHandle);
- mGCHandle = 0;
- }
- // Note: Re-creating the weak handle might not be necessary as the command shouldn't be allowed to be re-added
- // after it has been removed from the undo-redo stack (which should be the only place this method is called from).
- if(obj)
- mGCHandle = MonoUtil::newWeakGCHandle(obj);
- mInUndoRedoStack = false;
- }
- MonoObject* ScriptCmdManaged::_createManagedInstance(bool construct)
- {
- SPtr<ManagedSerializableObjectInfo> currentObjInfo = nullptr;
- // If not in undo-redo stack then this object should have been deleted
- assert(mInUndoRedoStack);
- // See if this type even still exists
- if (!ScriptAssemblyManager::instance().getSerializableObjectInfo(mNamespace, mType, currentObjInfo))
- {
- mTypeMissing = true;
- return nullptr;
- }
- MonoObject* instance = currentObjInfo->mMonoClass->createInstance(construct);
- mGCHandle = MonoUtil::newGCHandle(instance, false);
- mTypeMissing = false;
- return instance;
- }
- void ScriptCmdManaged::_clearManagedInstance()
- {
- if(mInUndoRedoStack)
- {
- MonoUtil::freeGCHandle(mGCHandle);
- mGCHandle = 0;
- }
- }
- ScriptObjectBackup ScriptCmdManaged::beginRefresh()
- {
- RawBackupData backupData;
- if(mInUndoRedoStack)
- {
- // If type is not missing, restore saved data, otherwise keep the data for later
- SPtr<ManagedSerializableObject> serializableObject;
- if(!mTypeMissing)
- {
- MonoObject* instance = MonoUtil::getObjectFromGCHandle(mGCHandle);
- serializableObject = ManagedSerializableObject::createFromExisting(instance);
- }
- else
- serializableObject = mSerializedObjectData;
- if (serializableObject != nullptr)
- {
- MemorySerializer ms;
- backupData.data = ms.encode(serializableObject.get(), backupData.size);
- }
- }
- return ScriptObjectBackup(backupData);
- }
- void ScriptCmdManaged::endRefresh(const ScriptObjectBackup& backupData)
- {
- const RawBackupData& data = any_cast_ref<RawBackupData>(backupData.data);
- MemorySerializer ms;
- SPtr<ManagedSerializableObject> serializableObject = std::static_pointer_cast<ManagedSerializableObject>(
- ms.decode(data.data, data.size));
- if(!mTypeMissing)
- {
- SPtr<ManagedSerializableObjectInfo> objInfo;
- ScriptAssemblyManager::instance().getSerializableObjectInfo(mNamespace, mType, objInfo);
- MonoObject* instance = MonoUtil::getObjectFromGCHandle(mGCHandle);
- serializableObject->deserialize(instance, objInfo);
- mSerializedObjectData = nullptr;
- }
- else
- mSerializedObjectData = serializableObject;
- }
- void ScriptCmdManaged::_onManagedInstanceDeleted(bool assemblyRefresh)
- {
- if(!mInUndoRedoStack || !assemblyRefresh)
- ScriptObjectBase::_onManagedInstanceDeleted(assemblyRefresh);
- }
- CmdManaged::CmdManaged(ScriptCmdManaged* scriptObj)
- : EditorCommand(""), mScriptObj(scriptObj), mRefCount(0)
- {
- }
- CmdManaged::~CmdManaged()
- {
- if (mScriptObj != nullptr)
- mScriptObj->notifyCommandDestroyed();
- }
- void CmdManaged::commit()
- {
- if(mScriptObj == nullptr)
- {
- BS_LOG(Warning, Editor, "Trying to execute a managed undo/redo command whose managed object has been "
- "destroyed, ignoring.");
- // Note: This can most likely happen if managed undo commands are queued on a global undo/redo stack. When
- // assembly refresh happens those commands will be rebuilt, but this can fail if the user removed the command
- // type from the assembly, or the command isn't serializable.
- return;
- }
- mScriptObj->triggerCommit();
- }
- void CmdManaged::revert()
- {
- if (mScriptObj == nullptr)
- {
- BS_LOG(Warning, Editor, "Trying to execute a managed undo/redo command whose managed object has been "
- "destroyed, ignoring.");
- // Note: This can most likely happen if managed undo commands are queued on a global undo/redo stack. When
- // assembly refresh happens those commands will be rebuilt, but this can fail if the user removed the command
- // type from the assembly, or the command isn't serializable.
- return;
- }
- mScriptObj->triggerRevert();
- }
- void CmdManaged::onCommandAdded()
- {
- if(mScriptObj)
- mScriptObj->notifyStackAdded();
- mRefCount++;
- }
- void CmdManaged::onCommandRemoved()
- {
- assert(mRefCount > 0);
- mRefCount--;
- if (mRefCount == 0)
- mScriptObj->notifyStackRemoved();
- }
- void CmdManaged::notifyScriptInstanceDestroyed()
- {
- mScriptObj = nullptr;
- }
- }
|