BsScriptObject.h 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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 <mono/jit/jit.h>
  8. namespace BansheeEngine
  9. {
  10. template <class Type, class Base>
  11. struct InitScriptObjectOnStart
  12. {
  13. public:
  14. InitScriptObjectOnStart()
  15. {
  16. ScriptObject<Type, Base>::_initMetaData();
  17. }
  18. void makeSureIAmInstantiated() { }
  19. };
  20. class BS_SCR_BE_EXPORT ScriptObjectBase
  21. {
  22. public:
  23. ScriptObjectBase(MonoObject* instance);
  24. virtual ~ScriptObjectBase();
  25. MonoObject* getManagedInstance() const { return mManagedInstance; }
  26. virtual void* getNativeRaw() const { return nullptr; }
  27. virtual void _onManagedInstanceDeleted();
  28. protected:
  29. MonoObject* mManagedInstance;
  30. };
  31. /**
  32. * @brief Base class for objects that can be extended using Mono scripting
  33. */
  34. template <class Type, class Base = ScriptObjectBase>
  35. class ScriptObject : public Base
  36. {
  37. public:
  38. ScriptObject(MonoObject* instance)
  39. :Base(instance)
  40. {
  41. // Compiler will only generate code for stuff that is directly used, including static data members,
  42. // so we fool it here like we're using the class directly. Otherwise compiler won't generate the code for the member
  43. // and our type won't get initialized on start (Actual behavior is a bit more random)
  44. initOnStart.makeSureIAmInstantiated();
  45. Type* param = (Type*)(Base*)this; // Needed due to multiple inheritance. Safe since Type must point to an class derived from this one.
  46. if(metaData.thisPtrField != nullptr)
  47. metaData.thisPtrField->setValue(instance, &param);
  48. }
  49. virtual ~ScriptObject()
  50. { }
  51. static Type* toNative(MonoObject* managedInstance)
  52. {
  53. Type* nativeInstance = nullptr;
  54. if (metaData.thisPtrField != nullptr && managedInstance != nullptr)
  55. metaData.thisPtrField->getValue(managedInstance, &nativeInstance);
  56. return nativeInstance;
  57. }
  58. static const ScriptMeta* getMetaData() { return &metaData; }
  59. static void _initMetaData()
  60. {
  61. metaData = ScriptMeta(Type::getAssemblyName(), Type::getNamespace(), Type::getTypeName(), &Type::initRuntimeData);
  62. MonoManager::registerScriptType(&metaData);
  63. }
  64. protected:
  65. static ScriptMeta metaData;
  66. template <class Type2, class Base2>
  67. static void throwIfInstancesDontMatch(ScriptObject<Type2, Base2>* lhs, void* rhs)
  68. {
  69. #if BS_DEBUG_MODE
  70. if((lhs == nullptr && rhs != nullptr) || (rhs == nullptr && lhs != nullptr) || lhs->getNativeRaw() != rhs)
  71. {
  72. BS_EXCEPT(InvalidStateException, "Native and script instance do not match. This usually happens when you modify a native object " \
  73. " that is also being referenced from script code. You should only modify such objects directly from script code.");
  74. }
  75. #endif
  76. }
  77. private:
  78. static InitScriptObjectOnStart<Type, Base> initOnStart;
  79. };
  80. template <typename Type, typename Base>
  81. InitScriptObjectOnStart<Type, Base> ScriptObject<Type, Base>::initOnStart;
  82. template <typename Type, typename Base>
  83. ScriptMeta ScriptObject<Type, Base>::metaData;
  84. #define SCRIPT_OBJ(assembly, namespace, name) \
  85. static String getAssemblyName() { return assembly; } \
  86. static String getNamespace() { return namespace; } \
  87. static String getTypeName() { return name; } \
  88. static void initRuntimeData();
  89. }