diff --git a/source/main/Application.h b/source/main/Application.h index 61379346c9..28aeff713d 100644 --- a/source/main/Application.h +++ b/source/main/Application.h @@ -91,12 +91,13 @@ enum MsgType // GUI MSG_GUI_OPEN_MENU_REQUESTED, MSG_GUI_CLOSE_MENU_REQUESTED, - MSG_GUI_OPEN_SELECTOR_REQUESTED, //!< Payload = LoaderType* (owner), Description = GUID | empty + MSG_GUI_OPEN_SELECTOR_REQUESTED, //!< Payload = LoaderType* (owner), Description = GUID | empty MSG_GUI_CLOSE_SELECTOR_REQUESTED, // Editing MSG_EDI_MODIFY_GROUNDMODEL_REQUESTED, //!< Payload = ground_model_t* (weak) MSG_EDI_ENTER_TERRN_EDITOR_REQUESTED, MSG_EDI_LEAVE_TERRN_EDITOR_REQUESTED, + MSG_EDI_RELOAD_BUNDLE_REQUESTED, //!< Payload = RoR::CacheEntry* (weak) }; enum class AppState diff --git a/source/main/GameContext.cpp b/source/main/GameContext.cpp index 21d4b608e1..c8ef9c65b1 100644 --- a/source/main/GameContext.cpp +++ b/source/main/GameContext.cpp @@ -279,28 +279,21 @@ void GameContext::ModifyActor(ActorModifyRequest& rq) return; } - auto reload_pos = rq.amr_actor->getPosition(); - auto reload_dir = Ogre::Quaternion(Ogre::Degree(270) - Ogre::Radian(m_player_actor->getRotation()), Ogre::Vector3::UNIT_Y); - auto debug_view = rq.amr_actor->GetGfxActor()->GetDebugView(); - auto asr_config = rq.amr_actor->GetSectionConfig(); - auto used_skin = rq.amr_actor->GetUsedSkin(); - - reload_pos.y = m_player_actor->GetMinHeight(); - - m_prev_player_actor = nullptr; - this->DeleteActor(rq.amr_actor); - entry->actor_def = nullptr; - App::GetCacheSystem()->ReLoadResource(*entry); - + // Create spawn request while actor still exists ActorSpawnRequest* srq = new ActorSpawnRequest; - srq->asr_position = reload_pos; - srq->asr_rotation = reload_dir; - srq->asr_config = asr_config; - srq->asr_skin_entry = used_skin; + srq->asr_position = Ogre::Vector3(rq.amr_actor->getPosition().x, rq.amr_actor->GetMinHeight(), rq.amr_actor->getPosition().z); + srq->asr_rotation = Ogre::Quaternion(Ogre::Degree(270) - Ogre::Radian(rq.amr_actor->getRotation()), Ogre::Vector3::UNIT_Y); + srq->asr_config = rq.amr_actor->GetSectionConfig(); + srq->asr_skin_entry = rq.amr_actor->GetUsedSkin(); srq->asr_cache_entry= entry; - srq->asr_debugview = (int)debug_view; + srq->asr_debugview = (int)rq.amr_actor->GetGfxActor()->GetDebugView(); srq->asr_origin = ActorSpawnRequest::Origin::USER; - this->PushMessage(Message(MSG_SIM_SPAWN_ACTOR_REQUESTED, (void*)srq)); + + // This deletes all actors using the resource bundle, including the one we're reloading. + this->PushMessage(Message(MSG_EDI_RELOAD_BUNDLE_REQUESTED, (void*)entry)); + + // Load our actor again, but only after all actors are deleted. + this->ChainMessage(Message(MSG_SIM_SPAWN_ACTOR_REQUESTED, (void*)srq)); } } diff --git a/source/main/main.cpp b/source/main/main.cpp index b637aed193..e9e779652c 100644 --- a/source/main/main.cpp +++ b/source/main/main.cpp @@ -676,6 +676,33 @@ int main(int argc, char *argv[]) } break; + case MSG_EDI_RELOAD_BUNDLE_REQUESTED: + { + // To reload the bundle, it's resource group must be destroyed and re-created. All actors using it must be deleted. + CacheEntry* entry = reinterpret_cast(m.payload); + bool all_clear = true; + for (Actor* actor: App::GetGameContext()->GetActorManager()->GetActors()) + { + if (actor->GetGfxActor()->GetResourceGroup() == entry->resource_group) + { + App::GetGameContext()->PushMessage(Message(MSG_SIM_DELETE_ACTOR_REQUESTED, actor)); + all_clear = false; + } + } + + if (all_clear) + { + // Nobody uses the RG anymore -> destroy and re-create it. + App::GetCacheSystem()->ReLoadResource(*entry); + } + else + { + // Re-post the same message again so that it's message chain is executed later. + App::GetGameContext()->PushMessage(m); + failed_m = true; + } + } + default:; } diff --git a/source/main/resources/CacheSystem.cpp b/source/main/resources/CacheSystem.cpp index 228a666f89..9e7a7d509f 100644 --- a/source/main/resources/CacheSystem.cpp +++ b/source/main/resources/CacheSystem.cpp @@ -1116,8 +1116,32 @@ void CacheSystem::ReLoadResource(CacheEntry& t) return; // Not loaded - nothing to do } - Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(t.resource_group); - t.resource_group = ""; + // IMPORTANT! No actors must use the bundle while reloading, use RoR::MsgType::MSG_EDI_RELOAD_BUNDLE_REQUESTED + + this->UnLoadResource(t); + this->LoadResource(t); // Will create the same resource group again +} + +void CacheSystem::UnLoadResource(CacheEntry& t) +{ + if (t.resource_group == "") + { + return; // Not loaded - nothing to do + } + + // IMPORTANT! No actors must use the bundle after reloading, use RoR::MsgType::MSG_EDI_RELOAD_BUNDLE_REQUESTED + + std::string resource_group = t.resource_group; // Keep local copy, the CacheEntry will be blanked! + for (CacheEntry& i_entry: m_entries) + { + if (i_entry.resource_group == resource_group) + { + i_entry.actor_def = nullptr; // Delete cached truck file - force reload from disk + i_entry.resource_group = ""; // Mark as unloaded + } + } + + Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(resource_group); this->LoadResource(t); // Will create the same resource group again } diff --git a/source/main/resources/CacheSystem.h b/source/main/resources/CacheSystem.h index 186d56283c..6ed3ed5c28 100644 --- a/source/main/resources/CacheSystem.h +++ b/source/main/resources/CacheSystem.h @@ -203,6 +203,7 @@ class CacheSystem : public ZeroedMemoryAllocator bool CheckResourceLoaded(Ogre::String &in_out_filename); //!< Finds + loads the associated resource bundle if not already done. bool CheckResourceLoaded(Ogre::String &in_out_filename, Ogre::String &out_group); //!< Finds given resource, outputs group name. Also loads the associated resource bundle if not already done. void ReLoadResource(CacheEntry& t); //!< Forces reloading the associated bundle. + void UnLoadResource(CacheEntry& t); //!< Unloads associated bundle, destroying all spawned actors. const std::vector &GetEntries() const { return m_entries; } const CategoryIdNameMap &GetCategories() const { return m_categories; }