Skip to content

Commit

Permalink
Merge branch 'feature-config'
Browse files Browse the repository at this point in the history
  • Loading branch information
dymanoid committed Jul 28, 2018
2 parents 55c8f58 + 77abb9f commit 16cc6c4
Show file tree
Hide file tree
Showing 19 changed files with 244 additions and 103 deletions.
124 changes: 95 additions & 29 deletions src/RealTime/Config/ConfigurationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,80 +7,146 @@ namespace RealTime.Config
using System;
using System.IO;
using System.Xml.Serialization;
using RealTime.Core;
using RealTime.Tools;

/// <summary>
/// A static class that loads and stores the <see cref="RealTimeConfig"/> objects.
/// An XML file 'RealTime.xml' in the default (game) directory is used as a storage.
/// </summary>
internal static class ConfigurationProvider
internal sealed class ConfigurationProvider : IStorageData
{
private const string StorageId = "RealTimeConfiguration";
private static readonly string SettingsFileName = typeof(ConfigurationProvider).Assembly.GetName().Name + ".xml";

/// <summary>Occurs when the <see cref="Configuration"/> instance changes.</summary>
public event EventHandler Changed;

/// <summary>Gets the current configuration.</summary>
public RealTimeConfig Configuration { get; private set; }

/// <summary>Gets a value indicating whether the <see cref="Configuration"/> instance is a default configuration.</summary>
public bool IsDefault { get; private set; }

/// <summary>Gets an unique ID of this storage data set.</summary>
string IStorageData.StorageDataId => StorageId;

/// <summary>
/// Loads the configuration object from the serialized storage. If no storage is available,
/// Loads the default configuration object from the serialized storage. If no storage is available,
/// returns a new <see cref="RealTimeConfig"/> object with its values set to defaults.
/// </summary>
///
/// <returns>A <see cref="RealTimeConfig"/> object containing the configuration.</returns>
public static RealTimeConfig LoadConfiguration()
public void LoadDefaultConfiguration()
{
try
{
if (!File.Exists(SettingsFileName))
if (File.Exists(SettingsFileName))
{
return new RealTimeConfig(true);
using (var stream = new FileStream(SettingsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
Configuration = Deserialize(stream);
}
}

return Deserialize();
}
catch (Exception ex)
{
Log.Warning($"The 'Real Time' mod has encountered an error while trying to load the configuration, error message: " + ex);
return new RealTimeConfig(true);
Log.Error("The 'Real Time' mod cannot load its default configuration, error message: " + ex);
}

IsDefault = true;
if (Configuration == null)
{
Configuration = new RealTimeConfig(true);
}

OnChanged();
}

/// <summary>
/// Stores the specified <paramref name="config"/> object to the storage.
/// Stores the current configuration object to the storage as a default configuration.
/// </summary>
///
/// <exception cref="ArgumentNullException">Thrown when the argument is null.</exception>
///
/// <param name="config">A <see cref="RealTimeConfig"/> object to store.</param>
public static void SaveConfiguration(RealTimeConfig config)
public void SaveDefaultConfiguration()
{
if (config == null)
if (Configuration == null)
{
throw new ArgumentNullException(nameof(config));
Configuration = new RealTimeConfig(true);
}

try
{
Serialize(config);
using (var stream = new FileStream(SettingsFileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
Serialize(Configuration, stream);
}
}
catch (Exception ex)
{
Log.Error("The 'Real Time' mod cannot save its configuration, error message: " + ex);
Log.Error("The 'Real Time' mod cannot save its default configuration, error message: " + ex);
}
}

private static RealTimeConfig Deserialize()
/// <summary>Reads the data set from the specified <see cref="Stream" />.</summary>
/// <param name="source">A <see cref="Stream" /> to read the data set from.</param>
void IStorageData.ReadData(Stream source)
{
var serializer = new XmlSerializer(typeof(RealTimeConfig));
using (var sr = new StreamReader(SettingsFileName))
Configuration = Deserialize(source);
if (Configuration == null)
{
LoadDefaultConfiguration();
}
else
{
return ((RealTimeConfig)serializer.Deserialize(sr)).MigrateWhenNecessary().Validate();
OnChanged();
}

IsDefault = false;
}

private static void Serialize(RealTimeConfig config)
/// <summary>Stores the data set to the specified <see cref="Stream" />.</summary>
/// <param name="target">A <see cref="Stream" /> to write the data set to.</param>
void IStorageData.StoreData(Stream target)
{
var serializer = new XmlSerializer(typeof(RealTimeConfig), SerializationTools.IgnoreObsoleteProperties(config));
using (var sw = new StreamWriter(SettingsFileName))
if (Configuration != null)
{
serializer.Serialize(sw, config);
Serialize(Configuration, target);
}
}

private static RealTimeConfig Deserialize(Stream source)
{
try
{
var serializer = new XmlSerializer(typeof(RealTimeConfig));
using (var sr = new StreamReader(source))
{
return ((RealTimeConfig)serializer.Deserialize(sr)).MigrateWhenNecessary().Validate();
}
}
catch (Exception ex)
{
Log.Warning($"The 'Real Time' mod has encountered an error while trying to load the configuration, error message: " + ex);
return null;
}
}

private static void Serialize(RealTimeConfig config, Stream target)
{
try
{
var serializer = new XmlSerializer(typeof(RealTimeConfig), SerializationTools.IgnoreObsoleteProperties(config));
using (var sw = new StreamWriter(target))
{
serializer.Serialize(sw, config);
}
}
catch (Exception ex)
{
Log.Error("The 'Real Time' mod cannot save its configuration, error message: " + ex);
}
}

private void OnChanged()
{
Changed?.Invoke(this, EventArgs.Empty);
}
}
}
50 changes: 29 additions & 21 deletions src/RealTime/Core/RealTimeCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@ private RealTimeCore(TimeAdjustment timeAdjustment, CustomTimeBar timeBar, RealT
/// Runs the mod by activating its parts.
/// </summary>
///
/// <exception cref="ArgumentNullException">Thrown when <paramref name="config"/> or <paramref name="localizationProvider"/> is null.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="configProvider"/> or <paramref name="localizationProvider"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="rootPath"/> is null or an empty string.</exception>
///
/// <param name="config">The configuration to run with.</param>
/// <param name="configProvider">The configuration provider that provides the mod's configuration.</param>
/// <param name="rootPath">The path to the mod's assembly. Additional files are stored here too.</param>
/// <param name="localizationProvider">The <see cref="ILocalizationProvider"/> to use for text translation.</param>
///
/// <returns>A <see cref="RealTimeCore"/> instance that can be used to stop the mod.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This is the entry point and needs to instantiate all parts")]
public static RealTimeCore Run(RealTimeConfig config, string rootPath, ILocalizationProvider localizationProvider)
public static RealTimeCore Run(ConfigurationProvider configProvider, string rootPath, ILocalizationProvider localizationProvider)
{
if (config == null)
if (configProvider == null)
{
throw new ArgumentNullException(nameof(config));
throw new ArgumentNullException(nameof(configProvider));
}

if (string.IsNullOrEmpty(rootPath))
Expand Down Expand Up @@ -96,7 +96,12 @@ public static RealTimeCore Run(RealTimeConfig config, string rootPath, ILocaliza
return null;
}

var timeInfo = new TimeInfo(config);
if (RealTimeStorage.CurrentLevelStorage != null)
{
LoadStorageData(new[] { configProvider }, RealTimeStorage.CurrentLevelStorage);
}

var timeInfo = new TimeInfo(configProvider.Configuration);
var buildingManager = new BuildingManagerConnection();
var randomizer = new GameRandomizer();

Expand All @@ -112,21 +117,21 @@ public static RealTimeCore Run(RealTimeConfig config, string rootPath, ILocaliza
weatherInfo);

var eventManager = new RealTimeEventManager(
config,
configProvider.Configuration,
CityEventsLoader.Instance,
new EventManagerConnection(),
buildingManager,
randomizer,
timeInfo);

if (!SetupCustomAI(timeInfo, config, gameConnections, eventManager))
if (!SetupCustomAI(timeInfo, configProvider.Configuration, gameConnections, eventManager))
{
Log.Error("The 'Real Time' mod failed to setup the customized AI and will now be deactivated.");
patcher.Revert();
return null;
}

var timeAdjustment = new TimeAdjustment(config);
var timeAdjustment = new TimeAdjustment(configProvider.Configuration);
DateTime gameDate = timeAdjustment.Enable();
SimulationHandler.CitizenProcessor.UpdateFrameDuration();

Expand All @@ -141,19 +146,24 @@ public static RealTimeCore Run(RealTimeConfig config, string rootPath, ILocaliza
SimulationHandler.NewDay += result.CityEventsChanged;

SimulationHandler.TimeAdjustment = timeAdjustment;
SimulationHandler.DayTimeSimulation = new DayTimeSimulation(config);
SimulationHandler.DayTimeSimulation = new DayTimeSimulation(configProvider.Configuration);
SimulationHandler.EventManager = eventManager;
SimulationHandler.WeatherInfo = weatherInfo;
SimulationHandler.Buildings = BuildingAIPatches.RealTimeAI;
SimulationHandler.Buildings.UpdateFrameDuration();
SimulationHandler.Buildings.InitializeLightState();

AwakeSleepSimulation.Install(config);
AwakeSleepSimulation.Install(configProvider.Configuration);

RealTimeStorage.CurrentLevelStorage.GameSaving += result.GameSaving;
result.storageData.Add(eventManager);
result.storageData.Add(ResidentAIPatch.RealTimeAI.GetStorageService());
result.LoadStorageData(RealTimeStorage.CurrentLevelStorage);
if (RealTimeStorage.CurrentLevelStorage != null)
{
LoadStorageData(result.storageData, RealTimeStorage.CurrentLevelStorage);
}

result.storageData.Add(configProvider);

result.Translate(localizationProvider);

Expand Down Expand Up @@ -278,19 +288,18 @@ private static void CustomTimeBarCityEventClick(object sender, CustomTimeBarClic
CameraHelper.NavigateToBuilding(e.CityEventBuildingId);
}

private void CityEventsChanged(object sender, EventArgs e)
{
timeBar.UpdateEventsDisplay(eventManager.CityEvents);
}

private void LoadStorageData(RealTimeStorage storage)
private static void LoadStorageData(IEnumerable<IStorageData> storageData, RealTimeStorage storage)
{
foreach (IStorageData item in storageData)
{
storage.Deserialize(item);
Log.Debug("The 'Real Time' mod loaded its data from container " + item.StorageDataId);
}
}

Log.Info("The 'Real Time' mod successfully loaded its data for the current game.");
private void CityEventsChanged(object sender, EventArgs e)
{
timeBar.UpdateEventsDisplay(eventManager.CityEvents);
}

private void GameSaving(object sender, EventArgs e)
Expand All @@ -299,9 +308,8 @@ private void GameSaving(object sender, EventArgs e)
foreach (IStorageData item in storageData)
{
storage.Serialize(item);
Log.Debug("The 'Real Time' mod stored its data in the current game for container " + item.StorageDataId);
}

Log.Info("The 'Real Time' mod successfully stored its data in the current game.");
}
}
}
Loading

0 comments on commit 16cc6c4

Please sign in to comment.