Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options API #1378

Merged
merged 7 commits into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions TLM/TLM/Lifecycle/TMPELifecycle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ internal void Load() {

internal void Unload() {
try {
Options.Available = false;

GeometryNotifierDisposable?.Dispose();
GeometryNotifierDisposable = null;

Expand Down
34 changes: 34 additions & 0 deletions TLM/TLM/Manager/Impl/OptionsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace TrafficManager.Manager.Impl {
using TrafficManager.Lifecycle;
using JetBrains.Annotations;
using TrafficManager.Util;
using System.Reflection;

public class OptionsManager
: AbstractCustomManager,
Expand All @@ -29,6 +30,31 @@ private static SimulationAccuracy ConvertToSimulationAccuracy(byte value) {
return SimulationAccuracy.MaxValue - value;
}

/// <summary>
/// API method for external mods to get option values by name.
/// </summary>
/// <typeparam name="TVal">Option type, eg. <c>bool</c>.</typeparam>
/// <param name="optionName">Name of the option in <see cref="Options"/>.</param>
/// <param name="value">The option value, if found, otherwise <c>default</c> for <typeparamref name="TVal"/>.</param>
/// <returns>Returns <c>true</c> if successful, or <c>false</c> if there was a problem (eg. option not found, wrong TVal, etc).</returns>
/// <remarks>Check <see cref="OptionsAreSafeToQuery"/> first before trying to get an option value.</remarks>
public bool TryGetOptionByName<TVal>(string optionName, out TVal value) {
if (!Options.Available) {
value = default;
return false;
}

var field = typeof(Options).GetField(optionName, BindingFlags.Static | BindingFlags.Public);

if (field == null || field.FieldType is not TVal) {
value = default;
return false;
}

value = (TVal)field.GetValue(null);
return true;
}

/// <summary>
/// Converts SimulationAccuracy to SimulationAccuracy
/// </summary>
Expand Down Expand Up @@ -74,6 +100,8 @@ private static void ToCheckbox([NotNull] byte[] data, uint idx, ILegacySerializa

public bool LoadData(byte[] data) {
try {
Options.Available = false;

Log.Info($"OptionsManager.LoadData: {data.Length} bytes");

GeneralTab.SetSimulationAccuracy(ConvertToSimulationAccuracy(LoadByte(data, idx: 0)));
Expand Down Expand Up @@ -154,10 +182,16 @@ public bool LoadData(byte[] data) {

ToCheckbox(data, idx: 59, OverlaysTab.ShowDefaultSpeedSubIcon, false);

Options.Available = true;

return true;
}
catch (Exception ex) {
ex.LogException();

// even though there was error, the options are now available for querying
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh .. are you sure about that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If the load method fails, the options will just have default values but TM:PE will still be fully operable.

The goal was to prevent other mods reading options either prior to options being set, or during save/load, or from main menu.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But whats the point of playing with default options?
And what happens if I save the game with those defaults?
And what if another mod makes descisions based on that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If its fine for krzy its fine for me though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't try to save anything if failed to load, that won't overwrite old data, so in case of a bug in load code it could be loaded after applying hotfix - IIRC game is cloning mod data container so even if you not longer have a mod, data will be migrated to new saves.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prior to this PR, what would the difference be?

If the data fails to load, the LoadData() method will still return false just like it previously did, which results in loadingSucceeded = false at the call site - that currently has no effect on whether SaveData() gets invoked later.

I've created an issue to track the need to prevent data save should data fail to load.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is ok, you can merge :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Options.Available = true;

return false;
}
}
Expand Down
10 changes: 10 additions & 0 deletions TLM/TLM/State/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class Options : MonoBehaviour {
// private static UITextField pathCostMultiplicator2Field = null;
#endif

// Likely to change or be removed in future
[Flags]
public enum PersistTo {
None = 0,
Expand All @@ -33,6 +34,15 @@ public enum PersistTo {
GlobalOrSavegame = Global | Savegame,
}

/// <summary>
/// When <c>true</c>, options are safe to query.
/// </summary>
/// <remarks>
/// Is set <c>true</c> after options are loaded via <see cref="Manager.Impl.OptionsManager"/>.
/// Is set <c>false</c> while options are being saved, and also when level unloads.
/// </remarks>
public static bool Available = false;

public static bool instantEffects = true;
public static bool individualDrivingStyle = true;
public static int recklessDrivers = 3;
Expand Down
11 changes: 10 additions & 1 deletion TLM/TMPE.API/Manager/IOptionsManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace TrafficManager.API.Manager {
namespace TrafficManager.API.Manager {
/// <summary>
/// Manages mod options
/// </summary>
Expand All @@ -8,5 +8,14 @@ public interface IOptionsManager : ICustomDataManager<byte[]> {
/// </summary>
/// <returns>true if changes may be published, false otherwise</returns>
bool MayPublishSegmentChanges();

/// <summary>
/// Get current value of TMPE mod option from <see cref="Options"/>.
/// </summary>
/// <typeparam name="TVal">Option type, eg. <c>bool</c>.</typeparam>
/// <param name="optionName">Name of the option in <see cref="Options"/>.</param>
/// <param name="value">The option value, if found, otherwise <c>default</c> for <typeparamref name="TVal"/>.</param>
/// <returns>Returns <c>true</c> if successful, or <c>false</c> if there was a problem (eg. option not found, wrong TVal, etc).</returns>
bool TryGetOptionByName<TVal>(string optionName, out TVal value);
}
}