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.
pyro-refactor
MrSimbax 2020-07-27 15:59:33 +02:00
parent fd36ff3840
commit 9cb80daedf
9 changed files with 157 additions and 130 deletions

View File

@ -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();

View File

@ -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<std::string> 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<std::string> 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();
}

View File

@ -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<Mod>& 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<std::string> m_mountedModPaths;
//! List of mods
std::vector<Mod> m_mods;
bool m_userChanges = false;
};

View File

@ -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;

View File

@ -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;

View File

@ -52,6 +52,10 @@ void CSoundInterface::CacheAll()
}
}
void CSoundInterface::Reset()
{
}
bool CSoundInterface::Cache(SoundType sound, const std::string &file)
{
return true;

View File

@ -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

View File

@ -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<KeyEventData>()->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<size_t>(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<size_t>(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<CWindow*>(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<CEdit*>(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<CButton*>(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<CButton*>(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);

View File

@ -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