Skip to content

Commit

Permalink
Merge pull request #17 from BUTR/dev
Browse files Browse the repository at this point in the history
v1.0.10
  • Loading branch information
Aragas authored Oct 4, 2020
2 parents 9018607 + 6564c5f commit dc058da
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 92 deletions.
2 changes: 1 addition & 1 deletion build/common.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<!--Development Variables-->
<PropertyGroup>
<!--Module Version-->
<Version>1.0.9</Version>
<Version>1.0.10</Version>
<!--Harmony Version-->
<HarmonyVersion>2.0.2</HarmonyVersion>
<!--Current Bannerlord Stable Version-->
Expand Down
4 changes: 4 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
---------------------------------------------------------------------------------------------------
Version: 1.0.10
Game Versions: e1.4.3,e1.5.0,e1.5.1,e1.5.2,e1.5.3
* Switched serialization from binary to json
---------------------------------------------------------------------------------------------------
Version: 1.0.9
Game Versions: e1.4.3,e1.5.0,e1.5.1,e1.5.2,e1.5.3
* Better nullable handling
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using Bannerlord.ButterLib.CampaignIdentifier;
using Bannerlord.ButterLib.Implementation.Common;
using Bannerlord.ButterLib.Common.Extensions;

using Microsoft.Extensions.DependencyInjection;

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using TaleWorlds.CampaignSystem;
using TaleWorlds.Core;
Expand All @@ -26,20 +26,17 @@ internal sealed class CampaignDescriptorManager
private const string InquiryLowerBody =
"{=zed49rdkQR}Select '{NEW_ID}' if the loaded save is a separate campaign that has nothing to do with the suggested options and a new ID should be assigned to it.";

private static readonly string ExistingCampaignDescriptorsLogFile = Path.Combine(Utilities.GetConfigsPath(), "ButterLib", "CampaignIdentifier", "ExistingCampaignIdentifiers.bin");

private ICampaignDescriptorProvider? _campaignDescriptorSerializer;
private ICampaignDescriptorProvider CampaignDescriptorSerializer => _campaignDescriptorSerializer ??=
ButterLibSubModule.Instance?.GetServiceProvider()?.GetRequiredService<ICampaignDescriptorProvider>() ?? new JsonCampaignDescriptorProvider();

[SaveableField(1)]
private CampaignDescriptorImplementation _campaignDescriptor;
private CampaignDescriptorImplementation _campaignDescriptor = null!; // Won't be null when properly accessed.

private CampaignDescriptorImplementation? _descriptorToBeAssigned;

internal CampaignDescriptorImplementation CampaignDescriptor => _campaignDescriptor;

internal CampaignDescriptorManager()
{
_campaignDescriptor = null!; // Won't be null when properly accessed.
}
internal void GenerateNewGameDescriptor()
{
_campaignDescriptor = new CampaignDescriptorImplementation(Hero.MainHero);
Expand Down Expand Up @@ -127,35 +124,22 @@ private void UpdateSavedDescriptors()
var existingCampaignDescriptors = LoadExistingDescriptors();
existingCampaignDescriptors.Add(_campaignDescriptor);

var path = Path.GetDirectoryName(ExistingCampaignDescriptorsLogFile);
if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
{
Directory.CreateDirectory(path!);
}

using FileStream fileStream = File.OpenWrite(ExistingCampaignDescriptorsLogFile);
var binaryFormatter = new BinaryFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
Binder = new ButterLibSerializationBinder()
};
binaryFormatter.Serialize(fileStream, existingCampaignDescriptors);
CampaignDescriptorSerializer.Save(existingCampaignDescriptors);
}

private static List<CampaignDescriptorImplementation> LoadExistingDescriptors()
private List<CampaignDescriptor> LoadExistingDescriptors()
{
if (!File.Exists(ExistingCampaignDescriptorsLogFile))
// We are stuck with it for backwards compatibility sake
var binaryFile = Path.Combine(Utilities.GetConfigsPath(), "ButterLib", "CampaignIdentifier", "ExistingCampaignIdentifiers.bin");
var binaryDescriptors = new List<CampaignDescriptor>();
if (File.Exists(binaryFile))
{
return new List<CampaignDescriptorImplementation>();
var binaryCampaignDescriptorProvider = new BinaryCampaignDescriptorProvider();
binaryDescriptors.AddRange(binaryCampaignDescriptorProvider.Load());
File.Delete(binaryFile);
}

using FileStream fileStream = File.OpenRead(ExistingCampaignDescriptorsLogFile);
var binaryFormatter = new BinaryFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
Binder = new ButterLibSerializationBinder()
};
return (List<CampaignDescriptorImplementation>)binaryFormatter.Deserialize(fileStream);
return CampaignDescriptorSerializer.Load().Concat(binaryDescriptors).ToList();
}

internal void Sync()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using Bannerlord.ButterLib.CampaignIdentifier;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using TaleWorlds.Engine;

using Path = System.IO.Path;

namespace Bannerlord.ButterLib.Implementation.CampaignIdentifier.CampaignBehaviors
{
internal sealed class BinaryCampaignDescriptorProvider : ICampaignDescriptorProvider
{
private static readonly string ExistingCampaignDescriptorsLogFile =
Path.Combine(Utilities.GetConfigsPath(), "ButterLib", "CampaignIdentifier", "ExistingCampaignIdentifiers.bin");

public IEnumerable<CampaignDescriptor> Load()
{
try
{
var path = Path.GetDirectoryName(ExistingCampaignDescriptorsLogFile);
if (string.IsNullOrEmpty(path) || !Directory.Exists(path) || !File.Exists(ExistingCampaignDescriptorsLogFile))
return Enumerable.Empty<CampaignDescriptor>();

using var fileStream = File.OpenRead(ExistingCampaignDescriptorsLogFile);
var binaryFormatter = new BinaryFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
Binder = new ButterLibSerializationBinder()
};
return (List<CampaignDescriptorImplementation>) binaryFormatter.Deserialize(fileStream);
}
catch (Exception e) when (e is SerializationException)
{
return Enumerable.Empty<CampaignDescriptor>();
}
}

public void Save(IEnumerable<CampaignDescriptor> descriptors)
{
var path = Path.GetDirectoryName(ExistingCampaignDescriptorsLogFile);
if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
Directory.CreateDirectory(path!);

using var fileStream = File.OpenWrite(ExistingCampaignDescriptorsLogFile);
var binaryFormatter = new BinaryFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
Binder = new ButterLibSerializationBinder()
};
binaryFormatter.Serialize(fileStream, descriptors.ToList());
}

internal sealed class ButterLibSerializationBinder : SerializationBinder
{
public override Type? BindToType(string assemblyName, string typeName)
{
if (assemblyName.StartsWith("Bannerlord.ButterLib.Implementation"))
return typeof(ButterLibSerializationBinder).Assembly.GetType(typeName);

var type = Type.GetType($"{typeName}, {assemblyName}");
if (type != null)
return type;

var tokens = typeName.Split(new [] {"[[", "]]", "],["}, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 1)
return Type.GetType(typeName, true);

var generic = tokens[0];
var genericTypeArgs = new List<string>();
foreach (var token in tokens.Skip(1))
{
var (typeName1, assemblyName1) = GetTokenInfo(token);
var type1 = assemblyName1.StartsWith("Bannerlord.ButterLib.Implementation")
? typeof(ButterLibSerializationBinder).Assembly.GetType(typeName1)
: Type.GetType($"{typeName1}, {assemblyName1}", true);
genericTypeArgs.Add(type1.AssemblyQualifiedName);
}

return Type.GetType($"{generic}[[{string.Join("],[", genericTypeArgs)}]]", true);
}

private static (string TypeName, string AssemblyName) GetTokenInfo(string str)
{
var split = str.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
return (split[0].Trim(), string.Join(",", split.Skip(1)).Trim());
}

public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = "Bannerlord.ButterLib.Implementation";
typeName = serializedType.FullName!;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,14 @@ namespace Bannerlord.ButterLib.Implementation.CampaignIdentifier.CampaignBehavio
/// <summary>Custom behavior used by CampaignIdentifier.</summary>
internal sealed class CampaignIdentifierBehavior : CampaignBehaviorBase
{
private CampaignDescriptorManager _descriptorManager;
private CampaignDescriptorManager _descriptorManager = new CampaignDescriptorManager();

/// <summary>Alphanumeric campaign ID.</summary>
public string CampaignId => _descriptorManager.CampaignDescriptor.KeyValue;

/// <summary><see cref = "T:Bannerlord.ButterLib.CampaignIdentifier.CampaignDescriptor" /> object corresponding with the campaign.</summary>
public CampaignDescriptorImplementation CampaignDescriptor => _descriptorManager.CampaignDescriptor;

/// <summary>Initializes a new instance of the <see cref="CampaignIdentifierBehavior" />.</summary>
public CampaignIdentifierBehavior()
{
_descriptorManager = new CampaignDescriptorManager();
}

public override void RegisterEvents()
{
CampaignIdentifierEvents.OnDescriptorRelatedDataChangedEvent.AddNonSerializedListener(this, UpdateCampaignDescriptorOnCharacterOrClanModified);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Bannerlord.ButterLib.CampaignIdentifier;

using Newtonsoft.Json;

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using TaleWorlds.Engine;

using Path = System.IO.Path;

namespace Bannerlord.ButterLib.Implementation.CampaignIdentifier.CampaignBehaviors
{
internal sealed class JsonCampaignDescriptorProvider : ICampaignDescriptorProvider
{
private static readonly string ExistingCampaignDescriptorsLogFile =
Path.Combine(Utilities.GetConfigsPath(), "ButterLib", "CampaignIdentifier", "Existing.json");

public IEnumerable<CampaignDescriptor> Load()
{
var path = Path.GetDirectoryName(ExistingCampaignDescriptorsLogFile);
if (string.IsNullOrEmpty(path) || !Directory.Exists(path) || !File.Exists(ExistingCampaignDescriptorsLogFile))
return Enumerable.Empty<CampaignDescriptor>();

var success = true;
var settings = new JsonSerializerSettings
{
Error = (sender, args) => { success = false; args.ErrorContext.Handled = true; },
MissingMemberHandling = MissingMemberHandling.Error
};
using var fileStream = File.OpenRead(ExistingCampaignDescriptorsLogFile);
var buffer = ReadFully(fileStream);
var result = JsonConvert.DeserializeObject<IEnumerable<CampaignDescriptor>>(Encoding.UTF8.GetString(buffer), settings);
return success ? result : Enumerable.Empty<CampaignDescriptor>();
}

public void Save(IEnumerable<CampaignDescriptor> descriptors)
{
var path = Path.GetDirectoryName(ExistingCampaignDescriptorsLogFile);
if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
Directory.CreateDirectory(path!);

using var fileStream = File.OpenWrite(ExistingCampaignDescriptorsLogFile);
using var writer = new StreamWriter(fileStream);
var buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(descriptors, Formatting.Indented));
fileStream.Write(buffer, 0, buffer.Length);
}

private static byte[] ReadFully(Stream input)
{
var buffer = new byte[16*1024];
using MemoryStream ms = new MemoryStream();
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
ms.Write(buffer, 0, read);
return ms.ToArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ namespace Bannerlord.ButterLib.Implementation.CampaignIdentifier
/// Also stores some general description on the campaign, based on a specified hero
/// (default is the initial player character in the current campaign).
/// </remarks>
[Serializable]
[SaveableClass(1)]
[SaveableClass(1), Serializable]
internal sealed class CampaignDescriptorImplementation : CampaignDescriptor, ISerializable
{
//Consts
Expand Down Expand Up @@ -101,6 +100,7 @@ internal CampaignDescriptorImplementation(string value, Dictionary<DescriptorAtt
private CampaignDescriptorImplementation(SerializationInfo info, StreamingContext context)
{
_value = info.GetString(nameof(KeyValue));
// Do not fix typo
_attributes = (Dictionary<DescriptorAttribute, object>) info.GetValue("DecriptorAttributes", typeof(Dictionary<DescriptorAttribute, object>));
_baseHero = null!; // Serialization will do it's thing.
}
Expand All @@ -110,6 +110,7 @@ private CampaignDescriptorImplementation(SerializationInfo info, StreamingContex
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(nameof(KeyValue), _value);
// Do not fix typo
info.AddValue("DecriptorAttributes", _attributes);
}

Expand Down

This file was deleted.

1 change: 1 addition & 0 deletions src/Bannerlord.ButterLib.Implementation/SubModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ protected override void OnSubModuleLoad()
services.AddScoped(typeof(DistanceMatrix<>), typeof(DistanceMatrixImplementation<>));
services.AddSingleton<DistanceMatrixStatic, DistanceMatrixStaticImplementation>();
services.AddSingleton<ICampaignExtensions, CampaignExtensionsImplementation>();
services.AddTransient<ICampaignDescriptorProvider, JsonCampaignDescriptorProvider>();

DelayedSubModuleManager.Register<StoryModeSubModule>();
DelayedSubModuleManager.Subscribe<StoryModeSubModule, SubModule>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Collections.Generic;

namespace Bannerlord.ButterLib.CampaignIdentifier
{
public interface ICampaignDescriptorProvider
{
IEnumerable<CampaignDescriptor> Load();
void Save(IEnumerable<CampaignDescriptor> descriptors);
}
}

0 comments on commit dc058da

Please sign in to comment.