diff --git a/ChaosMod/Components/EffectDispatcher.cpp b/ChaosMod/Components/EffectDispatcher.cpp index 6fadb285e..e4d6db97c 100644 --- a/ChaosMod/Components/EffectDispatcher.cpp +++ b/ChaosMod/Components/EffectDispatcher.cpp @@ -71,7 +71,7 @@ static void _DispatchEffect(EffectDispatcher *effectDispatcher, const EffectDisp if (activeEffect.Identifier == entry.Identifier) { - if (effectData.TimedType != EffectTimedType::Default && effectData.TimedType != EffectTimedType::NotTimed) + if (effectData.TimedType != EffectTimedType::NotTimed) { alreadyExists = true; activeEffect.Timer = activeEffect.MaxTime; @@ -444,9 +444,8 @@ void EffectDispatcher::UpdateEffects(int deltaTime) if (g_EnabledEffects.contains(effect.Identifier)) { auto &effectData = g_EnabledEffects.at(effect.Identifier); - isTimed = - effectData.TimedType != EffectTimedType::NotTimed && effectData.TimedType != EffectTimedType::Default; - isMeta = effectData.IsMeta(); + isTimed = effectData.TimedType != EffectTimedType::NotTimed; + isMeta = effectData.IsMeta(); } if (effect.MaxTime > 0) diff --git a/ChaosMod/Components/EffectDispatcher.h b/ChaosMod/Components/EffectDispatcher.h index a130701b1..70bc478ab 100644 --- a/ChaosMod/Components/EffectDispatcher.h +++ b/ChaosMod/Components/EffectDispatcher.h @@ -54,17 +54,15 @@ class EffectDispatcher : public Component ActiveEffect(const EffectIdentifier &effectIdentifier, RegisteredEffect *registeredEffect, const std::string &name, const EffectData &effectData, float effectDuration) { - Identifier = effectIdentifier; - Name = name; - FakeName = effectData.FakeName; - Timer = effectDuration; - MaxTime = effectDuration; - HideEffectName = effectData.ShouldHideRealNameOnStart(); - - auto eTimedType = g_EnabledEffects.at(effectIdentifier).TimedType; - - ThreadId = EffectThreads::CreateThread(registeredEffect, eTimedType != EffectTimedType::Default - && eTimedType != EffectTimedType::NotTimed); + Identifier = effectIdentifier; + Name = name; + FakeName = effectData.FakeName; + Timer = effectDuration; + MaxTime = effectDuration; + HideEffectName = effectData.ShouldHideRealNameOnStart(); + + auto timedType = g_EnabledEffects.at(effectIdentifier).TimedType; + ThreadId = EffectThreads::CreateThread(registeredEffect, timedType != EffectTimedType::NotTimed); } }; struct diff --git a/ChaosMod/Components/LuaScripts.cpp b/ChaosMod/Components/LuaScripts.cpp index 55cd38271..9eb290bdc 100644 --- a/ChaosMod/Components/LuaScripts.cpp +++ b/ChaosMod/Components/LuaScripts.cpp @@ -7,6 +7,7 @@ #include "Components/EffectDispatcher.h" #include "Components/KeyStates.h" #include "Components/MetaModifiers.h" +#include "Components/Workshop.h" #include "Effects/Effect.h" #include "Effects/EffectData.h" @@ -34,7 +35,6 @@ #include "Util/Types.h" #include "Util/Vehicle.h" #include "Util/Weapon.h" -#include "Util/Workshop.h" #define LUA_NATIVESDEF "chaosmod\\natives_def.lua" @@ -333,9 +333,25 @@ LuaScripts::LuaScripts() LUA_LOG("Running script " << scriptName); } - auto currentThread = std::this_thread::get_id(); - if (ParseScriptRaw(fileName, buffer.str(), - currentThread == mainThread ? ParseScriptFlag_None : ParseScriptFlag_IsAlienThread) + auto currentThread = std::this_thread::get_id(); + int parseScriptFlags = ParseScriptFlag_None; + if (currentThread != mainThread) + { + parseScriptFlags |= ParseScriptFlag_IsAlienThread; + } + + std::unordered_map userEffectSettings; + if (pathStr.starts_with("chaosmod\\workshop") && ComponentExists()) + { + // Read user script settings + auto tmp = pathStr.substr(strlen("chaosmod\\workshop\\")); + userEffectSettings = GetComponent()->GetSubmissionScriptSettings( + pathStr.substr(0, pathStr.find('\\', pathStr.find_first_not_of("chaosmod\\workshop\\"))), + tmp.substr(tmp.find("\\") + 1)); + } + + if (ParseScriptRaw(fileName, buffer.str(), static_cast(parseScriptFlags), + userEffectSettings) == ParseScriptReturnReason::Error_ThreadUnsafe) { std::lock_guard lock(threadUnsafeEntryQueueMutex); @@ -408,10 +424,10 @@ LuaScripts::LuaScripts() { for (const auto &entry : std::filesystem::directory_iterator(dir)) { - if (entry.is_directory()) + if (entry.is_directory() && ComponentExists()) { - for (const auto &entry : - GetWorkshopSubmissionFiles(entry.path().string(), WorkshopFileType::Script)) + for (const auto &entry : GetComponent()->GetSubmissionFiles(entry.path().string(), + Workshop::FileType::Script)) { parseScriptThreaded(entry); } @@ -475,8 +491,9 @@ LuaScripts::~LuaScripts() } } -LuaScripts::ParseScriptReturnReason LuaScripts::ParseScriptRaw(std::string scriptName, std::string_view script, - ParseScriptFlags flags) +LuaScripts::ParseScriptReturnReason +LuaScripts::ParseScriptRaw(std::string scriptName, std::string_view script, ParseScriptFlags flags, + std::unordered_map settingOverrides) { sol::state lua; lua.open_libraries(sol::lib::base); @@ -903,6 +920,24 @@ LuaScripts::ParseScriptReturnReason LuaScripts::ParseScriptRaw(std::string scrip << effectName << "\"!"); } } + try + { + effectData.TimedType = static_cast(settingOverrides["TimedType"]); + } + catch (nlohmann::json::exception) + { + } + try + { + effectData.CustomTime = settingOverrides["CustomTime"]; + if (effectData.CustomTime > 0) + { + effectData.TimedType = EffectTimedType::Custom; + } + } + catch (nlohmann::json::exception) + { + } const sol::optional &weightMultOpt = effectInfo["WeightMultiplier"]; if (weightMultOpt) @@ -910,6 +945,13 @@ LuaScripts::ParseScriptReturnReason LuaScripts::ParseScriptRaw(std::string scrip effectData.WeightMult = (std::max)(1, *weightMultOpt); effectData.Weight = effectData.WeightMult; } + try + { + effectData.WeightMult = settingOverrides["WeightMult"]; + } + catch (nlohmann::json::exception) + { + } const sol::optional &isMetaOpt = effectInfo["IsMeta"]; if (isMetaOpt) @@ -922,6 +964,13 @@ LuaScripts::ParseScriptReturnReason LuaScripts::ParseScriptRaw(std::string scrip { effectData.SetAttribute(EffectAttributes::ExcludedFromVoting, *excludeFromVotingOpt); } + try + { + effectData.SetAttribute(EffectAttributes::ExcludedFromVoting, settingOverrides["ExcludedFromVoting"]); + } + catch (nlohmann::json::exception) + { + } const sol::optional &isUtilityOpt = effectInfo["IsUtility"]; if (isUtilityOpt) @@ -988,6 +1037,25 @@ LuaScripts::ParseScriptReturnReason LuaScripts::ParseScriptRaw(std::string scrip effectData.ShortcutKeycode = shortcutKeycode; } } + try + { + effectData.ShortcutKeycode = settingOverrides["ShortcutKeycode"]; + } + catch (nlohmann::json::exception) + { + } + + try + { + std::string name = StringTrim(settingOverrides["CustomName"]); + if (!name.empty()) + { + effectData.CustomName = name; + } + } + catch (nlohmann::json::exception) + { + } // Exclude temporary effects from choices pool effectData.SetAttribute(EffectAttributes::IsTemporary, flags & ParseScriptFlag_IsTemporary); diff --git a/ChaosMod/Components/LuaScripts.h b/ChaosMod/Components/LuaScripts.h index 01a9b69a9..861f042bc 100644 --- a/ChaosMod/Components/LuaScripts.h +++ b/ChaosMod/Components/LuaScripts.h @@ -8,6 +8,8 @@ #define SOL_SAFE_NUMERICS 1 #include +#include + #include class LuaScripts : public Component @@ -98,7 +100,8 @@ class LuaScripts : public Component Error_ThreadUnsafe }; ParseScriptReturnReason ParseScriptRaw(std::string scriptName, std::string_view script, - ParseScriptFlags flags = ParseScriptFlag_None); + ParseScriptFlags flags = ParseScriptFlag_None, + std::unordered_map settingOverrides = {}); void RemoveScriptEntry(const std::string &effectId); public: diff --git a/ChaosMod/Components/Mp3Manager.cpp b/ChaosMod/Components/Mp3Manager.cpp index 378253912..6b8de64c2 100644 --- a/ChaosMod/Components/Mp3Manager.cpp +++ b/ChaosMod/Components/Mp3Manager.cpp @@ -2,7 +2,7 @@ #include "Mp3Manager.h" -#include "Util/Workshop.h" +#include "Components/Workshop.h" #define CHAOS_SOUNDFILES_USER_DIR "chaosmod" #define CHAOS_SOUNDFILES_WORKSHOP_DIR "chaosmod\\workshop" @@ -30,13 +30,13 @@ void Mp3Manager::HandleDirectory(const std::string &dir, const std::string &soun return; } - auto soundDirPath = soundRootDirName + soundName; - auto soundFilePath = soundDirPath + ".mp3"; + auto soundDirPath = soundRootDirName + soundName; + auto soundFilePath = soundDirPath + ".mp3"; std::vector blacklistedFiles; - if (dir.starts_with(CHAOS_SOUNDFILES_WORKSHOP_DIR)) + if (dir.starts_with(CHAOS_SOUNDFILES_WORKSHOP_DIR) && ComponentExists()) { - blacklistedFiles = GetWorkshopSubmissionBlacklistedFiles(dir); + blacklistedFiles = GetComponent()->GetSubmissionBlacklistedFiles(dir); } auto &soundFiles = m_EffectSoundFilesCache[soundName]; diff --git a/ChaosMod/Components/Workshop.cpp b/ChaosMod/Components/Workshop.cpp new file mode 100644 index 000000000..8fe5c9e43 --- /dev/null +++ b/ChaosMod/Components/Workshop.cpp @@ -0,0 +1,98 @@ +#include + +#include "Workshop.h" + +void Workshop::OnModPauseCleanup() +{ + m_CachedSubmissionSettings.clear(); +} + +nlohmann::json Workshop::GetSubmissionSettingJson(const std::string &submissionPath) +{ + if (m_CachedSubmissionSettings.contains(submissionPath)) + { + return m_CachedSubmissionSettings.at(submissionPath); + } + + auto submissionSettingsFile = submissionPath + ".json"; + if (!DoesFileExist(submissionSettingsFile)) + { + return {}; + } + + std::ifstream file(submissionSettingsFile); + std::stringstream buffer; + buffer << file.rdbuf(); + + try + { + m_CachedSubmissionSettings[submissionPath] = nlohmann::json::parse(buffer.str()); + } + catch (nlohmann::json::exception) + { + m_CachedSubmissionSettings[submissionPath] = {}; + } + + return m_CachedSubmissionSettings.at(submissionPath); +} + +std::vector Workshop::GetSubmissionBlacklistedFiles(const std::string &submissionPath) +{ + std::vector blacklistedFiles; + + auto json = GetSubmissionSettingJson(submissionPath); + try + { + for (const std::string &file : json["disabled_files"]) + { + blacklistedFiles.push_back(file); + } + } + catch (nlohmann::json::exception) + { + } + + return blacklistedFiles; +} + +std::vector Workshop::GetSubmissionFiles(const std::string &submissionPath, + FileType fileType, std::string subPath) +{ + const auto &blacklistedFiles = GetSubmissionBlacklistedFiles(submissionPath); + + std::vector entries; + switch (fileType) + { + case FileType::Script: + entries = GetFiles(submissionPath + "\\" + subPath, ".lua", true, blacklistedFiles); + break; + case FileType::Audio: + entries = GetFiles(submissionPath + "\\" + subPath, ".mp3", true, blacklistedFiles); + break; + default: + return {}; + } + + return entries; +} + +std::unordered_map Workshop::GetSubmissionScriptSettings(const std::string &submissionPath, + const std::string &scriptPath) +{ + std::unordered_map scriptSettings; + + auto json = GetSubmissionSettingJson(submissionPath); + try + { + for (const auto &[key, value] : json["effect_settings"][scriptPath].items()) + { + LOG(key << " " << value); + scriptSettings[key] = value; + } + } + catch (nlohmann::json::exception) + { + } + + return scriptSettings; +} \ No newline at end of file diff --git a/ChaosMod/Components/Workshop.h b/ChaosMod/Components/Workshop.h new file mode 100644 index 000000000..01039092c --- /dev/null +++ b/ChaosMod/Components/Workshop.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Util/File.h" + +#include +#include +#include +#include + +#include + +class Workshop : public Component +{ + private: + std::unordered_map m_CachedSubmissionSettings; + + private: + nlohmann::json GetSubmissionSettingJson(const std::string &submissionPath); + + public: + void OnModPauseCleanup() override; + + std::vector GetSubmissionBlacklistedFiles(const std::string &submissionPath); + enum class FileType + { + Script, + Audio + }; + std::vector GetSubmissionFiles(const std::string &submissionPath, + FileType fileType, std::string subPath = ""); + + std::unordered_map GetSubmissionScriptSettings(const std::string &submissionPath, + const std::string &scriptPath); + + template + requires std::is_base_of_v + friend struct ComponentHolder; +}; \ No newline at end of file diff --git a/ChaosMod/Effects/EffectConfig.h b/ChaosMod/Effects/EffectConfig.h index f133dccf8..11cbd55fa 100644 --- a/ChaosMod/Effects/EffectConfig.h +++ b/ChaosMod/Effects/EffectConfig.h @@ -43,10 +43,24 @@ namespace EffectConfig for (auto &[effectId, effectInfo] : g_EffectsMap) { - // Default EffectData values - // Enabled, TimedType, CustomTime (-1 = Disabled), Weight, Permanent, ExcludedFromVoting, "Dummy for - // name-override", Shortcut - std::vector values = { true, static_cast(EffectTimedType::Default), -1, 5, false, false, 0, 0 }; + struct + { + union + { + std::array Values; + struct + { + bool Enabled; + EffectTimedType TimedType; + int CustomTime; + int WeightMult; + bool Permanent; + bool ExcludedFromVoting; + char Placeholder; + int ShortcutKeycode; + }; + }; + } configValues; // HACK: Store EffectCustomName seperately std::string valueEffectName; @@ -75,7 +89,7 @@ namespace EffectConfig { const auto &split = value.substr(0, splitIndex); - Util::TryParse(split, values[j]); + Util::TryParse(split, configValues.Values[j]); } if (splitIndex == value.npos) @@ -88,7 +102,7 @@ namespace EffectConfig } } - if (!values[0]) // enabled == false + if (!configValues.Enabled) { continue; } @@ -98,32 +112,37 @@ namespace EffectConfig { effectData.TimedType = EffectTimedType::NotTimed; } - else if (values[4]) + else if (configValues.Permanent) { effectData.TimedType = EffectTimedType::Permanent; } - else if (values[2] > -1) + else if (configValues.CustomTime > 0) { effectData.TimedType = EffectTimedType::Custom; - effectData.CustomTime = values[2]; + effectData.CustomTime = configValues.CustomTime; } else { - effectData.TimedType = static_cast( - static_cast(values[1]) == EffectTimedType::Default ? effectInfo.IsShortDuration - : values[1]); + effectData.TimedType = + configValues.TimedType == EffectTimedType::NotTimed + ? (effectInfo.IsShortDuration ? EffectTimedType::Short : EffectTimedType::Normal) + : configValues.TimedType; } - effectData.WeightMult = values[3]; - effectData.Weight = effectData.WeightMult; // Set initial effect weight to WeightMult - effectData.SetAttribute(EffectAttributes::ExcludedFromVoting, values[5]); + if (configValues.WeightMult) + { + effectData.WeightMult = configValues.WeightMult; + } + effectData.Weight = effectData.WeightMult; // Set initial effect weight to WeightMult + effectData.SetAttribute(EffectAttributes::ExcludedFromVoting, configValues.ExcludedFromVoting); effectData.SetAttribute(EffectAttributes::IsMeta, effectInfo.ExecutionType == EffectExecutionType::Meta); effectData.Name = effectInfo.Name; effectData.SetAttribute(EffectAttributes::HideRealNameOnStart, effectInfo.HideRealNameOnStart); #ifdef _DEBUG - effectData.ShortcutKeycode = effectInfo.DebugShortcutKeycode ? effectInfo.DebugShortcutKeycode : values[7]; + effectData.ShortcutKeycode = + effectInfo.DebugShortcutKeycode ? effectInfo.DebugShortcutKeycode : configValues.ShortcutKeycode; #else - effectData.ShortcutKeycode = values[7]; + effectData.ShortcutKeycode = configValues.ShortcutKeycode; #endif if (!valueEffectName.empty()) { diff --git a/ChaosMod/Effects/EffectData.h b/ChaosMod/Effects/EffectData.h index 7106b2f6c..2c6b19a7c 100644 --- a/ChaosMod/Effects/EffectData.h +++ b/ChaosMod/Effects/EffectData.h @@ -22,7 +22,7 @@ struct EffectData int CustomTime = -1; int WeightMult = 5; int ShortcutKeycode = 0; - EffectTimedType TimedType = EffectTimedType::Default; + EffectTimedType TimedType = EffectTimedType::NotTimed; EffectCategory EffectCategory = EffectCategory::None; private: diff --git a/ChaosMod/Effects/EffectTimedType.h b/ChaosMod/Effects/EffectTimedType.h index f78f0de1d..78f28f6a3 100644 --- a/ChaosMod/Effects/EffectTimedType.h +++ b/ChaosMod/Effects/EffectTimedType.h @@ -2,10 +2,9 @@ enum class EffectTimedType { - NotTimed = -4, - Permanent, + Permanent = -3, Custom, - Default, + NotTimed, Normal, Short }; \ No newline at end of file diff --git a/ChaosMod/Main.cpp b/ChaosMod/Main.cpp index b1cc94c37..dba39f0ef 100644 --- a/ChaosMod/Main.cpp +++ b/ChaosMod/Main.cpp @@ -21,6 +21,7 @@ #include "Components/Mp3Manager.h" #include "Components/SplashTexts.h" #include "Components/Voting.h" +#include "Components/Workshop.h" #include "Util/File.h" #include "Util/OptionsManager.h" @@ -192,6 +193,8 @@ static void Init() INIT_COMPONENT("SplashTexts", "mod splash texts handler", SplashTexts); + INIT_COMPONENT("Workshop", "workshop", Workshop); + INIT_COMPONENT("Mp3Manager", "effect sound system", Mp3Manager); INIT_COMPONENT("MetaModifiers", "meta modifier states", MetaModifiers); diff --git a/ChaosMod/Util/Peds.h b/ChaosMod/Util/Peds.h index 426b49489..1e8d47119 100644 --- a/ChaosMod/Util/Peds.h +++ b/ChaosMod/Util/Peds.h @@ -1,6 +1,6 @@ #pragma once -#include "../Util/Natives.h" +#include "Util/Natives.h" #include "PoolSpawner.h" diff --git a/ChaosMod/Util/Workshop.h b/ChaosMod/Util/Workshop.h deleted file mode 100644 index e0a093fa0..000000000 --- a/ChaosMod/Util/Workshop.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "Util/File.h" - -#include -#include -#include - -#include - -enum class WorkshopFileType -{ - Script, - Audio -}; - -inline std::vector GetWorkshopSubmissionBlacklistedFiles(const std::string &submissionPath) -{ - auto submissionSettingsFile = submissionPath + ".json"; - std::vector blacklistedFiles; - if (DoesFileExist(submissionSettingsFile)) - { - try - { - std::ifstream fileStream(submissionSettingsFile); - std::stringstream buffer; - buffer << fileStream.rdbuf(); - - auto submissionSettingsJson = nlohmann::json::parse(buffer); - for (const std::string &file : submissionSettingsJson["disabled_files"]) - { - blacklistedFiles.push_back(file); - } - } - catch (nlohmann::json::exception) - { - } - } - - return blacklistedFiles; -} - -inline std::vector -GetWorkshopSubmissionFiles(const std::string &submissionPath, WorkshopFileType fileType, std::string subPath = "") -{ - const auto &blacklistedFiles = GetWorkshopSubmissionBlacklistedFiles(submissionPath); - - std::vector entries; - switch (fileType) - { - case WorkshopFileType::Script: - entries = GetFiles(submissionPath + "\\" + subPath, ".lua", true, blacklistedFiles); - break; - case WorkshopFileType::Audio: - entries = GetFiles(submissionPath + "\\" + subPath, ".mp3", true, blacklistedFiles); - break; - default: - return {}; - } - - return entries; -} \ No newline at end of file diff --git a/ChaosMod/stdafx.h b/ChaosMod/stdafx.h index a448f5aa3..ce1a324c5 100644 --- a/ChaosMod/stdafx.h +++ b/ChaosMod/stdafx.h @@ -13,14 +13,18 @@ #include "Util/TryParse.h" #include +#include #include #include #include + #define SOL_ALL_SAFETIES_ON 1 #define SOL_SAFE_NUMERICS 1 #include #define WIN32_LEAN_AND_MEAN +#include + #include #include #include @@ -28,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/ConfigApp/EffectConfig.xaml b/ConfigApp/EffectConfig.xaml index 4262730ba..d2b889b3c 100644 --- a/ConfigApp/EffectConfig.xaml +++ b/ConfigApp/EffectConfig.xaml @@ -47,30 +47,25 @@ IsEnabled="False" MaxLength="6" PreviewTextInput="OnlyNumbersPreviewTextInput" CommandManager.PreviewExecuted="NoCopyPastePreviewExecuted" ContextMenu="{x:Null}" /> -