IMonoObject *CScriptSystem::InstantiateScript(const char *scriptName, EMonoScriptFlags scriptFlags, IMonoArray *pConstructorParameters, bool throwOnFail) { auto *pInstance = new CCryScriptInstance(scriptFlags); IMonoArray *pScriptCreationArgs = CreateMonoArray(5); pScriptCreationArgs->Insert(scriptName); pScriptCreationArgs->Insert(scriptFlags); pScriptCreationArgs->InsertNativePointer(pInstance); pScriptCreationArgs->InsertMonoObject((pConstructorParameters != nullptr ? pConstructorParameters->GetManagedObject() : nullptr)); pScriptCreationArgs->Insert(throwOnFail); mono::object result = m_pScriptManager->GetClass()->InvokeArray(m_pScriptManager->GetManagedObject(), "CreateScriptInstance", pScriptCreationArgs); SAFE_RELEASE(pScriptCreationArgs); if(!result) return nullptr; pInstance->SetManagedObject((MonoObject *)result, true); if(scriptFlags & eScriptFlag_GameRules) { IMonoClass *pGameRulesInitParamsClass = g_pScriptSystem->GetCryBraryAssembly()->GetClass("GameRulesInitializationParams"); IMonoArray *pArgs = CreateMonoArray(1); SGameRulesInitializationParams params; pArgs->InsertMonoObject(pGameRulesInitParamsClass->BoxObject(¶ms)); InitializeScriptInstance(pInstance, pArgs); SAFE_RELEASE(pArgs); } for each(auto listener in m_listeners) listener->OnScriptInstanceCreated(scriptName, scriptFlags, pInstance); return pInstance; }
bool CMonoFlowNode::CreatedNode(TFlowNodeId id, const char *name, TFlowNodeTypeId typeId, IFlowNodePtr pNode) { if(pNode==this) { m_id = id; m_graphId = m_pActInfo->pGraph->GetGraphId(); const char *typeName = gEnv->pFlowSystem->GetTypeName(typeId); ICryScriptInstance *pScript = GetMonoScriptSystem()->InstantiateScript(gEnv->pFlowSystem->GetTypeName(typeId), eScriptFlag_FlowNode); IMonoClass *pNodeInfo = GetMonoScriptSystem()->GetCryBraryAssembly()->GetClass("NodeInitializationParams", "CryEngine.Flowgraph.Native"); IMonoArray *pArgs = CreateMonoArray(1); pArgs->InsertMonoObject(pNodeInfo->BoxObject(&SMonoNodeInfo(this, id, m_graphId))); mono::object result = static_cast<CScriptSystem *>(GetMonoScriptSystem())->InitializeScriptInstance(pScript, pArgs); pArgs->Release(); m_pScript = pScript; m_scriptId = pScript->GetId(); if(result) { IMonoObject *pResult = *result; bool bResult = pResult->Unbox<bool>(); SAFE_RELEASE(pResult); return bResult; } CryLogAlways("Failed to create node %s", gEnv->pFlowSystem->GetTypeName(typeId)); return false; } return true; }
mono::object CScriptbind_Entity::GetEntitiesByClasses(mono::object classes) { IMonoArray *pClassArray = *classes; int numClasses = pClassArray->GetSize(); IEntityClass **pClasses = new IEntityClass *[numClasses]; for(int i = 0; i < numClasses; i++) pClasses[i] = gEnv->pEntitySystem->GetClassRegistry()->FindClass(ToCryString((mono::string)pClassArray->GetManagedObject())); IEntityItPtr pIt = gEnv->pEntitySystem->GetEntityIterator(); IMonoClass *pEntityIdClass = GetMonoScriptSystem()->GetCryBraryAssembly()->GetClass("EntityId"); IMonoArray *pEntities = CreateDynamicMonoArray(); pIt->MoveFirst(); while(!pIt->IsEnd()) { if(IEntity *pEntity = pIt->Next()) { IEntityClass *pEntityClass = pEntity->GetClass(); for(int i = 0; i < numClasses; i++) { if(pEntityClass == pClasses[i]) { pEntities->InsertMonoObject(pEntityIdClass->BoxObject(&mono::entityId(pEntity->GetId()))); break; } } } } auto result = pEntities->GetManagedObject(); pEntities->Release(); return result; }
mono::object CScriptbind_Entity::GetEntitiesByClass(mono::string _class) { IEntityClass *pDesiredClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(ToCryString(_class)); IEntityItPtr pIt = gEnv->pEntitySystem->GetEntityIterator(); IMonoClass *pEntityIdClass = GetMonoScriptSystem()->GetCryBraryAssembly()->GetClass("EntityId"); IMonoArray *pEntities = CreateDynamicMonoArray(); pIt->MoveFirst(); while(!pIt->IsEnd()) { if(IEntity *pEntity = pIt->Next()) { if(pEntity->GetClass() == pDesiredClass) pEntities->InsertMonoObject(pEntityIdClass->BoxObject(&mono::entityId(pEntity->GetId()))); } } auto result = pEntities->GetManagedObject(); pEntities->Release(); return result; }
mono::object CScriptbind_Entity::QueryProximity(AABB box, mono::string className, uint32 nEntityFlags) { SEntityProximityQuery query; if(className != nullptr) query.pEntityClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(ToCryString(className)); query.box = box; query.nEntityFlags = nEntityFlags; gEnv->pEntitySystem->QueryProximity(query); IMonoClass *pEntityIdClass = g_pScriptSystem->GetCryBraryAssembly()->GetClass("EntityId"); if(query.nCount > 0) { IMonoArray *pEntities = CreateMonoArray(query.nCount); for(int i = 0; i < query.nCount; i++) pEntities->Insert(pEntityIdClass->BoxObject(&mono::entityId(query.pEntities[i]->GetId()))); return pEntities->GetManagedObject(); } return nullptr; }
CMonoFlowNode::~CMonoFlowNode() { IMonoClass *pFlowNodeClass = GetMonoScriptSystem()->GetCryBraryAssembly()->GetClass("FlowNode", "CryEngine.Flowgraph"); pFlowNodeClass->CallMethod("InternalRemove", m_scriptId); }
CScriptSystem::CScriptSystem(IGameFramework *pGameFramework) : m_pRootDomain(nullptr) , m_pCryBraryAssembly(nullptr) , m_pPdb2MdbAssembly(nullptr) , m_pScriptManager(nullptr) , m_pScriptDomain(nullptr) , m_bReloading(false) , m_bDetectedChanges(false) , m_bQuitting(false) , m_pConverter(nullptr) , m_bFirstReload(true) , m_pGameFramework(pGameFramework) { CryLogAlways("Initializing Mono Script System"); g_pThis = this; m_pCVars = new SCVars(); g_pMonoCVars = m_pCVars; // We should look into storing mono binaries, configuration as well as scripts via CryPak. mono_set_dirs(PathUtils::GetMonoLibPath(), PathUtils::GetMonoConfigPath()); #ifndef _RELEASE // Enable Mono signal handling // Makes sure that Mono sends back exceptions it tries to handle, for CE crash handling. mono_set_signal_chaining(true); #endif string monoCmdOptions = ""; if(auto *pArg = gEnv->pSystem->GetICmdLine()->FindArg(eCLAT_Pre, "monoArgs")) monoCmdOptions.append(pArg->GetValue()); // Commandline switch -DEBUG makes the process connect to the debugging server. Warning: Failure to connect to a debugging server WILL result in a crash. // This is currently a WIP feature which requires custom MonoDevelop extensions and other irritating things. const ICmdLineArg* arg = gEnv->pSystem->GetICmdLine()->FindArg(eCLAT_Pre, "DEBUG"); if (arg != nullptr) monoCmdOptions.append("--debugger-agent=transport=dt_socket,address=127.0.0.1:65432,embedding=1"); #ifndef _RELEASE else if(g_pMonoCVars->mono_softBreakpoints) // Soft breakpoints not compatible with debugging server { CryLogAlways(" [Performance Warning] Mono soft breakpoints are enabled!"); // Prevents managed null reference exceptions causing crashes in unmanaged code // See: https://bugzilla.xamarin.com/show_bug.cgi?id=5963 monoCmdOptions.append("--soft-breakpoints"); } #endif char *options = new char[monoCmdOptions.size() + 1]; strcpy(options, monoCmdOptions.c_str()); // Note: iPhone requires AOT compilation, this can be enforced via mono options. TODO: Get Crytek to add CryMobile support to the Free SDK. mono_jit_parse_options(1, &options); #ifndef _RELEASE // Required for mdb's to load for detailed stack traces etc. mono_debug_init(MONO_DEBUG_FORMAT_MONO); #endif m_pConverter = new CConverter(); if(!CompleteInit()) { CryLogAlways("CryMono initialization failed!"); return; } RegisterSecondaryBindings(); pGameFramework->RegisterListener(this, "CryMono", FRAMEWORKLISTENERPRIORITY_GAME); gEnv->pSystem->GetISystemEventDispatcher()->RegisterListener(&g_systemEventListener_CryMono); if(IFileChangeMonitor *pFileChangeMonitor = gEnv->pFileChangeMonitor) pFileChangeMonitor->RegisterListener(this, "scripts\\"); CryModuleMemoryInfo memInfo; CryModuleGetMemoryInfo(&memInfo); IMonoClass *pCryStats = m_pCryBraryAssembly->GetClass("CryStats", "CryEngine.Utilities"); IMonoObject *pMemoryUsage = *pCryStats->GetPropertyValue(NULL, "MemoryUsage"); CryLogAlways(" Initializing CryMono done, MemUsage=%iKb", (memInfo.allocated + pMemoryUsage->Unbox<long>()) / 1024); pMemoryUsage->Release(); }
bool CScriptSystem::Reload() { if((!m_bFirstReload && g_pMonoCVars->mono_realtimeScripting == 0) || m_bReloading) return false; m_bReloading = true; if(!m_bFirstReload) { for each(auto listener in m_listeners) listener->OnReloadStart(); m_pScriptManager->CallMethod("Serialize"); } IMonoDomain *pScriptDomain = CreateDomain("ScriptDomain", nullptr, true); IMonoAssembly *pCryBraryAssembly = pScriptDomain->LoadAssembly(PathUtils::GetBinaryPath() + "CryBrary.dll"); IMonoArray *pCtorParams = CreateMonoArray(2); pCtorParams->InsertAny(m_bFirstReload); pCtorParams->InsertMonoString(ToMonoString(PathUtils::GetConfigPath())); IMonoObject *pScriptManager = *pCryBraryAssembly->GetClass("ScriptManager", "CryEngine.Initialization")->CreateInstance(pCtorParams); SAFE_RELEASE(pCtorParams); auto result = pScriptManager->CallMethod("Initialize", m_bFirstReload); if(result == nullptr) return false; IMonoObject *pResult = *result; auto reloadResult = pResult->Unbox<EScriptReloadResult>(); SAFE_RELEASE(pResult); switch(reloadResult) { case EScriptReloadResult_Success: { // revert previous domain if(!m_bFirstReload) m_pScriptDomain->Release(); m_pScriptDomain = pScriptDomain; m_pScriptManager = pScriptManager; m_pCryBraryAssembly = pCryBraryAssembly; CacheManagedResources(); if(!m_bFirstReload) m_pScriptManager->CallMethod("Deserialize"); // Set Network.Editor etc. IMonoClass *pClass = m_pCryBraryAssembly->GetClass("Game"); IMonoArray *pArgs = CreateMonoArray(2); pArgs->Insert(gEnv->IsEditor()); pArgs->Insert(gEnv->IsDedicated()); pClass->GetMethod("InitializeGameStatics", 2)->InvokeArray(nullptr, pArgs); SAFE_RELEASE(pArgs); m_pScriptManager->CallMethod("ProcessWaitingScripts", m_bFirstReload); for each(auto listener in m_listeners) listener->OnReloadComplete(); if(!m_bFirstReload && gEnv->IsEditor()) gEnv->pFlowSystem->ReloadAllNodeTypes(); m_bReloading = false; m_bDetectedChanges = false; return true; } break; case EScriptReloadResult_Retry: { m_bReloading = false; return Reload(); } case EScriptReloadResult_Revert: { pScriptDomain->Release(); m_pScriptDomain->SetActive(); m_bReloading = false; m_bDetectedChanges = false; return false; } break; case EScriptReloadResult_Abort: { gEnv->pSystem->Quit(); m_bReloading = false; } break; } return false; }
void CEntityEventHandler::HandleEntityEvent(EEntityType type, SEntityEvent &event, IEntity *pEntity, mono::object managedObject) { switch(event.event) { case ENTITY_EVENT_RESET: { bool enterGamemode = event.nParam[0]==1; if(!enterGamemode && pEntity->GetFlags() & ENTITY_FLAG_NO_SAVE) { gEnv->pEntitySystem->RemoveEntity(pEntity->GetId()); return; } IMonoArray *pParams = CreateMonoArray(1); pParams->Insert(enterGamemode); m_pClass[type]->GetMethod("OnEditorReset", 1)->InvokeArray(managedObject, pParams); SAFE_RELEASE(pParams); } break; case ENTITY_EVENT_COLLISION: { EventPhysCollision *pCollision = (EventPhysCollision *)event.nParam[0]; SMonoColliderInfo source = SMonoColliderInfo(pCollision, 0); SMonoColliderInfo target = SMonoColliderInfo(pCollision, 1); IMonoClass *pColliderInfoClass = g_pScriptSystem->GetCryBraryAssembly()->GetClass("ColliderInfo"); IMonoArray *pArgs = CreateMonoArray(6); pArgs->InsertMonoObject(pColliderInfoClass->BoxObject(&source)); pArgs->InsertMonoObject(pColliderInfoClass->BoxObject(&target)); pArgs->Insert(pCollision->pt); pArgs->Insert(pCollision->n); pArgs->Insert(pCollision->penetration); pArgs->Insert(pCollision->radius); m_pClass[type]->GetMethod("OnCollision", 6)->InvokeArray(managedObject, pArgs); SAFE_RELEASE(pArgs); } break; case ENTITY_EVENT_START_GAME: m_pClass[type]->GetMethod("OnStartGame")->Invoke(managedObject); break; case ENTITY_EVENT_START_LEVEL: m_pClass[type]->GetMethod("OnStartLevel")->Invoke(managedObject); break; case ENTITY_EVENT_LEVEL_LOADED: m_pClass[type]->GetMethod("OnLevelLoaded")->Invoke(managedObject); break; case ENTITY_EVENT_ENTERAREA: m_pClass[type]->GetMethod("OnEnterArea", 3)->Call(managedObject, (EntityId)event.nParam[0], (int)event.nParam[1], (EntityId)event.nParam[2]); break; case ENTITY_EVENT_MOVEINSIDEAREA: m_pClass[type]->GetMethod("OnMoveInsideArea", 3)->Call(managedObject, (EntityId)event.nParam[0], (int)event.nParam[1], (EntityId)event.nParam[2]); break; case ENTITY_EVENT_LEAVEAREA: m_pClass[type]->GetMethod("OnLeaveArea", 3)->Call(managedObject, (EntityId)event.nParam[0], (int)event.nParam[1], (EntityId)event.nParam[2]); break; case ENTITY_EVENT_ENTERNEARAREA: m_pClass[type]->GetMethod("OnEnterNearArea", 3)->Call(managedObject, (EntityId)event.nParam[0], (int)event.nParam[1], (EntityId)event.nParam[2]); break; case ENTITY_EVENT_MOVENEARAREA: m_pClass[type]->GetMethod("OnMoveNearArea", 4)->Call(managedObject, (EntityId)event.nParam[0], (int)event.nParam[1], (EntityId)event.nParam[2], event.fParam[0]); break; case ENTITY_EVENT_LEAVENEARAREA: m_pClass[type]->GetMethod("OnLeaveNearArea", 3)->Call(managedObject, (EntityId)event.nParam[0], (int)event.nParam[1], (EntityId)event.nParam[2]); break; case ENTITY_EVENT_XFORM: m_pClass[type]->GetMethod("OnMove", 1)->Call(managedObject, (EEntityXFormFlags)event.nParam[0]); break; case ENTITY_EVENT_ATTACH: m_pClass[type]->GetMethod("OnAttach", 1)->Call(managedObject, (EntityId)event.nParam[0]); break; case ENTITY_EVENT_DETACH: m_pClass[type]->GetMethod("OnDetach", 1)->Call(managedObject, (EntityId)event.nParam[0]); break; case ENTITY_EVENT_DETACH_THIS: m_pClass[type]->GetMethod("OnDetachThis", 1)->Call(managedObject, (EntityId)event.nParam[0]); break; case ENTITY_EVENT_ANIM_EVENT: { const AnimEventInstance* pAnimEvent = reinterpret_cast<const AnimEventInstance*>(event.nParam[0]); ICharacterInstance* pCharacter = reinterpret_cast<ICharacterInstance*>(event.nParam[1]); IMonoClass *pAnimationEventClass = g_pScriptSystem->GetCryBraryAssembly()->GetClass("AnimationEvent"); SMonoAnimationEvent animEvent(pAnimEvent); IMonoArray *pArgs = CreateMonoArray(1); pArgs->InsertMonoObject(pAnimationEventClass->BoxObject(&animEvent)); m_pClass[type]->GetMethod("OnAnimEvent", 1)->InvokeArray(managedObject, pArgs); SAFE_RELEASE(pArgs); } break; case ENTITY_EVENT_PREPHYSICSUPDATE: { m_pClass[type]->GetMethod("OnPrePhysicsUpdate")->Invoke(managedObject); } break; } }