From 9cb80daedf10955eaacdccedde6a679d29f085b3 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Mon, 27 Jul 2020 15:59:33 +0200 Subject: [PATCH] Improve sound reloading Also some minor refactoring. Since realoding may take a lot of time, modman no longer reloads the whole app on entering/leaving the screen. --- src/app/app.cpp | 5 +- src/app/modman.cpp | 78 +++++++++++++++++++------ src/app/modman.h | 32 +++++++---- src/sound/oalsound/alsound.cpp | 35 +++++------ src/sound/oalsound/alsound.h | 1 + src/sound/sound.cpp | 4 ++ src/sound/sound.h | 4 ++ src/ui/screen/screen_mod_list.cpp | 96 ++++++++++++++----------------- src/ui/screen/screen_mod_list.h | 32 +---------- 9 files changed, 157 insertions(+), 130 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 72c2c2e1..ecd8cc1b 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -520,7 +520,9 @@ bool CApplication::Create() GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } - m_modManager->Init(); + m_modManager->FindMods(); + m_modManager->SaveMods(); + m_modManager->MountAllMods(); // Create the sound instance. #ifdef OPENAL_SOUND @@ -1551,6 +1553,7 @@ void CApplication::StartLoadingMusic() SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); m_systemUtils->GetCurrentTimeStamp(musicLoadStart); + m_sound->Reset(); m_sound->CacheAll(); SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); diff --git a/src/app/modman.cpp b/src/app/modman.cpp index 033ae458..42ba11be 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -44,8 +44,11 @@ CModManager::CModManager(CApplication* app, CPathManager* pathManager) { } -void CModManager::Init() +void CModManager::FindMods() { + m_mods.clear(); + m_userChanges = false; + // Load names from the config file std::vector savedModNames; GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames); @@ -103,9 +106,15 @@ void CModManager::Init() m_mods.push_back(mod); } - SaveMods(); - // Load the metadata for each mod + + // Unfortunately, the paths are distinguished by their real paths, not mount points + // So we must unmount mods temporarily + for (const auto& path : m_mountedModPaths) + { + UnmountMod(path); + } + for (auto& mod : m_mods) { MountMod(mod, "/temp/mod"); @@ -113,28 +122,30 @@ void CModManager::Init() UnmountMod(mod); } - UpdatePaths(); + // Mount back + for (const auto& path : m_mountedModPaths) + { + MountMod(path); + } } void CModManager::ReloadMods() { - UnmountAllMods(); - m_mods.clear(); - - Init(); - - // Apply the configuration + UnmountAllMountedMods(); + MountAllMods(); ReloadResources(); } void CModManager::EnableMod(size_t i) { m_mods[i].enabled = true; + m_userChanges = true; } void CModManager::DisableMod(size_t i) { m_mods[i].enabled = false; + m_userChanges = true; } size_t CModManager::MoveUp(size_t i) @@ -142,6 +153,7 @@ size_t CModManager::MoveUp(size_t i) if (i != 0) { std::swap(m_mods[i - 1], m_mods[i]); + m_userChanges = true; return i - 1; } else @@ -155,6 +167,7 @@ size_t CModManager::MoveDown(size_t i) if (i != m_mods.size() - 1) { std::swap(m_mods[i], m_mods[i + 1]); + m_userChanges = true; return i + 1; } else @@ -163,13 +176,27 @@ size_t CModManager::MoveDown(size_t i) } } -void CModManager::UpdatePaths() +bool CModManager::Changes() +{ + std::vector paths; + for (const auto& mod : m_mods) + { + if (mod.enabled) + { + paths.push_back(mod.path); + } + } + return paths != m_mountedModPaths || m_userChanges; +} + +void CModManager::MountAllMods() { for (const auto& mod : m_mods) { if (mod.enabled) { MountMod(mod); + m_mountedModPaths.push_back(mod.path); } } } @@ -192,6 +219,8 @@ void CModManager::SaveMods() GetConfigFile().SetArrayProperty("Mods", "Enabled", savedEnabled); GetConfigFile().Save(); + + m_userChanges = false; } size_t CModManager::CountMods() const @@ -296,23 +325,34 @@ void CModManager::LoadModData(Mod& mod) void CModManager::MountMod(const Mod& mod, const std::string& mountPoint) { - GetLogger()->Debug("Mounting mod: '%s' at path %s\n", mod.path.c_str(), mountPoint.c_str()); - CResourceManager::AddLocation(mod.path, true, mountPoint); + MountMod(mod.path, mountPoint); +} + +void CModManager::MountMod(const std::string& path, const std::string& mountPoint) +{ + GetLogger()->Debug("Mounting mod: '%s' at path %s\n", path.c_str(), mountPoint.c_str()); + CResourceManager::AddLocation(path, true, mountPoint); } void CModManager::UnmountMod(const Mod& mod) { - if (CResourceManager::LocationExists(mod.path)) + UnmountMod(mod.path); +} + +void CModManager::UnmountMod(const std::string& path) +{ + if (CResourceManager::LocationExists(path)) { - GetLogger()->Debug("Unmounting mod: '%s'\n", mod.path.c_str()); - CResourceManager::RemoveLocation(mod.path); + GetLogger()->Debug("Unmounting mod: '%s'\n", path.c_str()); + CResourceManager::RemoveLocation(path); } } -void CModManager::UnmountAllMods() +void CModManager::UnmountAllMountedMods() { - for (const auto& mod : m_mods) + for (const auto& path : m_mountedModPaths) { - UnmountMod(mod); + UnmountMod(path); } + m_mountedModPaths.clear(); } diff --git a/src/app/modman.h b/src/app/modman.h index a8af565e..09f8b0aa 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -51,21 +51,17 @@ struct Mod * The order matters since the order in which files are loaded matters, * because some files can be overwritten. * - * The changes in the list do not immediately apply - * and will be lost after the \ref ReloadMods call if they were not - * saved beforehand with \ref SaveMods. - * - * The changes are also lost to mods which are no longer found in the search paths. + * The changes in the list do not immediately apply. */ class CModManager { public: CModManager(CApplication* app, CPathManager* pathManager); - //! Loads mods without resource reloading; should be called only once after creation - void Init(); - //! Finds all the mods along with their metadata + void FindMods(); + + //! Applies the current configuration and reloads the application void ReloadMods(); //! Removes a mod from the list of loaded mods @@ -80,6 +76,9 @@ public: //! Moves the selected mod down in the list so that it's loaded later than others, returns the new index size_t MoveDown(size_t i); + //! Checks if the list of currently used mods differs from the current configuration or there were changes made by the user + bool Changes(); + //! Saves the current configuration of mods to the config file void SaveMods(); @@ -93,8 +92,8 @@ public: const std::vector& GetMods() const; private: - //! Updates the paths in Path Manager according to the current mod configuration - void UpdatePaths(); + // Allow access to MountAllMods() as CApplication doesn't want to reload itself during initialization + friend CApplication; //! Reloads application resources so the enabled mods are applied void ReloadResources(); @@ -102,13 +101,24 @@ private: //! Load mod data into mod void LoadModData(Mod& mod); + //! Updates the paths in Path Manager according to the current mod configuration + void MountAllMods(); + void MountMod(const Mod& mod, const std::string& mountPoint = ""); + void MountMod(const std::string& path, const std::string& mountPoint = ""); void UnmountMod(const Mod& mod); - void UnmountAllMods(); + void UnmountMod(const std::string& path); + void UnmountAllMountedMods(); private: CApplication* m_app; CPathManager* m_pathManager; + //! Paths to mods already in the virtual filesystem + std::vector m_mountedModPaths; + + //! List of mods std::vector m_mods; + + bool m_userChanges = false; }; diff --git a/src/sound/oalsound/alsound.cpp b/src/sound/oalsound/alsound.cpp index f27a462b..26006198 100644 --- a/src/sound/oalsound/alsound.cpp +++ b/src/sound/oalsound/alsound.cpp @@ -47,22 +47,7 @@ void CALSound::CleanUp() if (m_enabled) { GetLogger()->Info("Unloading files and closing device...\n"); - StopAll(); - StopMusic(); - - m_channels.clear(); - - m_currentMusic.reset(); - - m_oldMusic.clear(); - - m_previousMusic.music.reset(); - - m_sounds.clear(); - - m_music.clear(); - - m_enabled = false; + Reset(); alcDestroyContext(m_context); alcCloseDevice(m_device); @@ -99,6 +84,24 @@ bool CALSound::Create() return true; } +void CALSound::Reset() +{ + StopAll(); + StopMusic(); + + m_channels.clear(); + + m_currentMusic.reset(); + + m_oldMusic.clear(); + + m_previousMusic.music.reset(); + + m_sounds.clear(); + + m_music.clear(); +} + bool CALSound::GetEnable() { return m_enabled; diff --git a/src/sound/oalsound/alsound.h b/src/sound/oalsound/alsound.h index 6f76c268..75e3d7f6 100644 --- a/src/sound/oalsound/alsound.h +++ b/src/sound/oalsound/alsound.h @@ -84,6 +84,7 @@ public: ~CALSound(); bool Create() override; + void Reset() override; bool Cache(SoundType, const std::string &) override; void CacheMusic(const std::string &) override; bool IsCached(SoundType) override; diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp index 1cc23c4b..87d7266c 100644 --- a/src/sound/sound.cpp +++ b/src/sound/sound.cpp @@ -52,6 +52,10 @@ void CSoundInterface::CacheAll() } } +void CSoundInterface::Reset() +{ +} + bool CSoundInterface::Cache(SoundType sound, const std::string &file) { return true; diff --git a/src/sound/sound.h b/src/sound/sound.h index 78a5449d..314eb856 100644 --- a/src/sound/sound.h +++ b/src/sound/sound.h @@ -72,6 +72,10 @@ public: */ void CacheAll(); + /** Stop all sounds and music and clean cache. + */ + virtual void Reset(); + /** Function called to cache sound effect file. * This function is called by plugin interface for each file. * \param sound - id of a file, will be used to identify sound files diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index f151f781..856f2a20 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -36,6 +36,8 @@ #include "math/func.h" +#include "sound/sound.h" + #include "ui/controls/button.h" #include "ui/controls/edit.h" #include "ui/controls/interface.h" @@ -64,9 +66,6 @@ void CScreenModList::CreateInterface() Math::Point pos, ddim; std::string name; - m_changes = false; - ApplyChanges(); - // Display the window pos.x = 0.10f; pos.y = 0.10f; @@ -104,8 +103,6 @@ void CScreenModList::CreateInterface() pli->SetState(STATE_SHADOW); pli->SetState(STATE_EXTEND); - UpdateModList(); - // Displays the mod details pos.x = ox+sx*9.5f; pos.y = oy+sy*10.5f; @@ -124,8 +121,6 @@ void CScreenModList::CreateInterface() pe->SetEditCap(false); // just to see pe->SetHighlightCap(true); - UpdateModDetails(); - pos = pli->GetPos(); ddim = pli->GetDim(); @@ -148,8 +143,6 @@ void CScreenModList::CreateInterface() pe->SetEditCap(false); // just to see pe->SetHighlightCap(true); - UpdateModSummary(); - // Apply button pos.x = ox+sx*13.75f; pos.y = oy+sy*2; @@ -158,16 +151,12 @@ void CScreenModList::CreateInterface() pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MODS_APPLY); pb->SetState(STATE_SHADOW); - UpdateApplyButton(); - // Display the enable/disable button pos.x -= dim.x*2.3f; ddim.x = dim.x*2.0f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE); pb->SetState(STATE_SHADOW); - UpdateEnableDisableButton(); - // Display the move up button pos.x -= dim.x*0.8f; pos.y = oy+sy*2.48; @@ -181,8 +170,6 @@ void CScreenModList::CreateInterface() pb = pw->CreateButton(pos, ddim, 50, EVENT_INTERFACE_MOD_MOVE_DOWN); pb->SetState(STATE_SHADOW); - UpdateUpDownButtons(); - // Display the refresh button pos.x -= dim.x*1.3f; pos.y = oy+sy*2; @@ -207,6 +194,9 @@ void CScreenModList::CreateInterface() pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); pb->SetState(STATE_SHADOW); + FindMods(); + UpdateAll(); + // Background SetBackground("textures/interface/interface.png"); CreateVersionDisplay(); @@ -231,7 +221,7 @@ bool CScreenModList::EventProcess(const Event &event) event.type == EVENT_INTERFACE_BACK || (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE))) { - if (m_changes) + if (m_modManager->Changes()) { m_dialog->StartQuestion(RT_DIALOG_CHANGES_QUESTION, true, true, false, [this]() @@ -241,14 +231,11 @@ bool CScreenModList::EventProcess(const Event &event) }, [this]() { - m_changes = false; // do not save changes on "No" - ApplyChanges(); CloseWindow(); }); } else { - ApplyChanges(); CloseWindow(); } return false; @@ -278,7 +265,6 @@ bool CScreenModList::EventProcess(const Event &event) } UpdateModList(); UpdateEnableDisableButton(); - m_changes = true; UpdateApplyButton(); break; @@ -286,7 +272,6 @@ bool CScreenModList::EventProcess(const Event &event) m_modSelectedIndex = m_modManager->MoveUp(m_modSelectedIndex); UpdateModList(); UpdateUpDownButtons(); - m_changes = true; UpdateApplyButton(); break; @@ -294,20 +279,21 @@ bool CScreenModList::EventProcess(const Event &event) m_modSelectedIndex = m_modManager->MoveDown(m_modSelectedIndex); UpdateModList(); UpdateUpDownButtons(); - m_changes = true; UpdateApplyButton(); break; case EVENT_INTERFACE_MODS_REFRESH: + // Apply any changes before refresh so that the config file + // is better synchronized with the state of the game case EVENT_INTERFACE_MODS_APPLY: ApplyChanges(); - // Update the whole UI - UpdateModList(); - UpdateModSummary(); - UpdateModDetails(); - UpdateEnableDisableButton(); - UpdateApplyButton(); - UpdateUpDownButtons(); + UpdateAll(); + // Start playing the main menu music again + if (!m_app->GetSound()->IsPlayingMusic()) + { + m_app->GetSound()->PlayMusic("music/Intro1.ogg", false); + m_app->GetSound()->CacheMusic("music/Intro2.ogg"); + } break; case EVENT_INTERFACE_MODS_DIR: @@ -341,19 +327,19 @@ bool CScreenModList::EventProcess(const Event &event) return false; } +void CScreenModList::FindMods() +{ + m_modManager->FindMods(); + if (m_modManager->CountMods() != 0) + { + m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast(0), m_modManager->CountMods() - 1); + } +} + void CScreenModList::ApplyChanges() { - if (m_changes) - { - m_changes = false; - m_modManager->SaveMods(); - } - + m_modManager->SaveMods(); m_modManager->ReloadMods(); - - m_empty = (m_modManager->CountMods() == 0); - - m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast(0), m_modManager->CountMods() - 1); } void CScreenModList::CloseWindow() @@ -361,6 +347,16 @@ void CScreenModList::CloseWindow() m_main->ChangePhase(PHASE_MAIN_MENU); } +void CScreenModList::UpdateAll() +{ + UpdateModList(); + UpdateModDetails(); + UpdateModSummary(); + UpdateEnableDisableButton(); + UpdateApplyButton(); + UpdateUpDownButtons(); +} + void CScreenModList::UpdateModList() { CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); @@ -371,16 +367,16 @@ void CScreenModList::UpdateModList() pl->Flush(); - if (m_empty) + if (m_modManager->CountMods() == 0) { return; } - auto mods = m_modManager->GetMods(); + const auto& mods = m_modManager->GetMods(); for (size_t i = 0; i < mods.size(); ++i) { const auto& mod = mods[i]; - auto name = mod.data.displayName; + const auto& name = mod.data.displayName; pl->SetItemName(i, name); pl->SetCheck(i, mod.enabled); pl->SetEnable(i, true); @@ -398,7 +394,7 @@ void CScreenModList::UpdateModDetails() CEdit* pe = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_DETAILS)); if (pe == nullptr) return; - if (m_empty) + if (m_modManager->CountMods() == 0) { pe->SetText("No information"); return; @@ -475,7 +471,7 @@ void CScreenModList::UpdateModSummary() std::string noSummary; GetResource(RES_TEXT, RT_MOD_NO_SUMMARY, noSummary); - if (m_empty) + if (m_modManager->CountMods() == 0) { pe->SetText(noSummary); return; @@ -503,7 +499,7 @@ void CScreenModList::UpdateEnableDisableButton() std::string buttonName{}; - if (m_empty) + if (m_modManager->CountMods() == 0) { pb->ClearState(STATE_ENABLE); @@ -536,13 +532,7 @@ void CScreenModList::UpdateApplyButton() CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_APPLY)); if (pb == nullptr) return; - if (m_empty) - { - pb->ClearState(STATE_ENABLE); - return; - } - - if (m_changes) + if (m_modManager->Changes()) { pb->SetState(STATE_ENABLE); } @@ -563,7 +553,7 @@ void CScreenModList::UpdateUpDownButtons() CButton* pb_down = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_MOVE_DOWN)); if (pb_down == nullptr) return; - if (m_empty) + if (m_modManager->CountMods() == 0) { pb_up->ClearState(STATE_ENABLE); pb_down->ClearState(STATE_ENABLE); diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index 639fd775..ca2c130b 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -33,34 +33,6 @@ namespace Ui /** * \class CScreenModList * \brief This class is the front-end for the \ref CModManager. - * - * \section Assumptions Assumptions - * - * It assumes the user is changing something in the mods folders while the screen is visible, - * e.g. removing them or adding more. For this reason, the mods are always reloaded after the user - * lefts the screen, possibly after asking the user if their changes should be saved. They are also reloaded - * when the user opens the screen to avoid weird situations like "the mod is no longer there but in theory - * it's still in the game's memory even if it's not visible on the list". - * - * Unsafe changes, like removing a mod which is still enabled, are a sole responsibility of the user and - * we basically can't guarantee the game will behave properly in such cases even when they happen while - * this screen is visible. - * - * \section Features UI Features - * - * The user can reorder mods with appropriate buttons and enable/disable them. To avoid reloading - * the resources after every change, the changes are not immediate. The resources are reloaded in the - * cases described above and also after using the Apply or Refresh buttons. The only technical - * difference between them is that the Refresh button is always enabled, but Apply is only enabled - * if the user made any changes in the list by using the UI. The reason is, again, to avoid dealing with - * weird situations like described above. - * - * The UI also shows the selected mod metadata like description, version, etc. - * - * There is also a button which will try to open the default web browser with the Workshop website, - * where the user can search for new mods. - * - * For convenience, also a button opening a saves/mods folder is provided. */ class CScreenModList : public CScreen { @@ -71,9 +43,11 @@ public: bool EventProcess(const Event &event) override; protected: + void FindMods(); void ApplyChanges(); void CloseWindow(); + void UpdateAll(); void UpdateModList(); void UpdateModDetails(); void UpdateModSummary(); @@ -87,8 +61,6 @@ protected: CModManager* m_modManager; size_t m_modSelectedIndex = 0; - bool m_changes = false; - bool m_empty = true; }; } // namespace Ui