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

Fix transport lines #325

Merged
merged 9 commits into from
Aug 21, 2023
32 changes: 27 additions & 5 deletions src/api/Helpers/ReflectionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,19 @@ public static T Call<T>(Type type, string name, params object[] param)

public static object Call(Type type, string name, params object[] param)
{
return type.GetMethod(name, AllAccessFlags, null, param.Select(p => p.GetType()).ToArray(), null)
?.Invoke(null, param);
return Call(type, name, param.Select(p => p.GetType()).ToArray(), param);
}

public static object Call(Type type, string name, Type[] types, params object[] param)
{
MethodInfo methodInfo = type.GetMethod(name, AllAccessFlags, null, types, null);
if (methodInfo == null)
{
Log.Error($"ReflectionHelper::Call failed on {type}::{name}");
return null;
}

return methodInfo.Invoke(null, param);
}

public static T Call<T>(object obj, string name, params object[] param)
Expand All @@ -61,13 +72,24 @@ public static object Call(object obj, string name, params object[] param)

public static object Call(object obj, string name, Type[] types, params object[] param)
{
return obj.GetType().GetMethod(name, AllAccessFlags, null, types, null)
?.Invoke(obj, param);
MethodInfo methodInfo = obj.GetType().GetMethod(name, AllAccessFlags, null, types, null);
if (methodInfo == null)
{
Log.Error($"ReflectionHelper::Call failed on {obj.GetType()}.{name}");
return null;
}
return methodInfo.Invoke(obj, param);
}

public static void SetAttr(object obj, string attribute, object value)
{
obj.GetType().GetField(attribute, AllAccessFlags)?.SetValue(obj, value);
FieldInfo field = obj.GetType().GetField(attribute, AllAccessFlags);
if (field == null)
{
Log.Error($"ReflectionHelper::SetAttr failed on {obj.GetType()}.{attribute}");
return;
}
field.SetValue(obj, value);
}

public static T GetAttr<T>(object obj, string attribute)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,93 @@ namespace CSM.BaseGame.Commands.Data.TransportLines
public class TransportLineTempCommand : CommandBase
{
/// <summary>
/// The new prefab info index.
/// Current prefab info index.
/// </summary>
[ProtoMember(1)]
public ushort InfoIndex { get; set; }

/// <summary>
/// The source line this temp line refers to.
/// If SetEditLine should be called with the force parameter.
/// </summary>
[ProtoMember(2)]
public ushort SourceLine { get; set; }
public bool ForceSetEditLine { get; set; }

/// <summary>
/// The index of the stop that will be moved.
/// The current temp line id.
/// </summary>
[ProtoMember(3)]
public int MoveIndex { get; set; }
public ushort TempLine { get; set; }

/// <summary>
/// The index of the stop that will be added.
/// The source line this temp line refers to.
/// </summary>
[ProtoMember(4)]
public int AddIndex { get; set; }
public ushort SourceLine { get; set; }

/// <summary>
/// The position of a new stop.
/// A line id to be released, 0 otherwise.
/// </summary>
[ProtoMember(5)]
public ushort ReleaseLine { get; set; }

/// <summary>
/// If the temp line should be created.
/// </summary>
[ProtoMember(6)]
public bool CreateLine { get; set; }

/// <summary>
/// The index of a previously added stop.
/// </summary>
[ProtoMember(7)]
public int LastAddIndex { get; set; }

/// <summary>
/// The index of a previously moved stop.
/// </summary>
[ProtoMember(8)]
public int LastMoveIndex { get; set; }

/// <summary>
/// The position of a previously moved stop.
/// </summary>
[ProtoMember(9)]
public Vector3 LastMovePos { get; set; }

/// <summary>
/// The position of a previously added stop.
/// </summary>
[ProtoMember(10)]
public Vector3 LastAddPos { get; set; }

/// <summary>
/// The index of the stop that will be added.
/// </summary>
[ProtoMember(11)]
public int AddIndex { get; set; }

/// <summary>
/// The index of the stop that will be moved.
/// </summary>
[ProtoMember(12)]
public int MoveIndex { get; set; }

/// <summary>
/// The position to add a new stop to.
/// </summary>
[ProtoMember(13)]
public Vector3 AddPos { get; set; }

/// <summary>
/// If the line has fixed platforms.
/// </summary>
[ProtoMember(6)]
[ProtoMember(14)]
public bool FixedPlatform { get; set; }

/// <summary>
/// The list of generated Array16 ids.
/// </summary>
[ProtoMember(7)]
[ProtoMember(15)]
public ushort[] Array16Ids { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ protected override void Handle(EconomyPayLoanCommand command)
EconomyPanel panel = typeof(ToolsModifierControl).GetField("m_EconomyPanel", ReflectionHelper.AllAccessFlags)?.GetValue(null) as EconomyPanel;
if (panel != null)
{
SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => ReflectionHelper.Call(panel, "PopulateLoansTab"));
SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => ReflectionHelper.Call(panel, "UpdateLoansTab"));
}

IgnoreHelper.Instance.EndIgnore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected override void Handle(EconomyTakeLoanCommand command)
EconomyPanel panel = typeof(ToolsModifierControl).GetField("m_EconomyPanel", ReflectionHelper.AllAccessFlags)?.GetValue(null) as EconomyPanel;
if (panel != null)
{
SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => ReflectionHelper.Call(panel, "PopulateLoansTab"));
SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => ReflectionHelper.Call(panel, "UpdateLoansTab"));
}

IgnoreHelper.Instance.EndIgnore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ protected override void Handle(TransportLineInitCommand command)
TransportTool tool = Singleton<ToolSimulator>.instance.GetTool<TransportTool>(command.SenderId);

ReflectionHelper.SetAttr(tool, "m_errors", ToolBase.ToolErrors.Pending);
ReflectionHelper.SetAttr(tool, "m_lastMoveIndex", -2);
ReflectionHelper.SetAttr(tool, "m_lastAddIndex", -2);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected override void Handle(TransportLineSyncCommand command)
if (command.UpdatePaths)
{
ushort tempLine = ReflectionHelper.GetAttr<ushort>(tool, "m_tempLine");
TransportManager.instance.m_lines.m_buffer[(int)tempLine].UpdatePaths(tempLine);
TransportManager.instance.m_lines.m_buffer[tempLine].UpdatePaths(tempLine);
}

IgnoreHelper.Instance.EndIgnore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using CSM.BaseGame.Helpers;
using CSM.BaseGame.Injections;
using ColossalFramework;
using CSM.API;
using UnityEngine;

namespace CSM.BaseGame.Commands.Handler.TransportLines
{
Expand All @@ -14,15 +16,115 @@ protected override void Handle(TransportLineTempCommand command)
TransportTool tool = Singleton<ToolSimulator>.instance.GetTool<TransportTool>(command.SenderId);

ArrayHandler.StartApplying(command.Array16Ids, null);
IgnoreHelper.Instance.StartIgnore();

// Update received values
ReflectionHelper.SetAttr(tool, "m_lastAddIndex", command.LastAddIndex);
ReflectionHelper.SetAttr(tool, "m_lastAddPos", command.LastAddPos);
ReflectionHelper.SetAttr(tool, "m_lastMoveIndex", command.LastMoveIndex);
ReflectionHelper.SetAttr(tool, "m_lastMovePos", command.LastMovePos);

TransportInfo info = PrefabCollection<TransportInfo>.GetPrefab(command.InfoIndex);

IgnoreHelper.Instance.StartIgnore();
TransportManager instance = Singleton<TransportManager>.instance;

ReflectionHelper.Call(tool, "EnsureTempLine", info, command.SourceLine, command.MoveIndex, command.AddIndex, command.AddPos, command.FixedPlatform);
// The following is a reproduction of the "EnsureTempLine" logic adapted for the remote players (e.g. no "temporary" flag for the temp line)

IgnoreHelper.Instance.EndIgnore();
// Release lines
if (command.ReleaseLine != 0)
{
instance.ReleaseLine(command.ReleaseLine);
}

// Create temp line if necessary
if (command.CreateLine)
{
Singleton<TransportManager>.instance.CreateLine(out ushort tempLine,
ref Singleton<SimulationManager>.instance.m_randomizer, info, newNumber: false);
instance.m_lines.m_buffer[command.TempLine].m_flags |= TransportLine.Flags.Temporary;
if (tempLine != command.TempLine)
{
Log.Error("Received temp line id does not match reserved temp line id");
Chat.Instance.PrintGameMessage(Chat.MessageType.Error,
"Received temp line id does not match reserved temp line id. Please restart the game session.");
}
}

// Set temp line and copy current edit line to it if necessary
ReflectionHelper.SetAttr(tool, "m_tempLine", command.TempLine);
ReflectionHelper.Call(tool, "SetEditLine", new[] { typeof(ushort), typeof(bool) },
command.TempLine == 0 ? (ushort)0 : command.SourceLine, command.ForceSetEditLine);

// Update local working values after SetEditLine call
command.LastAddIndex = ReflectionHelper.GetAttr<int>(tool, "m_lastAddIndex");
command.LastAddPos = ReflectionHelper.GetAttr<Vector3>(tool, "m_lastAddPos");
command.LastMoveIndex = ReflectionHelper.GetAttr<int>(tool, "m_lastMoveIndex");
command.LastMovePos = ReflectionHelper.GetAttr<Vector3>(tool, "m_lastMovePos");

// Handle changes in indices
if (command.TempLine != 0)
{
if (command.LastMoveIndex != command.MoveIndex
|| command.LastAddIndex != command.AddIndex
|| command.LastAddPos != command.AddPos)
{
if (command.LastAddIndex != -2 &&
instance.m_lines.m_buffer[command.TempLine].RemoveStop(command.TempLine, command.LastAddIndex))
{
command.LastAddIndex = -2;
command.LastAddPos = Vector3.zero;
}

if (command.LastMoveIndex != -2 && instance.m_lines.m_buffer[command.TempLine]
.MoveStop(command.TempLine, command.LastMoveIndex, command.LastMovePos,
command.FixedPlatform))
{
command.LastMoveIndex = -2;
command.LastMovePos = Vector3.zero;
}

instance.m_lines.m_buffer[command.TempLine].CopyMissingPaths(command.SourceLine);
if (command.MoveIndex != -2 && instance.m_lines.m_buffer[command.TempLine]
.MoveStop(command.TempLine, command.MoveIndex, command.AddPos, command.FixedPlatform,
out var oldPos))
{
command.LastMoveIndex = command.MoveIndex;
command.LastMovePos = oldPos;
command.LastAddPos = command.AddPos;
}

if (command.AddIndex != -2 && instance.m_lines.m_buffer[command.TempLine]
.AddStop(command.TempLine, command.AddIndex, command.AddPos, command.FixedPlatform))
{
command.LastAddIndex = command.AddIndex;
command.LastAddPos = command.AddPos;
}

instance.UpdateLine(command.TempLine);

// Write updated values back to tool instances
ReflectionHelper.SetAttr(tool, "m_lastAddIndex", command.LastAddIndex);
ReflectionHelper.SetAttr(tool, "m_lastAddPos", command.LastAddPos);
ReflectionHelper.SetAttr(tool, "m_lastMoveIndex", command.LastMoveIndex);
ReflectionHelper.SetAttr(tool, "m_lastMovePos", command.LastMovePos);
}

// Some other stuff
instance.m_lines.m_buffer[command.TempLine].m_color =
instance.m_lines.m_buffer[command.SourceLine].m_color;
instance.m_lines.m_buffer[command.TempLine].m_flags &= ~TransportLine.Flags.Hidden;

if ((instance.m_lines.m_buffer[command.SourceLine].m_flags & TransportLine.Flags.CustomColor) != 0)
{
instance.m_lines.m_buffer[command.TempLine].m_flags |= TransportLine.Flags.CustomColor;
}
else
{
instance.m_lines.m_buffer[command.TempLine].m_flags &= ~TransportLine.Flags.CustomColor;
}
}

IgnoreHelper.Instance.EndIgnore();
ArrayHandler.StopApplying();
}
}
Expand Down
20 changes: 15 additions & 5 deletions src/basegame/Helpers/ToolSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ public void GetToolAndController<Tool>(int sender, out Tool tool, out ToolContro

public T GetTool<T>(int sender) where T : ToolBase
{
ToolBase tool;
if (_currentTools.ContainsKey(sender))
if (_currentTools.TryGetValue(sender, out ToolBase tool))
{
tool = _currentTools[sender];
if (tool.GetType() == typeof(T))
{
return (T)tool;
Expand All @@ -47,7 +45,6 @@ public T GetTool<T>(int sender) where T : ToolBase
ReflectionHelper.SetAttr(tool, "m_toolController", controller);
// See ToolController::Awake
ReflectionHelper.SetAttr(controller, "m_brushData", new float[4096]);
ReflectionHelper.SetAttr(controller, "m_collidingSegments", new float[4096]);
ReflectionHelper.SetAttr(controller, "ID_BrushTex", Shader.PropertyToID("_BrushTex"));
ReflectionHelper.SetAttr(controller, "ID_BrushWS", Shader.PropertyToID("_BrushWS"));
ReflectionHelper.SetAttr(controller, "m_collidingSegments1", new ulong[576]);
Expand Down Expand Up @@ -147,7 +144,20 @@ public T GetTool<T>(int sender) where T : ToolBase

public void RemoveSender(int sender)
{
_currentTools.Remove(sender);
if (_currentTools.TryGetValue(sender, out ToolBase tool))
{
_currentTools.Remove(sender);

// Tool based cleanup
switch (tool)
{
case TransportTool transportTool:
IgnoreHelper.Instance.StartIgnore();
Singleton<TransportManager>.instance.ReleaseLine(ReflectionHelper.GetAttr<ushort>(transportTool, "m_tempLine"));
IgnoreHelper.Instance.EndIgnore();
break;
}
}
}

public void Clear()
Expand Down
1 change: 0 additions & 1 deletion src/basegame/Injections/Tools/TransportToolHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using ColossalFramework;
using CSM.API;
using CSM.API.Commands;
using CSM.API.Helpers;
using HarmonyLib;
using ProtoBuf;
using UnityEngine;
Expand Down
Loading