BsScriptObject.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. #pragma once
  2. #include "BsScriptEnginePrerequisites.h"
  3. #include "BsScriptMeta.h"
  4. #include "BsException.h"
  5. #include "BsMonoManager.h"
  6. #include "BsMonoField.h"
  7. #include "BsMonoClass.h"
  8. #include <mono/jit/jit.h>
  9. namespace BansheeEngine
  10. {
  11. struct ScriptObjectBackup;
  12. template <class Type, class Base>
  13. struct InitScriptObjectOnStart
  14. {
  15. public:
  16. InitScriptObjectOnStart()
  17. {
  18. ScriptObject<Type, Base>::_initMetaData();
  19. }
  20. void makeSureIAmInstantiated() { }
  21. };
  22. class BS_SCR_BE_EXPORT ScriptObjectBase
  23. {
  24. public:
  25. ScriptObjectBase(MonoObject* instance);
  26. virtual ~ScriptObjectBase();
  27. MonoObject* getManagedInstance() const { return mManagedInstance; }
  28. virtual void* getNativeRaw() const { return nullptr; }
  29. virtual bool isPersistent() const { return false; }
  30. virtual void _clearManagedInstance() { }
  31. virtual void _restoreManagedInstance() { }
  32. virtual void _onManagedInstanceDeleted();
  33. virtual ScriptObjectBackup beginRefresh();
  34. virtual void endRefresh(const ScriptObjectBackup& data);
  35. protected:
  36. MonoObject* mManagedInstance;
  37. };
  38. class BS_SCR_BE_EXPORT PersistentScriptObjectBase : public ScriptObjectBase
  39. {
  40. public:
  41. PersistentScriptObjectBase(MonoObject* instance);
  42. virtual ~PersistentScriptObjectBase();
  43. virtual bool isPersistent() const { return true; }
  44. };
  45. /**
  46. * @brief Base class for objects that can be extended using Mono scripting
  47. */
  48. template <class Type, class Base = ScriptObjectBase>
  49. class ScriptObject : public Base
  50. {
  51. public:
  52. ScriptObject(MonoObject* instance)
  53. :Base(instance)
  54. {
  55. // Compiler will only generate code for stuff that is directly used, including static data members,
  56. // so we fool it here like we're using the class directly. Otherwise compiler won't generate the code for the member
  57. // and our type won't get initialized on start (Actual behavior is a bit more random)
  58. // TODO - Use volatile instead?
  59. initOnStart.makeSureIAmInstantiated();
  60. Type* param = (Type*)(Base*)this; // Needed due to multiple inheritance. Safe since Type must point to an class derived from this one.
  61. if(metaData.thisPtrField != nullptr)
  62. metaData.thisPtrField->setValue(instance, &param);
  63. }
  64. virtual ~ScriptObject()
  65. { }
  66. void _clearManagedInstance()
  67. {
  68. if (metaData.thisPtrField != nullptr && mManagedInstance != nullptr)
  69. metaData.thisPtrField->setValue(mManagedInstance, nullptr);
  70. mManagedInstance = nullptr;
  71. }
  72. void _restoreManagedInstance()
  73. {
  74. // Do not construct as constructor usually calls initialization code we don't need when restoring.
  75. // Managed type needs to account for that on a per-type basis.
  76. mManagedInstance = _createManagedInstance(false);
  77. Type* param = (Type*)(Base*)this; // Needed due to multiple inheritance. Safe since Type must point to an class derived from this one.
  78. if (metaData.thisPtrField != nullptr && mManagedInstance != nullptr)
  79. metaData.thisPtrField->setValue(mManagedInstance, &param);
  80. }
  81. virtual MonoObject* _createManagedInstance(bool construct)
  82. {
  83. return metaData.scriptClass->createInstance(construct);
  84. }
  85. static Type* toNative(MonoObject* managedInstance)
  86. {
  87. Type* nativeInstance = nullptr;
  88. if (metaData.thisPtrField != nullptr && managedInstance != nullptr)
  89. metaData.thisPtrField->getValue(managedInstance, &nativeInstance);
  90. return nativeInstance;
  91. }
  92. static const ScriptMeta* getMetaData() { return &metaData; }
  93. static void _initMetaData()
  94. {
  95. metaData = ScriptMeta(Type::getAssemblyName(), Type::getNamespace(), Type::getTypeName(), &Type::initRuntimeData);
  96. MonoManager::registerScriptType(&metaData);
  97. }
  98. protected:
  99. static ScriptMeta metaData;
  100. template <class Type2, class Base2>
  101. static void throwIfInstancesDontMatch(ScriptObject<Type2, Base2>* lhs, void* rhs)
  102. {
  103. #if BS_DEBUG_MODE
  104. if((lhs == nullptr && rhs != nullptr) || (rhs == nullptr && lhs != nullptr) || lhs->getNativeRaw() != rhs)
  105. {
  106. BS_EXCEPT(InvalidStateException, "Native and script instance do not match. This usually happens when you modify a native object " \
  107. " that is also being referenced from script code. You should only modify such objects directly from script code.");
  108. }
  109. #endif
  110. }
  111. private:
  112. static InitScriptObjectOnStart<Type, Base> initOnStart;
  113. };
  114. template <typename Type, typename Base>
  115. InitScriptObjectOnStart<Type, Base> ScriptObject<Type, Base>::initOnStart;
  116. template <typename Type, typename Base>
  117. ScriptMeta ScriptObject<Type, Base>::metaData;
  118. struct ScriptObjectBackup
  119. {
  120. Any data;
  121. };
  122. #define SCRIPT_OBJ(assembly, namespace, name) \
  123. static String getAssemblyName() { return assembly; } \
  124. static String getNamespace() { return namespace; } \
  125. static String getTypeName() { return name; } \
  126. static void initRuntimeData();
  127. }