From 3ed7191acf4b5c1fc5fabcad83963c70994a2526 Mon Sep 17 00:00:00 2001 From: Adele Reed Date: Tue, 6 Aug 2024 17:26:31 +0000 Subject: [PATCH] Use System.Text.Json with BepInEx (#1) When running via BepInEx, Unity's JsonUtility produces and reads empty documents. As a bit of a hacky fix, this builds using System.Text.Json. granted, this pulls new deps in. All of the deps listed in the csproj will need to be included in the install package for BepInEx. Reviewed-on: https://gitea.tendokyu.moe/beerpsi/Rizu/pulls/1 Co-authored-by: Adele Reed Co-committed-by: Adele Reed --- Rizu.BepInEx/Rizu.BepInEx.csproj | 10 +-- Rizu.Core/App.config | 10 +++ Rizu.Core/Exporter.cs | 26 ++++-- Rizu.Core/JsonShim.cs | 39 +++++++++ Rizu.Core/Models/BatchManual.cs | 9 ++- Rizu.Core/Models/BatchManualDan.cs | 5 ++ Rizu.Core/Models/BatchManualMatchingClass.cs | 5 ++ Rizu.Core/Models/BatchManualMeta.cs | 7 ++ Rizu.Core/Models/BatchManualOptional.cs | 7 ++ Rizu.Core/Models/BatchManualRankUp.cs | 7 ++ Rizu.Core/Models/BatchManualResponseBody.cs | 5 ++ Rizu.Core/Models/BatchManualScore.cs | 12 +++ .../Models/BatchManualScoreJudgements.cs | 9 +++ Rizu.Core/Models/ImportDocument.cs | 7 ++ Rizu.Core/Models/ImportErrContent.cs | 6 ++ Rizu.Core/Models/ImportProgress.cs | 6 ++ Rizu.Core/Models/ImportStatusResponseBody.cs | 7 ++ Rizu.Core/Models/SessionInfoReturn.cs | 6 ++ Rizu.Core/Models/TachiResponse.cs | 7 ++ Rizu.Core/Rizu.Core.csproj | 79 ++++++++++++++++--- Rizu.Core/ScoreConversion.cs | 5 +- Rizu.Core/packages.config | 12 +++ Rizu.MonoMod/Rizu.MonoMod.csproj | 21 +---- Rizu.sln | 38 +++++---- 24 files changed, 284 insertions(+), 61 deletions(-) create mode 100644 Rizu.Core/App.config create mode 100644 Rizu.Core/JsonShim.cs create mode 100644 Rizu.Core/packages.config diff --git a/Rizu.BepInEx/Rizu.BepInEx.csproj b/Rizu.BepInEx/Rizu.BepInEx.csproj index 13bc3a3..9baccb4 100644 --- a/Rizu.BepInEx/Rizu.BepInEx.csproj +++ b/Rizu.BepInEx/Rizu.BepInEx.csproj @@ -20,24 +20,22 @@ https://nuget.bepinex.dev/v3/index.json - - AnyCPU + true full false - bin\Debug\ DEBUG;TRACE prompt 4 + bin\BepInEx (Debug)\ - - AnyCPU + pdbonly true - bin\Release\ TRACE prompt 4 + bin\BepInEx (Release)\ diff --git a/Rizu.Core/App.config b/Rizu.Core/App.config new file mode 100644 index 0000000..9816074 --- /dev/null +++ b/Rizu.Core/App.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Rizu.Core/Exporter.cs b/Rizu.Core/Exporter.cs index cedb595..352df79 100644 --- a/Rizu.Core/Exporter.cs +++ b/Rizu.Core/Exporter.cs @@ -29,11 +29,21 @@ public class Exporter { yield break; } - - var user = Singleton.Instance.GetUserData(score.PlayerIndex); - var import = ScoreConversion.CreateScoreBatchManual(score); - yield return SubmitImport(import, user.Detail.AccessCode); + var user = Singleton.Instance.GetUserData(score.PlayerIndex); + string import = ""; + + try + { + import = ScoreConversion.CreateScoreBatchManual(score); + } + catch (Exception exception) + { + Logger.Error(exception.ToString()); + } + + if (import != "") + yield return SubmitImport(import, user.Detail.AccessCode); } public IEnumerator ExportDan(UserDetail userDetail) @@ -123,9 +133,10 @@ public class Exporter TachiResponse resp; + Logger.Debug("Received response from Tachi (Response code {1}): {0}", req.downloadHandler.text, req.responseCode); try { - resp = JsonUtility.FromJson>(req.downloadHandler.text); + resp = JsonShim.Deserialize>(req.downloadHandler.text); } catch (Exception e) { @@ -139,12 +150,11 @@ public class Exporter yield break; } - Logger.Info("{0}", resp.description); - var pollUrl = resp.body?.url; if (string.IsNullOrEmpty(pollUrl)) { + Logger.Debug("Received no poll uri!"); yield break; } @@ -170,7 +180,7 @@ public class Exporter try { - pollResp = JsonUtility.FromJson>(pollReq.downloadHandler.text); + pollResp = JsonShim.Deserialize>(pollReq.downloadHandler.text); } catch (Exception e) { diff --git a/Rizu.Core/JsonShim.cs b/Rizu.Core/JsonShim.cs new file mode 100644 index 0000000..ffaa38c --- /dev/null +++ b/Rizu.Core/JsonShim.cs @@ -0,0 +1,39 @@ +namespace Rizu.Core; + +/* + * JsonShim creates a single place that JSON APIs get called from, to avoid having to manually write precompiler directives every time. + * Never call a JSON package directly, always call via JsonShim. + */ + +#if MONOMOD +using UnityEngine; +using System; +#else +using System.Text.Json; +#endif + +public class JsonShim +{ + public static string Serialize(object Target) + { +#if MONOMOD + return JsonUtility.ToJson(Target); +#else + return JsonSerializer.Serialize(Target); +#endif + } + + public static T Deserialize(string data) + { +#if MONOMOD + return JsonUtility.FromJson(data); +#else + return JsonSerializer.Deserialize(data); +#endif + } +} + +#if MONOMOD +[AttributeUsage(AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple = false)] +public class JsonIncludeAttribute : Attribute { } +#endif \ No newline at end of file diff --git a/Rizu.Core/Models/BatchManual.cs b/Rizu.Core/Models/BatchManual.cs index cb8a20f..c8c446a 100644 --- a/Rizu.Core/Models/BatchManual.cs +++ b/Rizu.Core/Models/BatchManual.cs @@ -1,11 +1,18 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManual { + [JsonInclude] public BatchManualMeta meta = new(); - public BatchManualScore[] scores; + [JsonInclude] + public BatchManualScore[] scores; + [JsonInclude] public BatchManualMatchingClass classes; } diff --git a/Rizu.Core/Models/BatchManualDan.cs b/Rizu.Core/Models/BatchManualDan.cs index c27d419..0ebceaf 100644 --- a/Rizu.Core/Models/BatchManualDan.cs +++ b/Rizu.Core/Models/BatchManualDan.cs @@ -1,9 +1,14 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualDan { + [JsonInclude] public string dan; } \ No newline at end of file diff --git a/Rizu.Core/Models/BatchManualMatchingClass.cs b/Rizu.Core/Models/BatchManualMatchingClass.cs index 7095b3d..36593d8 100644 --- a/Rizu.Core/Models/BatchManualMatchingClass.cs +++ b/Rizu.Core/Models/BatchManualMatchingClass.cs @@ -1,9 +1,14 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualMatchingClass { + [JsonInclude] public string matchingClass; } diff --git a/Rizu.Core/Models/BatchManualMeta.cs b/Rizu.Core/Models/BatchManualMeta.cs index a1081b0..935fd17 100644 --- a/Rizu.Core/Models/BatchManualMeta.cs +++ b/Rizu.Core/Models/BatchManualMeta.cs @@ -1,11 +1,18 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualMeta { + [JsonInclude] public string game = "maimaidx"; + [JsonInclude] public string playtype = "Single"; + [JsonInclude] public string service = "Rizu"; } diff --git a/Rizu.Core/Models/BatchManualOptional.cs b/Rizu.Core/Models/BatchManualOptional.cs index 17f1764..e527299 100644 --- a/Rizu.Core/Models/BatchManualOptional.cs +++ b/Rizu.Core/Models/BatchManualOptional.cs @@ -1,11 +1,18 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualOptional { + [JsonInclude] public uint fast; + [JsonInclude] public uint slow; + [JsonInclude] public uint maxCombo; } diff --git a/Rizu.Core/Models/BatchManualRankUp.cs b/Rizu.Core/Models/BatchManualRankUp.cs index 5c72557..c4fb0cc 100644 --- a/Rizu.Core/Models/BatchManualRankUp.cs +++ b/Rizu.Core/Models/BatchManualRankUp.cs @@ -1,11 +1,18 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualRankUp { + [JsonInclude] public BatchManualMeta meta = new(); + [JsonInclude] public BatchManualScore[] scores; + [JsonInclude] public BatchManualDan classes; } \ No newline at end of file diff --git a/Rizu.Core/Models/BatchManualResponseBody.cs b/Rizu.Core/Models/BatchManualResponseBody.cs index b2cd261..c255f70 100644 --- a/Rizu.Core/Models/BatchManualResponseBody.cs +++ b/Rizu.Core/Models/BatchManualResponseBody.cs @@ -1,9 +1,14 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualResponseBody { + [JsonInclude] public string url; } diff --git a/Rizu.Core/Models/BatchManualScore.cs b/Rizu.Core/Models/BatchManualScore.cs index a7c72ea..e43f715 100644 --- a/Rizu.Core/Models/BatchManualScore.cs +++ b/Rizu.Core/Models/BatchManualScore.cs @@ -1,16 +1,28 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualScore { + [JsonInclude] public float percent; + [JsonInclude] public string lamp; + [JsonInclude] public string matchType = "songTitle"; + [JsonInclude] public string identifier; + [JsonInclude] public string difficulty; + [JsonInclude] public long timeAchieved; + [JsonInclude] public BatchManualScoreJudgements judgements; + [JsonInclude] public BatchManualOptional optional; } diff --git a/Rizu.Core/Models/BatchManualScoreJudgements.cs b/Rizu.Core/Models/BatchManualScoreJudgements.cs index 7b61af2..ecd65a2 100644 --- a/Rizu.Core/Models/BatchManualScoreJudgements.cs +++ b/Rizu.Core/Models/BatchManualScoreJudgements.cs @@ -1,13 +1,22 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class BatchManualScoreJudgements { + [JsonInclude] public uint pcrit; + [JsonInclude] public uint perfect; + [JsonInclude] public uint great; + [JsonInclude] public uint good; + [JsonInclude] public uint miss; } diff --git a/Rizu.Core/Models/ImportDocument.cs b/Rizu.Core/Models/ImportDocument.cs index bb1f382..68c4e2f 100644 --- a/Rizu.Core/Models/ImportDocument.cs +++ b/Rizu.Core/Models/ImportDocument.cs @@ -1,11 +1,18 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class ImportDocument { + [JsonInclude] public string[] scoreIDs; + [JsonInclude] public ImportErrContent[] errors; + [JsonInclude] public SessionInfoReturn[] createdSessions; } diff --git a/Rizu.Core/Models/ImportErrContent.cs b/Rizu.Core/Models/ImportErrContent.cs index 816db5c..8652949 100644 --- a/Rizu.Core/Models/ImportErrContent.cs +++ b/Rizu.Core/Models/ImportErrContent.cs @@ -1,10 +1,16 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class ImportErrContent { + [JsonInclude] public string type; + [JsonInclude] public string message; } \ No newline at end of file diff --git a/Rizu.Core/Models/ImportProgress.cs b/Rizu.Core/Models/ImportProgress.cs index 555b45a..2b46527 100644 --- a/Rizu.Core/Models/ImportProgress.cs +++ b/Rizu.Core/Models/ImportProgress.cs @@ -1,10 +1,16 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class ImportProgress { + [JsonInclude] public string description; + [JsonInclude] public int value; } diff --git a/Rizu.Core/Models/ImportStatusResponseBody.cs b/Rizu.Core/Models/ImportStatusResponseBody.cs index af6994b..541e03e 100644 --- a/Rizu.Core/Models/ImportStatusResponseBody.cs +++ b/Rizu.Core/Models/ImportStatusResponseBody.cs @@ -1,11 +1,18 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class ImportStatusResponseBody { + [JsonInclude] public string importStatus; + [JsonInclude] public ImportProgress progress; + [JsonInclude] public ImportDocument import; } diff --git a/Rizu.Core/Models/SessionInfoReturn.cs b/Rizu.Core/Models/SessionInfoReturn.cs index d8fecd0..54a9ef5 100644 --- a/Rizu.Core/Models/SessionInfoReturn.cs +++ b/Rizu.Core/Models/SessionInfoReturn.cs @@ -1,10 +1,16 @@ using System; +#if BEPINEX +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class SessionInfoReturn { + [JsonInclude] public string type; + [JsonInclude] public string sessionID; } \ No newline at end of file diff --git a/Rizu.Core/Models/TachiResponse.cs b/Rizu.Core/Models/TachiResponse.cs index aa31dcd..ab45db5 100644 --- a/Rizu.Core/Models/TachiResponse.cs +++ b/Rizu.Core/Models/TachiResponse.cs @@ -1,11 +1,18 @@ using System; +#if BEPINEX // When we're building for BepInEx, the dummy JsonInclude is *not* defined, so let's cover that base. +using System.Text.Json.Serialization; +#endif + namespace Rizu.Core.Models; [Serializable] public class TachiResponse { + [JsonInclude] public bool success; + [JsonInclude] public string description; + [JsonInclude] public T body; } diff --git a/Rizu.Core/Rizu.Core.csproj b/Rizu.Core/Rizu.Core.csproj index 34f15b8..c014a2b 100644 --- a/Rizu.Core/Rizu.Core.csproj +++ b/Rizu.Core/Rizu.Core.csproj @@ -15,25 +15,83 @@ 512 latest - - AnyCPU + + + pdbonly + true + MONOMOD;TRACE + prompt + 4 + bin\MonoMod (Release)\ + + true full false - bin\Debug\ - DEBUG;TRACE + MONOMOD;DEBUG;TRACE prompt 4 + bin\MonoMod (Debug)\ - - AnyCPU + + + ..\External\UnityEngine.JSONSerializeModule.dll + False + + + + + true + full + false + BEPINEX;DEBUG;TRACE + prompt + 4 + bin\BepInEx (Debug)\ + + pdbonly true - bin\Release\ - TRACE + BEPINEX;TRACE prompt 4 + bin\BepInEx (Release)\ + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.8.0.4\lib\net462\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + + + + + @@ -47,10 +105,6 @@ ..\External\UnityEngine.CoreModule.dll False - - ..\External\UnityEngine.JSONSerializeModule.dll - False - ..\External\UnityEngine.UnityWebRequestModule.dll False @@ -60,6 +114,7 @@ + diff --git a/Rizu.Core/ScoreConversion.cs b/Rizu.Core/ScoreConversion.cs index ac6beaa..2159572 100644 --- a/Rizu.Core/ScoreConversion.cs +++ b/Rizu.Core/ScoreConversion.cs @@ -3,7 +3,6 @@ using DB; using MAI2.Util; using Manager; using Rizu.Core.Models; -using UnityEngine; namespace Rizu.Core; @@ -89,7 +88,7 @@ public static class ScoreConversion score.matchType = "tachiSongID"; } - return JsonUtility.ToJson(new BatchManual + return JsonShim.Serialize(new BatchManual { meta = new BatchManualMeta(), scores = [score], @@ -108,7 +107,7 @@ public static class ScoreConversion return null; } - return JsonUtility.ToJson(new BatchManualRankUp + return JsonShim.Serialize(new BatchManualRankUp { meta = new BatchManualMeta(), scores = [], diff --git a/Rizu.Core/packages.config b/Rizu.Core/packages.config new file mode 100644 index 0000000..b5afb59 --- /dev/null +++ b/Rizu.Core/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Rizu.MonoMod/Rizu.MonoMod.csproj b/Rizu.MonoMod/Rizu.MonoMod.csproj index ae0a2c3..8a3eb38 100644 --- a/Rizu.MonoMod/Rizu.MonoMod.csproj +++ b/Rizu.MonoMod/Rizu.MonoMod.csproj @@ -15,24 +15,11 @@ 512 latest - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 + + bin\MonoMod (Debug)\ - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + + bin\MonoMod (Release)\ diff --git a/Rizu.sln b/Rizu.sln index 1738134..84f57a9 100644 --- a/Rizu.sln +++ b/Rizu.sln @@ -8,21 +8,31 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rizu.BepInEx", "Rizu.BepInE EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU + MonoMod (Debug)|Any CPU = MonoMod (Debug)|Any CPU + MonoMod (Release)|Any CPU = MonoMod (Release)|Any CPU + BepInEx (Release)|Any CPU = BepInEx (Release)|Any CPU + BepInEx (Debug)|Any CPU = BepInEx (Debug)|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4241A369-1619-4450-8182-CB66F447985F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4241A369-1619-4450-8182-CB66F447985F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4241A369-1619-4450-8182-CB66F447985F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4241A369-1619-4450-8182-CB66F447985F}.Release|Any CPU.Build.0 = Release|Any CPU - {888E076C-8A77-453F-87DC-BC0186FDBB55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {888E076C-8A77-453F-87DC-BC0186FDBB55}.Debug|Any CPU.Build.0 = Debug|Any CPU - {888E076C-8A77-453F-87DC-BC0186FDBB55}.Release|Any CPU.ActiveCfg = Release|Any CPU - {888E076C-8A77-453F-87DC-BC0186FDBB55}.Release|Any CPU.Build.0 = Release|Any CPU - {4DDF918E-7A74-4266-B56A-028D66ED336F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4DDF918E-7A74-4266-B56A-028D66ED336F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4DDF918E-7A74-4266-B56A-028D66ED336F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4DDF918E-7A74-4266-B56A-028D66ED336F}.Release|Any CPU.Build.0 = Release|Any CPU + {4DDF918E-7A74-4266-B56A-028D66ED336F}.MonoMod (Debug)|Any CPU.ActiveCfg = BepInEx (Debug)|Any CPU + {4DDF918E-7A74-4266-B56A-028D66ED336F}.MonoMod (Release)|Any CPU.ActiveCfg = BepInEx (Release)|Any CPU + {4DDF918E-7A74-4266-B56A-028D66ED336F}.BepInEx (Release)|Any CPU.ActiveCfg = BepInEx (Release)|Any CPU + {4DDF918E-7A74-4266-B56A-028D66ED336F}.BepInEx (Release)|Any CPU.Build.0 = BepInEx (Release)|Any CPU + {4DDF918E-7A74-4266-B56A-028D66ED336F}.BepInEx (Debug)|Any CPU.ActiveCfg = BepInEx (Debug)|Any CPU + {4DDF918E-7A74-4266-B56A-028D66ED336F}.BepInEx (Debug)|Any CPU.Build.0 = BepInEx (Debug)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.MonoMod (Debug)|Any CPU.ActiveCfg = MonoMod (Debug)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.MonoMod (Debug)|Any CPU.Build.0 = MonoMod (Debug)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.MonoMod (Release)|Any CPU.ActiveCfg = MonoMod (Release)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.MonoMod (Release)|Any CPU.Build.0 = MonoMod (Release)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.BepInEx (Release)|Any CPU.ActiveCfg = BepInEx (Release)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.BepInEx (Release)|Any CPU.Build.0 = BepInEx (Release)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.BepInEx (Debug)|Any CPU.ActiveCfg = BepInEx (Debug)|Any CPU + {4241A369-1619-4450-8182-CB66F447985F}.BepInEx (Debug)|Any CPU.Build.0 = BepInEx (Debug)|Any CPU + {888E076C-8A77-453F-87DC-BC0186FDBB55}.MonoMod (Debug)|Any CPU.ActiveCfg = MonoMod (Debug)|Any CPU + {888E076C-8A77-453F-87DC-BC0186FDBB55}.MonoMod (Debug)|Any CPU.Build.0 = MonoMod (Debug)|Any CPU + {888E076C-8A77-453F-87DC-BC0186FDBB55}.MonoMod (Release)|Any CPU.ActiveCfg = MonoMod (Release)|Any CPU + {888E076C-8A77-453F-87DC-BC0186FDBB55}.MonoMod (Release)|Any CPU.Build.0 = MonoMod (Release)|Any CPU + {888E076C-8A77-453F-87DC-BC0186FDBB55}.BepInEx (Release)|Any CPU.ActiveCfg = MonoMod (Release)|Any CPU + {888E076C-8A77-453F-87DC-BC0186FDBB55}.BepInEx (Debug)|Any CPU.ActiveCfg = MonoMod (Debug)|Any CPU EndGlobalSection EndGlobal