Json extension data fody (#340)
* Added a new Fody plugin to add to every model class the JsonExtensionData attribute * retargeted fody plugin to netstandard for msbuild runtime * Fixed runtime issue * Fixed property check for new extension data properties --------- Co-authored-by: Alex <clodanSPT@hotmail.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace JsonExtensionData.Fody;
|
||||
|
||||
public partial class ModuleWeaver
|
||||
{
|
||||
public void ProcessAssembly()
|
||||
{
|
||||
foreach (var type in allClasses)
|
||||
{
|
||||
if (!ShouldInclude(type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldIncludeType(type))
|
||||
{
|
||||
ProcessType(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldIncludeType(TypeDefinition type)
|
||||
{
|
||||
return IncludeNamespacesRegex.Any(r => r.IsMatch(type.Namespace));
|
||||
}
|
||||
|
||||
static bool ShouldInclude(TypeDefinition type) => !type.IsSealed;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace JsonExtensionData.Fody;
|
||||
|
||||
public static class CecilExtensions
|
||||
{
|
||||
public static List<TypeDefinition> GetAllClasses(this ModuleDefinition moduleDefinition)
|
||||
{
|
||||
var definitions = new List<TypeDefinition>();
|
||||
//First is always module so we will skip that;
|
||||
GetTypes(moduleDefinition.Types.Skip(1), definitions);
|
||||
return definitions;
|
||||
}
|
||||
|
||||
static void GetTypes(IEnumerable<TypeDefinition> typeDefinitions, List<TypeDefinition> definitions)
|
||||
{
|
||||
foreach (var typeDefinition in typeDefinitions)
|
||||
{
|
||||
GetTypes(typeDefinition.NestedTypes, definitions);
|
||||
|
||||
if (typeDefinition.IsInterface)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeDefinition.IsEnum)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
definitions.Add(typeDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace JsonExtensionData.Fody;
|
||||
|
||||
public partial class ModuleWeaver
|
||||
{
|
||||
public List<Regex> IncludeNamespacesRegex = new();
|
||||
|
||||
public void ReadConfig()
|
||||
{
|
||||
ReadExcludes();
|
||||
}
|
||||
|
||||
void ReadExcludes()
|
||||
{
|
||||
var includeNamespacesElement = Config.Element("IncludeNamespacesRegex");
|
||||
if (includeNamespacesElement != null)
|
||||
{
|
||||
foreach (var item in includeNamespacesElement.Value
|
||||
.Split(
|
||||
[
|
||||
"\r\n",
|
||||
"\n"
|
||||
],
|
||||
StringSplitOptions.RemoveEmptyEntries)
|
||||
.NonEmpty())
|
||||
{
|
||||
IncludeNamespacesRegex.Add(new Regex(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace JsonExtensionData.Fody;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static IEnumerable<string> NonEmpty(this IEnumerable<string> list) =>
|
||||
list.Select(_ => _.Trim()).Where(_ => _ != string.Empty);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Build.props"/>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
DO NOT change the target framework for this project
|
||||
This is a Fody plugin that runs over the MSBuild runtime, changing this target framework will make this plugin
|
||||
crash under certain MSBuild runtimes, like the one VS uses
|
||||
-->
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FodyHelpers" Version="6.8.0"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="IncludeNamespacesRegex"
|
||||
type="xs:string"
|
||||
minOccurs="0"
|
||||
maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Namespaces to use for adding JsonExtensionData properties and attribute</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:complexType>
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using Fody;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace JsonExtensionData.Fody;
|
||||
|
||||
public partial class ModuleWeaver : BaseModuleWeaver
|
||||
{
|
||||
List<TypeDefinition> allClasses;
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
allClasses = ModuleDefinition.GetAllClasses();
|
||||
ReadConfig();
|
||||
ProcessAssembly();
|
||||
}
|
||||
|
||||
public override IEnumerable<string> GetAssembliesForScanning()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public override bool ShouldCleanReference => true;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace JsonExtensionData.Fody;
|
||||
|
||||
public partial class ModuleWeaver
|
||||
{
|
||||
private TypeReference? _dictionaryStringObjectReference;
|
||||
private MethodReference? _jsonExtensionDataAttributeReference;
|
||||
private MethodReference? _jsonIgnoreAttributeReference;
|
||||
public void ProcessType(TypeDefinition typeDefinition)
|
||||
{
|
||||
_dictionaryStringObjectReference ??= ModuleDefinition.ImportReference(typeof(Dictionary<string, object>));
|
||||
if (_jsonExtensionDataAttributeReference is null)
|
||||
{
|
||||
var jsonConstructorReference = ModuleDefinition.AssemblyResolver
|
||||
.Resolve(AssemblyNameReference.Parse("System.Text.Json")).MainModule
|
||||
.GetType("System.Text.Json.Serialization.JsonExtensionDataAttribute").Methods
|
||||
.First(m => m.IsConstructor && !m.HasParameters);
|
||||
_jsonExtensionDataAttributeReference = ModuleDefinition.ImportReference(jsonConstructorReference);
|
||||
}
|
||||
if (_jsonIgnoreAttributeReference is null)
|
||||
{
|
||||
var jsonIgnoreConstructorReference = ModuleDefinition.AssemblyResolver
|
||||
.Resolve(AssemblyNameReference.Parse("System.Text.Json")).MainModule
|
||||
.GetType("System.Text.Json.Serialization.JsonIgnoreAttribute").Methods
|
||||
.First(m => m.IsConstructor && !m.HasParameters);
|
||||
_jsonIgnoreAttributeReference = ModuleDefinition.ImportReference(jsonIgnoreConstructorReference);
|
||||
}
|
||||
var propertyDefinition = new PropertyDefinition("ExtensionData", PropertyAttributes.None, _dictionaryStringObjectReference);
|
||||
propertyDefinition.CustomAttributes.Add(new CustomAttribute(_jsonExtensionDataAttributeReference));
|
||||
|
||||
// Add backing field
|
||||
var field = new FieldDefinition("_extensionData",
|
||||
FieldAttributes.Private,
|
||||
_dictionaryStringObjectReference);
|
||||
field.CustomAttributes.Add(new CustomAttribute(_jsonIgnoreAttributeReference));
|
||||
typeDefinition.Fields.Add(field);
|
||||
|
||||
// Add getter
|
||||
var get = new MethodDefinition("get_ExtensionData",
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
_dictionaryStringObjectReference);
|
||||
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, field));
|
||||
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
propertyDefinition.GetMethod = get;
|
||||
typeDefinition.Methods.Add(get);
|
||||
|
||||
// Add setter
|
||||
var set = new MethodDefinition("set_ExtensionData",
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
ModuleDefinition.TypeSystem.Void);
|
||||
set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, _dictionaryStringObjectReference));
|
||||
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field));
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
propertyDefinition.SetMethod = set;
|
||||
typeDefinition.Methods.Add(set);
|
||||
typeDefinition.Properties.Add(propertyDefinition);
|
||||
}
|
||||
}
|
||||
@@ -62,12 +62,12 @@ public class LauncherController(
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
var dbProfiles = _databaseService.GetProfiles();
|
||||
foreach (var templatesProperty in typeof(ProfileTemplates).GetProperties().Where(p => p.CanWrite))
|
||||
foreach (var templatesProperty in typeof(ProfileTemplates).GetProperties().Where(p => p.CanWrite && p.Name != "ExtensionData"))
|
||||
{
|
||||
var propertyValue = templatesProperty.GetValue(dbProfiles);
|
||||
if (propertyValue == null)
|
||||
{
|
||||
_logger.Warning(_localisationService.GetText("launcher-missing_property", templatesProperty));
|
||||
_logger.Warning(_localisationService.GetText("launcher-missing_property", templatesProperty.Name));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ public class LauncherV2Controller(
|
||||
var result = new Dictionary<string, string>();
|
||||
var dbProfiles = _databaseService.GetProfiles();
|
||||
|
||||
foreach (var templatesProperty in typeof(ProfileTemplates).GetProperties().Where(p => p.CanWrite))
|
||||
foreach (var templatesProperty in typeof(ProfileTemplates).GetProperties().Where(p => p.CanWrite && p.Name != "ExtensionData"))
|
||||
{
|
||||
var propertyValue = templatesProperty.GetValue(dbProfiles);
|
||||
if (propertyValue == null)
|
||||
{
|
||||
_logger.Warning(_localisationService.GetText("launcher-missing_property", templatesProperty));
|
||||
_logger.Warning(_localisationService.GetText("launcher-missing_property", templatesProperty.Name));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<JsonExtensionData>
|
||||
<IncludeNamespacesRegex>SPTarkov\.Server\.Core\.Models.*</IncludeNamespacesRegex>
|
||||
</JsonExtensionData>
|
||||
<Virtuosity/>
|
||||
</Weavers>
|
||||
|
||||
@@ -5,6 +5,15 @@
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="Virtuosity" minOccurs="0" maxOccurs="1" type="xs:anyType" />
|
||||
<xs:element name="JsonExtensionData" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:element name="IncludeNamespacesRegex" type="xs:string" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Namespaces to use for adding JsonExtensionData properties and attribute</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
|
||||
@@ -1217,13 +1217,6 @@ public record Victim
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> OtherProperties
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
public record SessionCounters
|
||||
@@ -1335,13 +1328,6 @@ public record Aggressor
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> OtherProperties
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
public record DamageHistory
|
||||
|
||||
@@ -88,14 +88,6 @@ public record Item
|
||||
get;
|
||||
set;
|
||||
}
|
||||
#if !DEBUG
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object>? ExtensionData
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public record HideoutItem
|
||||
@@ -384,14 +376,6 @@ public record Upd
|
||||
get;
|
||||
set;
|
||||
}
|
||||
#if !DEBUG
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object>? ExtensionData
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public record LockableKeyComponent
|
||||
|
||||
@@ -3517,9 +3517,6 @@ public record Props
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//[JsonExtensionData]
|
||||
//public Dictionary<string, object> OtherProperties { get; set; }
|
||||
}
|
||||
|
||||
public record WeaponRecoilSettings
|
||||
|
||||
@@ -547,13 +547,6 @@ public record PresetBatch
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalData
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
public record WalletLootSettings
|
||||
|
||||
@@ -22,18 +22,18 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.Hashing" Version="9.0.5"/>
|
||||
<PackageReference Include="FastCloner" Version="3.3.4"/>
|
||||
|
||||
<!--
|
||||
We are using Fody to make all methods virtual, this is a perfect use case for us to allow modders to override
|
||||
We are using Fody to make all methods virtual, this is a perfect use case for us to allow modders to override
|
||||
any behaviour they need and replace any classes.
|
||||
-->
|
||||
<PackageReference Include="Fody" Version="6.9.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Virtuosity.Fody" Version="3.1.2" >
|
||||
<PackageReference Include="Virtuosity.Fody" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<WeaverFiles Include="$(SolutionDir)Libraries\FodyWeavers\JsonExtensionData.Fody\bin\$(Configuration)\netstandard2.0\JsonExtensionData.Fody.dll"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -6,6 +6,9 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPTarkov.Server", "SPTarkov.Server\SPTarkov.Server.csproj", "{1F5ED9C6-8B1F-4776-85AB-B387CBBC5557}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPTarkov.Server.Core", "Libraries\SPTarkov.Server.Core\SPTarkov.Server.Core.csproj", "{AC8643DC-8779-4B4A-BBDA-2D4CC466F765}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8} = {905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{6C0681F9-4013-4579-82DA-0A9297984FD3}"
|
||||
EndProject
|
||||
@@ -27,6 +30,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Be
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPTarkov.Reflection", "Libraries\SPTarkov.Reflection\SPTarkov.Reflection.csproj", "{9073A593-A2F5-471E-9678-B896A7226FD4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Fody", "Fody", "{61D0AD50-5B86-41E6-8B19-F11944AC3EE4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JsonExtensionData", "JsonExtensionData", "{2938742B-34FA-47F2-A50B-E0470FB1D807}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonExtensionData.Fody", "Libraries\FodyWeavers\JsonExtensionData.Fody\JsonExtensionData.Fody.csproj", "{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -73,6 +82,10 @@ Global
|
||||
{9073A593-A2F5-471E-9678-B896A7226FD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9073A593-A2F5-471E-9678-B896A7226FD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9073A593-A2F5-471E-9678-B896A7226FD4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -85,5 +98,8 @@ Global
|
||||
{4B973AC0-0C60-4853-9AF7-7CB69127473E} = {F084DDFD-89F3-44F9-89C3-5CA11F4CDEEF}
|
||||
{C24B1FEB-F8AC-434E-998D-5DA4D1687295} = {587959C2-5AFA-4B77-B327-566610F9A289}
|
||||
{9073A593-A2F5-471E-9678-B896A7226FD4} = {F084DDFD-89F3-44F9-89C3-5CA11F4CDEEF}
|
||||
{61D0AD50-5B86-41E6-8B19-F11944AC3EE4} = {F084DDFD-89F3-44F9-89C3-5CA11F4CDEEF}
|
||||
{2938742B-34FA-47F2-A50B-E0470FB1D807} = {61D0AD50-5B86-41E6-8B19-F11944AC3EE4}
|
||||
{905FBA04-D73A-4A46-930B-1B0C3A7C4EB8} = {2938742B-34FA-47F2-A50B-E0470FB1D807}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Reference in New Issue
Block a user