mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-19 15:51:45 +00:00
Large config file upgrade - backend rewrite; config file change monitoring; setting changed events propagate from ConfigFile; added unit tests and Test configuration
This commit is contained in:
1 parent
ad1c9bd7e1
commit
65177c419e
13 files changed
+916
-85
No files matched your search
+123
@@ -27,44 +27,167 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.RuntimeDetour", "su
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Utils", "submodules\MonoMod\MonoMod.Utils\MonoMod.Utils.csproj", "{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInExTests", "BepInExTests\BepInExTests.csproj", "{E7CD429A-D057-48E3-8C51-E5C934E8E07B}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9} = {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|Test = Debug|Test
|
||||
Legacy|Any CPU = Legacy|Any CPU
|
||||
Legacy|Test = Legacy|Test
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|Test = Release|Test
|
||||
v2018|Any CPU = v2018|Any CPU
|
||||
v2018|Test = v2018|Test
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Any CPU.Build.0 = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Test.ActiveCfg = v2018|x86
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Test.Build.0 = v2018|x86
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Any CPU.Build.0 = Legacy|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Test.ActiveCfg = Legacy|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Test.Build.0 = Legacy|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Any CPU.Build.0 = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Test.ActiveCfg = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Test.Build.0 = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.Build.0 = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Test.ActiveCfg = v2018|Any CPU
|
||||
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Test.Build.0 = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Any CPU.Build.0 = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Test.ActiveCfg = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Test.Build.0 = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Any CPU.Build.0 = Legacy|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Test.ActiveCfg = Legacy|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Test.Build.0 = Legacy|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Any CPU.Build.0 = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Test.ActiveCfg = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Test.Build.0 = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Any CPU.Build.0 = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Test.ActiveCfg = v2018|Any CPU
|
||||
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Test.Build.0 = v2018|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Any CPU.Build.0 = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Test.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Test.Build.0 = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.Build.0 = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Test.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Test.Build.0 = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Test.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Test.Build.0 = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Any CPU.Build.0 = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Test.ActiveCfg = Release|Any CPU
|
||||
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Test.Build.0 = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Test.ActiveCfg = Debug|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Test.Build.0 = Debug|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Any CPU.Build.0 = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Test.ActiveCfg = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Test.Build.0 = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Test.ActiveCfg = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Test.Build.0 = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Any CPU.Build.0 = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Test.ActiveCfg = Release|Any CPU
|
||||
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Test.Build.0 = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Test.ActiveCfg = Debug|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Test.Build.0 = Debug|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Any CPU.Build.0 = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Test.ActiveCfg = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Test.Build.0 = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Test.ActiveCfg = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Test.Build.0 = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Any CPU.Build.0 = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Test.ActiveCfg = Release|Any CPU
|
||||
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Test.Build.0 = Release|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Any CPU.Build.0 = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Test.ActiveCfg = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Test.Build.0 = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Any CPU.Build.0 = Legacy|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Test.ActiveCfg = Legacy|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Test.Build.0 = Legacy|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.Build.0 = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Test.ActiveCfg = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Test.Build.0 = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Any CPU.Build.0 = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Test.ActiveCfg = v2018|Any CPU
|
||||
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Test.Build.0 = v2018|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Test.ActiveCfg = Debug|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Test.Build.0 = Debug|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Any CPU.Build.0 = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Test.ActiveCfg = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Test.Build.0 = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Test.ActiveCfg = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Test.Build.0 = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Any CPU.Build.0 = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Test.ActiveCfg = Release|Any CPU
|
||||
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Test.Build.0 = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Test.ActiveCfg = Debug|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Test.Build.0 = Debug|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Any CPU.Build.0 = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Test.ActiveCfg = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Test.Build.0 = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Test.ActiveCfg = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Test.Build.0 = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Any CPU.Build.0 = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Test.ActiveCfg = Release|Any CPU
|
||||
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Test.Build.0 = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Test.ActiveCfg = Debug|x86
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Test.Build.0 = Debug|x86
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Any CPU.Build.0 = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Test.ActiveCfg = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Test.Build.0 = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Test.ActiveCfg = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Test.Build.0 = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Any CPU.Build.0 = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Test.ActiveCfg = Release|Any CPU
|
||||
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Test.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -33,11 +33,41 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<DocumentationFile>..\bin\BepInEx.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Legacy|x86'">
|
||||
<OutputPath>bin\x86\Legacy\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'v2018|x86'">
|
||||
<OutputPath>bin\x86\v2018\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;UNITY_2018</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Cecil, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Mdb, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Mdb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Pdb, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Pdb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Rocks, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Rocks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>..\lib\UnityEngine.dll</HintPath>
|
||||
@@ -46,8 +76,11 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Configuration\ConfigDefinition.cs" />
|
||||
<Compile Include="Configuration\ConfigDescription.cs" />
|
||||
<Compile Include="Configuration\ConfigEntry.cs" />
|
||||
<Compile Include="Configuration\ConfigFile.cs" />
|
||||
<Compile Include="Configuration\ConfigWrapper.cs" />
|
||||
<Compile Include="Configuration\SettingChangedEventArgs.cs" />
|
||||
<Compile Include="Configuration\TomlTypeConverter.cs" />
|
||||
<Compile Include="Contract\Attributes.cs" />
|
||||
<Compile Include="ConsoleUtil\ConsoleEncoding\ConsoleEncoding.Buffers.cs" />
|
||||
|
||||
@@ -2,27 +2,6 @@
|
||||
|
||||
namespace BepInEx.Configuration
|
||||
{
|
||||
public class ConfigDescription
|
||||
{
|
||||
public ConfigDescription(string description, Type settingType, object defaultValue)
|
||||
{
|
||||
Description = description ?? throw new ArgumentNullException(nameof(description));
|
||||
SettingType = settingType ?? throw new ArgumentNullException(nameof(settingType));
|
||||
DefaultValue = defaultValue;
|
||||
|
||||
if(defaultValue == null && settingType.IsByRef)
|
||||
throw new ArgumentException("defaultValue is null while settingType is a value type");
|
||||
|
||||
if(defaultValue != null && !settingType.IsInstanceOfType(defaultValue))
|
||||
throw new ArgumentException("defaultValue can not be assigned to type " + settingType.Name);
|
||||
}
|
||||
|
||||
public string Description { get; }
|
||||
public Type SettingType { get; }
|
||||
public object DefaultValue { get; }
|
||||
//todo value range
|
||||
}
|
||||
|
||||
public class ConfigDefinition : IEquatable<ConfigDefinition>
|
||||
{
|
||||
public string Section { get; }
|
||||
@@ -39,7 +18,7 @@ namespace BepInEx.Configuration
|
||||
{
|
||||
if (other == null) return false;
|
||||
return string.Equals(Key, other.Key)
|
||||
&& string.Equals(Section, other.Section);
|
||||
&& string.Equals(Section, other.Section);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
@@ -67,5 +46,10 @@ namespace BepInEx.Configuration
|
||||
|
||||
public static bool operator !=(ConfigDefinition left, ConfigDefinition right)
|
||||
=> !Equals(left, right);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Section + " / " + Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace BepInEx.Configuration
|
||||
{
|
||||
public class ConfigDescription
|
||||
{
|
||||
public ConfigDescription(string description)
|
||||
{
|
||||
Description = description ?? throw new ArgumentNullException(nameof(description));
|
||||
}
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
//todo value range
|
||||
|
||||
public string ToSerializedString()
|
||||
{
|
||||
return $"# {Description.Replace("\n", "\n# ")}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BepInEx.Logging;
|
||||
|
||||
namespace BepInEx.Configuration
|
||||
{
|
||||
public sealed class ConfigEntry
|
||||
{
|
||||
internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue) : this(configFile, definition)
|
||||
{
|
||||
SetTypeAndDefaultValue(settingType, defaultValue, true);
|
||||
}
|
||||
|
||||
internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition)
|
||||
{
|
||||
ConfigFile = configFile ?? throw new ArgumentNullException(nameof(configFile));
|
||||
Definition = definition ?? throw new ArgumentNullException(nameof(definition));
|
||||
}
|
||||
|
||||
internal void SetTypeAndDefaultValue(Type settingType, object defaultValue, bool uniqueDefaultValue)
|
||||
{
|
||||
if (settingType == null) throw new ArgumentNullException(nameof(settingType));
|
||||
|
||||
if (settingType == SettingType)
|
||||
{
|
||||
if (uniqueDefaultValue)
|
||||
DefaultValue = defaultValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SettingType != null)
|
||||
{
|
||||
throw new ArgumentException($"Tried to define setting \"{Definition}\" as type {settingType.Name} " +
|
||||
$"while it was already defined as type {SettingType.Name}. Use the same " +
|
||||
$"Type for all Wrappers of a single setting.");
|
||||
}
|
||||
|
||||
if (defaultValue == null && settingType.IsValueType)
|
||||
throw new ArgumentException("defaultValue is null while settingType is a value type");
|
||||
|
||||
if (defaultValue != null && !settingType.IsInstanceOfType(defaultValue))
|
||||
throw new ArgumentException("defaultValue can not be assigned to type " + settingType.Name);
|
||||
|
||||
SettingType = settingType;
|
||||
DefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
private object _convertedValue;
|
||||
private string _serializedValue;
|
||||
|
||||
public ConfigFile ConfigFile { get; }
|
||||
public ConfigDefinition Definition { get; }
|
||||
|
||||
public ConfigDescription Description { get; internal set; }
|
||||
|
||||
public Type SettingType { get; private set; }
|
||||
public object DefaultValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the type of this setting defined, and by extension can <see cref="Value"/> of this setting be accessed.
|
||||
/// Setting is defined when any <see cref="ConfigWrapper{T}"/> objects reference it.
|
||||
/// </summary>
|
||||
public bool IsDefined => SettingType != null;
|
||||
|
||||
/// <summary>
|
||||
/// Can't be used when <see cref="IsDefined"/> is false.
|
||||
/// </summary>
|
||||
public object Value
|
||||
{
|
||||
get
|
||||
{
|
||||
ProcessSerializedValue();
|
||||
|
||||
return _convertedValue;
|
||||
}
|
||||
set => SetValue(value, true, this);
|
||||
}
|
||||
|
||||
internal void SetValue(object newValue, bool fireEvent, object sender)
|
||||
{
|
||||
bool wasChanged = ProcessSerializedValue();
|
||||
wasChanged = wasChanged || !Equals(newValue, _convertedValue);
|
||||
|
||||
if (wasChanged)
|
||||
{
|
||||
_convertedValue = newValue;
|
||||
|
||||
if (fireEvent)
|
||||
OnSettingChanged(sender);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetSerializedValue()
|
||||
{
|
||||
if (_serializedValue != null)
|
||||
return _serializedValue;
|
||||
|
||||
if (!IsDefined)
|
||||
return null;
|
||||
|
||||
return TomlTypeConverter.ConvertToString(Value, SettingType);
|
||||
}
|
||||
|
||||
public void SetSerializedValue(string newValue, bool fireEvent, object sender)
|
||||
{
|
||||
string current = GetSerializedValue();
|
||||
if (string.Equals(current, newValue)) return;
|
||||
|
||||
_serializedValue = newValue;
|
||||
|
||||
if (!IsDefined) return;
|
||||
|
||||
if (ProcessSerializedValue())
|
||||
{
|
||||
if (fireEvent)
|
||||
OnSettingChanged(sender);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ProcessSerializedValue()
|
||||
{
|
||||
if (!IsDefined)
|
||||
throw new InvalidOperationException("Can't get the value before the SettingType is specified");
|
||||
|
||||
if (_serializedValue != null)
|
||||
{
|
||||
string value = _serializedValue;
|
||||
_serializedValue = null;
|
||||
|
||||
if (value != "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var newValue = TomlTypeConverter.ConvertToValue(value, SettingType);
|
||||
if (!Equals(newValue, _convertedValue))
|
||||
{
|
||||
_convertedValue = newValue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log(LogLevel.Warning, $"Config value of setting \"{Definition}\" could not be " +
|
||||
$"parsed and will be ignored. Reason: {e.Message}; Value: {value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_convertedValue == null && DefaultValue != null)
|
||||
{
|
||||
_convertedValue = DefaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnSettingChanged(object sender)
|
||||
{
|
||||
ConfigFile.OnSettingChanged(sender, this);
|
||||
}
|
||||
|
||||
public void WriteDescription(StreamWriter writer)
|
||||
{
|
||||
if (Description != null)
|
||||
writer.WriteLine(Description.ToSerializedString());
|
||||
|
||||
if (SettingType != null)
|
||||
{
|
||||
writer.WriteLine("# Setting type: " + SettingType.Name);
|
||||
writer.WriteLine("# Default value: " + DefaultValue);
|
||||
|
||||
// todo acceptable values
|
||||
|
||||
if (SettingType.IsEnum && SettingType.GetCustomAttributes(typeof(FlagsAttribute), true).Any())
|
||||
writer.WriteLine("# Multiple values can be set at the same time by separating them with , (e.g. Debug, Warning)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using BepInEx.Logging;
|
||||
|
||||
namespace BepInEx.Configuration
|
||||
{
|
||||
@@ -12,28 +13,30 @@ namespace BepInEx.Configuration
|
||||
/// </summary>
|
||||
public class ConfigFile
|
||||
{
|
||||
private static readonly Regex sanitizeKeyRegex = new Regex(@"[^a-zA-Z0-9\-\.]+");
|
||||
// Need to be lazy evaluated to not cause problems for unit tests
|
||||
private static ConfigFile _coreConfig;
|
||||
internal static ConfigFile CoreConfig => _coreConfig ?? (_coreConfig = new ConfigFile(Paths.BepInExConfigPath, true));
|
||||
|
||||
internal static ConfigFile CoreConfig { get; } = new ConfigFile(Paths.BepInExConfigPath, true);
|
||||
protected Dictionary<ConfigDefinition, ConfigEntry> Entries { get; } = new Dictionary<ConfigDefinition, ConfigEntry>();
|
||||
|
||||
protected internal Dictionary<ConfigDefinition, string> Cache { get; } = new Dictionary<ConfigDefinition, string>();
|
||||
[Obsolete("Use ConfigEntries instead")]
|
||||
public ReadOnlyCollection<ConfigDefinition> ConfigDefinitions => Entries.Keys.ToList().AsReadOnly();
|
||||
|
||||
public ReadOnlyCollection<ConfigDefinition> ConfigDefinitions => Cache.Keys.ToList().AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired every time the config is reloaded.
|
||||
/// </summary>
|
||||
public event EventHandler ConfigReloaded;
|
||||
public ReadOnlyCollection<ConfigEntry> ConfigEntries => Entries.Values.ToList().AsReadOnly();
|
||||
|
||||
public string ConfigFilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, writes the config to disk every time a value is set.
|
||||
/// If enabled, writes the config to disk every time a value is set. If disabled, you have to manually save or the changes will be lost!
|
||||
/// </summary>
|
||||
public bool SaveOnConfigSet { get; set; } = true;
|
||||
|
||||
public ConfigFile(string configPath, bool saveOnInit)
|
||||
{
|
||||
if (configPath == null) throw new ArgumentNullException(nameof(configPath));
|
||||
|
||||
configPath = Path.GetFullPath(configPath);
|
||||
|
||||
ConfigFilePath = configPath;
|
||||
|
||||
if (File.Exists(ConfigFilePath))
|
||||
@@ -44,9 +47,13 @@ namespace BepInEx.Configuration
|
||||
{
|
||||
Save();
|
||||
}
|
||||
|
||||
StartWatching();
|
||||
}
|
||||
|
||||
private object _ioLock = new object();
|
||||
#region Save/Load
|
||||
|
||||
private readonly object _ioLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the config from disk. Unsaved changes are lost.
|
||||
@@ -55,9 +62,7 @@ namespace BepInEx.Configuration
|
||||
{
|
||||
lock (_ioLock)
|
||||
{
|
||||
Dictionary<ConfigDefinition, string> descriptions = Cache.ToDictionary(x => x.Key, x => x.Key.Description);
|
||||
|
||||
string currentSection = "";
|
||||
string currentSection = string.Empty;
|
||||
|
||||
foreach (string rawLine in File.ReadAllLines(ConfigFilePath))
|
||||
{
|
||||
@@ -81,14 +86,18 @@ namespace BepInEx.Configuration
|
||||
|
||||
var definition = new ConfigDefinition(currentSection, currentKey);
|
||||
|
||||
if (descriptions.ContainsKey(definition))
|
||||
definition.Description = descriptions[definition];
|
||||
Entries.TryGetValue(definition, out ConfigEntry entry);
|
||||
if (entry == null)
|
||||
{
|
||||
entry = new ConfigEntry(this, definition);
|
||||
Entries[definition] = entry;
|
||||
}
|
||||
|
||||
Cache[definition] = currentValue;
|
||||
entry.SetSerializedValue(currentValue, true, this);
|
||||
}
|
||||
|
||||
ConfigReloaded?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
OnConfigReloaded();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -98,51 +107,177 @@ namespace BepInEx.Configuration
|
||||
{
|
||||
lock (_ioLock)
|
||||
{
|
||||
if (!Directory.Exists(Paths.ConfigPath))
|
||||
Directory.CreateDirectory(Paths.ConfigPath);
|
||||
StopWatching();
|
||||
|
||||
using (StreamWriter writer = new StreamWriter(File.Create(ConfigFilePath), System.Text.Encoding.UTF8))
|
||||
foreach (var sectionKv in Cache.GroupBy(x => x.Key.Section).OrderBy(x => x.Key))
|
||||
string directoryName = Path.GetDirectoryName(ConfigFilePath);
|
||||
if (directoryName != null) Directory.CreateDirectory(directoryName);
|
||||
|
||||
using (var writer = new StreamWriter(File.Create(ConfigFilePath), Encoding.UTF8))
|
||||
{
|
||||
foreach (var sectionKv in Entries.GroupBy(x => x.Key.Section).OrderBy(x => x.Key))
|
||||
{
|
||||
// Section heading
|
||||
writer.WriteLine($"[{sectionKv.Key}]");
|
||||
|
||||
foreach (var entryKv in sectionKv)
|
||||
foreach (var configEntry in sectionKv.Select(x => x.Value))
|
||||
{
|
||||
writer.WriteLine();
|
||||
|
||||
if (!string.IsNullOrEmpty(entryKv.Key.Description))
|
||||
writer.WriteLine($"# {entryKv.Key.Description.Replace("\n", "\n# ")}");
|
||||
configEntry.WriteDescription(writer);
|
||||
|
||||
writer.WriteLine($"{entryKv.Key.Key} = {entryKv.Value}");
|
||||
writer.WriteLine($"{configEntry.Definition.Key} = {configEntry.GetSerializedValue()}");
|
||||
}
|
||||
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
StartWatching();
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigWrapper<T> Wrap<T>(ConfigDefinition configDefinition, T defaultValue = default(T))
|
||||
#endregion
|
||||
|
||||
#region Wraps
|
||||
|
||||
public ConfigWrapper<T> Wrap<T>(ConfigDefinition configDefinition, T defaultValue, ConfigDescription configDescription = null)
|
||||
{
|
||||
if (!Cache.ContainsKey(configDefinition))
|
||||
if (!TomlTypeConverter.CanConvert(typeof(T)))
|
||||
throw new ArgumentException($"Type {typeof(T)} is not supported by the config system. Supported types: {string.Join(", ", TomlTypeConverter.GetSupportedTypes().Select(x => x.Name).ToArray())}");
|
||||
|
||||
Entries.TryGetValue(configDefinition, out var entry);
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
Cache.Add(configDefinition, TomlTypeConverter.ConvertToString(defaultValue));
|
||||
Save();
|
||||
entry = new ConfigEntry(this, configDefinition, typeof(T), defaultValue);
|
||||
Entries[configDefinition] = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
var original = Cache.Keys.First(x => x.Equals(configDefinition));
|
||||
entry.SetTypeAndDefaultValue(typeof(T), defaultValue, !Equals(defaultValue, default(T)));
|
||||
}
|
||||
|
||||
if (original.Description != configDefinition.Description)
|
||||
if (configDescription != null)
|
||||
{
|
||||
if (entry.Description != null)
|
||||
Logger.Log(LogLevel.Warning, $"Tried to add configDescription to setting {configDefinition} when it already had one defined. Only add configDescription once or a random one will be used.");
|
||||
|
||||
entry.Description = configDescription;
|
||||
}
|
||||
|
||||
return new ConfigWrapper<T>(entry);
|
||||
}
|
||||
|
||||
[Obsolete("Use other Wrap overloads instead")]
|
||||
public ConfigWrapper<T> Wrap<T>(string section, string key, string description = null, T defaultValue = default(T))
|
||||
=> Wrap(new ConfigDefinition(section, key), defaultValue, string.IsNullOrEmpty(description) ? null : new ConfigDescription(description));
|
||||
|
||||
public ConfigWrapper<T> Wrap<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null)
|
||||
=> Wrap(new ConfigDefinition(section, key), defaultValue, configDescription);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired every time the config is reloaded.
|
||||
/// </summary>
|
||||
public event EventHandler ConfigReloaded;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when one of the settings is changed.
|
||||
/// </summary>
|
||||
public event EventHandler<SettingChangedEventArgs> SettingChanged;
|
||||
|
||||
protected internal void OnSettingChanged(object sender, ConfigEntry changedEntry)
|
||||
{
|
||||
if (changedEntry == null) throw new ArgumentNullException(nameof(changedEntry));
|
||||
|
||||
if (SettingChanged != null)
|
||||
{
|
||||
var args = new SettingChangedEventArgs(changedEntry);
|
||||
|
||||
foreach (var callback in SettingChanged.GetInvocationList().Cast<EventHandler<SettingChangedEventArgs>>())
|
||||
{
|
||||
original.Description = configDefinition.Description;
|
||||
Save();
|
||||
try
|
||||
{
|
||||
callback(sender, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log(LogLevel.Error, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ConfigWrapper<T>(this, configDefinition);
|
||||
// todo better way to prevent write loop? maybe do some caching?
|
||||
if (sender != this && SaveOnConfigSet)
|
||||
Save();
|
||||
}
|
||||
|
||||
public ConfigWrapper<T> Wrap<T>(string section, string key, string description = null, T defaultValue = default(T))
|
||||
=> Wrap<T>(new ConfigDefinition(section, key, description), defaultValue);
|
||||
protected void OnConfigReloaded()
|
||||
{
|
||||
if (ConfigReloaded != null)
|
||||
{
|
||||
foreach (var callback in ConfigReloaded.GetInvocationList().Cast<EventHandler>())
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(this, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log(LogLevel.Error, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File watcher
|
||||
|
||||
private FileSystemWatcher _watcher;
|
||||
|
||||
/// <summary>
|
||||
/// Start watching the config file on disk for changes.
|
||||
/// </summary>
|
||||
public void StartWatching()
|
||||
{
|
||||
lock (_ioLock)
|
||||
{
|
||||
if (_watcher != null) return;
|
||||
|
||||
_watcher = new FileSystemWatcher
|
||||
{
|
||||
Path = Path.GetDirectoryName(ConfigFilePath) ?? throw new ArgumentException("Invalid config path"),
|
||||
Filter = Path.GetFileName(ConfigFilePath),
|
||||
IncludeSubdirectories = false,
|
||||
NotifyFilter = NotifyFilters.LastWrite,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
_watcher.Changed += (sender, args) => Reload();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop watching the config file on disk for changes.
|
||||
/// </summary>
|
||||
public void StopWatching()
|
||||
{
|
||||
lock (_ioLock)
|
||||
{
|
||||
_watcher?.Dispose();
|
||||
_watcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
~ConfigFile()
|
||||
{
|
||||
StopWatching();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace BepInEx.Configuration
|
||||
{
|
||||
public class ConfigWrapper<T>
|
||||
public sealed class ConfigWrapper<T>
|
||||
{
|
||||
public ConfigDefinition Definition { get; protected set; }
|
||||
public ConfigEntry ConfigEntry { get; }
|
||||
|
||||
public ConfigFile ConfigFile { get; protected set; }
|
||||
public ConfigDefinition Definition => ConfigEntry.Definition;
|
||||
public ConfigFile ConfigFile => ConfigEntry.ConfigFile;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the setting is changed. Does not detect changes made outside from this object.
|
||||
@@ -15,25 +16,18 @@ namespace BepInEx.Configuration
|
||||
|
||||
public T Value
|
||||
{
|
||||
get => TomlTypeConverter.ConvertToValue<T>(ConfigFile.Cache[Definition]);
|
||||
set
|
||||
{
|
||||
ConfigFile.Cache[Definition] = TomlTypeConverter.ConvertToString(value);
|
||||
|
||||
if (ConfigFile.SaveOnConfigSet)
|
||||
ConfigFile.Save();
|
||||
|
||||
SettingChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
get => (T)ConfigEntry.Value;
|
||||
set => ConfigEntry.SetValue(value, true, this);
|
||||
}
|
||||
|
||||
public ConfigWrapper(ConfigFile configFile, ConfigDefinition definition)
|
||||
internal ConfigWrapper(ConfigEntry configEntry)
|
||||
{
|
||||
if (!TomlTypeConverter.TypeConverters.ContainsKey(typeof(T)))
|
||||
throw new ArgumentException("Unsupported config wrapper type");
|
||||
ConfigEntry = configEntry ?? throw new ArgumentNullException(nameof(configEntry));
|
||||
|
||||
ConfigFile = configFile;
|
||||
Definition = definition;
|
||||
configEntry.ConfigFile.SettingChanged += (sender, args) =>
|
||||
{
|
||||
if (args.ChangedSetting == configEntry) SettingChanged?.Invoke(sender, args);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace BepInEx.Configuration
|
||||
{
|
||||
public sealed class SettingChangedEventArgs : EventArgs
|
||||
{
|
||||
public SettingChangedEventArgs(ConfigEntry changedSetting)
|
||||
{
|
||||
ChangedSetting = changedSetting;
|
||||
}
|
||||
|
||||
public ConfigEntry ChangedSetting { get; }
|
||||
}
|
||||
}
|
||||
@@ -98,10 +98,8 @@ namespace BepInEx.Configuration
|
||||
},
|
||||
};
|
||||
|
||||
public static string ConvertToString<T>(T value)
|
||||
public static string ConvertToString(object value, Type valueType)
|
||||
{
|
||||
var valueType = typeof(T);
|
||||
|
||||
var conv = GetConverter(valueType);
|
||||
if (conv == null)
|
||||
throw new InvalidOperationException($"Cannot convert from type {valueType}");
|
||||
@@ -111,13 +109,16 @@ namespace BepInEx.Configuration
|
||||
|
||||
public static T ConvertToValue<T>(string value)
|
||||
{
|
||||
var valueType = typeof(T);
|
||||
return (T)ConvertToValue(value, typeof(T));
|
||||
}
|
||||
|
||||
public static object ConvertToValue(string value, Type valueType)
|
||||
{
|
||||
var conv = GetConverter(valueType);
|
||||
if (conv == null)
|
||||
throw new InvalidOperationException($"Cannot convert to type {valueType}");
|
||||
|
||||
return (T)conv.ConvertToObject(value, valueType);
|
||||
return conv.ConvertToObject(value, valueType);
|
||||
}
|
||||
|
||||
private static TypeConverter GetConverter(Type valueType)
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E7CD429A-D057-48E3-8C51-E5C934E8E07B}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>BepInExTests</RootNamespace>
|
||||
<AssemblyName>BepInExTests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<TargetFrameworkProfile />
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>15.0</OldToolsVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<Otherwise />
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="Configuration\ConfigFileTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BepInEx\BepInEx.csproj">
|
||||
<Project>{4ffba620-f5ed-47f9-b90c-dad1316fd9b9}</Project>
|
||||
<Name>BepInEx</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace BepInEx.Configuration.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class ConfigFileTests
|
||||
{
|
||||
private static ConcurrentBag<ConfigFile> _toRemove;
|
||||
|
||||
[ClassInitialize]
|
||||
public static void Init(TestContext context)
|
||||
{
|
||||
_toRemove = new ConcurrentBag<ConfigFile>();
|
||||
}
|
||||
|
||||
[ClassCleanup]
|
||||
public static void Cleanup()
|
||||
{
|
||||
foreach (var configFile in _toRemove)
|
||||
{
|
||||
configFile.StopWatching();
|
||||
File.Delete(configFile.ConfigFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private static ConfigFile MakeConfig()
|
||||
{
|
||||
string configPath = Path.GetTempFileName();
|
||||
if (configPath == null) throw new InvalidOperationException("Wtf");
|
||||
var config = new ConfigFile(configPath, true);
|
||||
_toRemove.Add(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SaveTest()
|
||||
{
|
||||
MakeConfig().Save();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SaveTestValueChange()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
|
||||
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
|
||||
var lines = File.ReadAllLines(c.ConfigFilePath);
|
||||
Assert.AreEqual(0, lines.Count(x => x.Equals("[Cat]")));
|
||||
Assert.AreEqual(0, lines.Count(x => x.Equals("# Test")));
|
||||
Assert.AreEqual(0, lines.Count(x => x.Equals("Key = 0")));
|
||||
|
||||
c.Save();
|
||||
lines = File.ReadAllLines(c.ConfigFilePath);
|
||||
Assert.AreEqual(1, lines.Count(x => x.Equals("[Cat]")));
|
||||
Assert.AreEqual(1, lines.Count(x => x.Equals("# Test")));
|
||||
Assert.AreEqual(1, lines.Count(x => x.Equals("Key = 0")));
|
||||
|
||||
w.Value = 69;
|
||||
lines = File.ReadAllLines(c.ConfigFilePath);
|
||||
Assert.AreEqual(1, lines.Count(x => x.Equals("[Cat]")));
|
||||
Assert.AreEqual(1, lines.Count(x => x.Equals("# Test")));
|
||||
Assert.AreEqual(1, lines.Count(x => x.Equals("Key = 69")));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AutoSaveTest()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
|
||||
|
||||
var eventFired = new AutoResetEvent(false);
|
||||
c.ConfigReloaded += (sender, args) => eventFired.Set();
|
||||
|
||||
c.Save();
|
||||
|
||||
Assert.IsFalse(eventFired.WaitOne(200));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadTest()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n");
|
||||
c.Reload();
|
||||
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
|
||||
Assert.AreEqual(w.Value, 1);
|
||||
var w2 = c.Wrap("Cat", "Key2", 0, new ConfigDescription("Test"));
|
||||
Assert.AreEqual(w2.Value, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadTest2()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
|
||||
Assert.AreEqual(w.Value, 0);
|
||||
|
||||
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey = 1 \n");
|
||||
|
||||
c.Reload();
|
||||
Assert.AreEqual(w.Value, 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FileWatchTest()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
|
||||
Assert.AreEqual(w.Value, 0);
|
||||
|
||||
var eventFired = new AutoResetEvent(false);
|
||||
w.SettingChanged += (sender, args) => eventFired.Set();
|
||||
|
||||
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey = 1 \n");
|
||||
|
||||
Assert.IsTrue(eventFired.WaitOne(500));
|
||||
|
||||
Assert.AreEqual(w.Value, 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FileWatchTestNoSelfReload()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
|
||||
var eventFired = new AutoResetEvent(false);
|
||||
c.ConfigReloaded += (sender, args) => eventFired.Set();
|
||||
|
||||
c.Save();
|
||||
|
||||
Assert.IsFalse(eventFired.WaitOne(200));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EventTestWrapper()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
|
||||
|
||||
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n");
|
||||
|
||||
var eventFired = false;
|
||||
w.SettingChanged += (sender, args) => eventFired = true;
|
||||
|
||||
c.Reload();
|
||||
|
||||
Assert.IsTrue(eventFired);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EventTestReload()
|
||||
{
|
||||
var c = MakeConfig();
|
||||
var eventFired = false;
|
||||
|
||||
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
|
||||
w.SettingChanged += (sender, args) => eventFired = true;
|
||||
|
||||
Assert.IsFalse(eventFired);
|
||||
|
||||
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n");
|
||||
c.Reload();
|
||||
|
||||
Assert.IsTrue(eventFired);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("BepInExTests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("BepInExTests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("e7cd429a-d057-48e3-8c51-e5c934e8e07b")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net45" />
|
||||
<package id="MSTest.TestFramework" version="1.3.2" targetFramework="net45" />
|
||||
</packages>
|
||||
Reference in new issue
Block a user