using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("DSPPlannerExport")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.4.0.0")]
[assembly: AssemblyInformationalVersion("1.4.0+b263694d51951580018488a24aea05e21e5b5781")]
[assembly: AssemblyProduct("DSPPlannerExport")]
[assembly: AssemblyTitle("DSPPlannerExport")]
[assembly: AssemblyVersion("1.4.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace DSPPlannerExport
{
[BepInPlugin("dev.ski.dspplannerexport", "DSP Planner Export", "1.8.0")]
public class Plugin : BaseUnityPlugin
{
private enum DeficitMode
{
Sustained,
Instant,
Window
}
private enum Corner
{
TopLeft,
TopRight,
BottomLeft,
BottomRight
}
private struct Snap
{
public long tick;
public int[] ids;
public long[] p;
public long[] c;
}
private struct DefRow
{
public int id;
public string name;
public float prod;
public float cons;
public float net;
public int streak;
public bool flagged;
}
public const string GUID = "dev.ski.dspplannerexport";
public const string NAME = "DSP Planner Export";
public const string VERSION = "1.8.0";
private const int Port = 8765;
private HttpListener _listener;
private Thread _thread;
private Harmony _harmony;
private volatile bool _running;
private volatile string _snapshot = "{\"version\":1,\"running\":false,\"states\":[]}";
private volatile string _rates = "{\"version\":1,\"running\":false,\"gameTick\":0,\"items\":[]}";
private volatile string _protos;
private volatile string _techs;
private volatile string _deficits = "{\"version\":1,\"running\":false,\"items\":[]}";
private int _frame;
private ConfigEntry<bool> _cfgEnabled;
private ConfigEntry<KeyboardShortcut> _cfgToggle;
private ConfigEntry<DeficitMode> _cfgMode;
private ConfigEntry<int> _cfgSustainSeconds;
private ConfigEntry<int> _cfgWindowSeconds;
private ConfigEntry<float> _cfgMinMagnitude;
private ConfigEntry<int> _cfgMaxRows;
private ConfigEntry<float> _cfgScale;
private ConfigEntry<Corner> _cfgCorner;
private ConfigEntry<bool> _cfgShowPending;
private ConfigEntry<float> _cfgHudX;
private ConfigEntry<float> _cfgHudY;
private FileSystemWatcher _cfgWatcher;
private string[] _itemName;
private float[] _negSeconds;
private readonly List<Snap> _snaps = new List<Snap>();
private volatile DefRow[] _hudRows = new DefRow[0];
private volatile bool _inGame;
private bool _hudVisible = true;
private bool _dragging;
private float _hudX;
private float _hudY;
private Vector2 _dragOffset;
private GUIStyle _titleStyle;
private GUIStyle _rowStyle;
private Texture2D _px;
private void Awake()
{
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0239: Unknown result type (might be due to invalid IL or missing references)
//IL_0243: Expected O, but got Unknown
_cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Enabled", true, "Show the in-game live-deficit HUD overlay.");
_cfgToggle = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("HUD", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Hotkey to show/hide the HUD at runtime.");
_cfgMode = ((BaseUnityPlugin)this).Config.Bind<DeficitMode>("Deficit", "Mode", DeficitMode.Window, "How a deficit is flagged. Window = net averaged over the last WindowSeconds (recommended — buffer churn averages out, only real shortages show). Sustained = net-negative held for SustainSeconds. Instant = net-negative in the latest 1s sample (most detail, spammy).");
_cfgSustainSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Deficit", "SustainSeconds", 5, "Sustained mode: consecutive ~1s samples in deficit before a flag.");
_cfgWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Deficit", "WindowSeconds", 60, "Window mode: average net production over this many seconds (e.g. 30, 60, 120, 300, 600, 900, 1800, 3600). Longer = calmer, only structural deficits show. Capped at 3600 (1h).");
_cfgMinMagnitude = ((BaseUnityPlugin)this).Config.Bind<float>("Deficit", "MinMagnitude", 1f, "Ignore deficits smaller than this many items/min (noise floor).");
_cfgMaxRows = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "MaxRows", 12, "Maximum deficit rows drawn in the HUD.");
_cfgScale = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Scale", 1f, "HUD size multiplier.");
_cfgCorner = ((BaseUnityPlugin)this).Config.Bind<Corner>("HUD", "Corner", Corner.TopRight, "Screen corner the HUD anchors to (used until you drag the HUD).");
_cfgShowPending = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowPending", true, "Also list items dipping but not yet sustained (dimmed, with their streak) so the HUD matches the planner card. Off = show only flagged deficits.");
_cfgHudX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "HudX", -1f, "HUD pixel X of the top-left corner. -1 = anchor to Corner. Set by dragging the HUD in-game.");
_cfgHudY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "HudY", -1f, "HUD pixel Y of the top-left corner. -1 = anchor to Corner. Set by dragging the HUD in-game.");
_hudVisible = _cfgEnabled.Value;
try
{
string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath);
string fileName = Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath);
_cfgWatcher = new FileSystemWatcher(directoryName, fileName)
{
NotifyFilter = (NotifyFilters.Size | NotifyFilters.LastWrite),
EnableRaisingEvents = true
};
_cfgWatcher.Changed += delegate
{
try
{
((BaseUnityPlugin)this).Config.Reload();
}
catch
{
}
};
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: config watcher failed (edit the cfg + restart to apply). " + ex.Message));
}
try
{
_harmony = new Harmony("dev.ski.dspplannerexport");
_harmony.PatchAll(typeof(ProductionTally));
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: stats patch failed (live /rates disabled). " + ex2.Message));
}
try
{
_listener = new HttpListener();
_listener.Prefixes.Add($"http://localhost:{8765}/");
_listener.Prefixes.Add($"http://127.0.0.1:{8765}/");
_listener.Start();
_running = true;
_thread = new Thread(ServeLoop)
{
IsBackground = true,
Name = "DSPPlannerExport"
};
_thread.Start();
((BaseUnityPlugin)this).Logger.LogInfo((object)$"DSP Planner Export: serving http://localhost:{8765}/state, /protos, /techs, /rates, /deficits, /config");
}
catch (Exception ex3)
{
((BaseUnityPlugin)this).Logger.LogError((object)("DSP Planner Export: could not start HTTP listener. On Windows you may need to run the game as admin once, or pick another port. " + ex3));
}
}
private void Update()
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
KeyboardShortcut value = _cfgToggle.Value;
if (((KeyboardShortcut)(ref value)).IsDown())
{
_hudVisible = !_hudVisible;
}
if (++_frame % 60 != 0)
{
return;
}
try
{
_snapshot = BuildJson();
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: snapshot error " + ex.Message));
}
try
{
_rates = BuildRates();
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: rates error " + ex2.Message));
}
try
{
ComputeDeficits();
}
catch (Exception ex3)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: deficit error " + ex3.Message));
}
}
private void ServeLoop()
{
while (_running)
{
HttpListenerContext ctx;
try
{
ctx = _listener.GetContext();
}
catch
{
break;
}
try
{
HttpListenerResponse response = ctx.Response;
response.AddHeader("Access-Control-Allow-Origin", "*");
response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
response.AddHeader("Access-Control-Allow-Headers", "Content-Type");
response.AddHeader("Cache-Control", "no-store");
if (ctx.Request.HttpMethod == "OPTIONS")
{
response.StatusCode = 204;
response.OutputStream.Close();
continue;
}
string absolutePath = ctx.Request.Url.AbsolutePath;
if (absolutePath.EndsWith("/events"))
{
Thread thread = new Thread((ThreadStart)delegate
{
ServeEvents(ctx);
});
thread.IsBackground = true;
thread.Name = "DSPPlannerExportSSE";
thread.Start();
continue;
}
response.ContentType = "application/json";
string s;
if (absolutePath.EndsWith("/protos"))
{
if (_protos == null)
{
try
{
_protos = BuildProtos();
}
catch (Exception ex)
{
_protos = "{\"items\":[]}";
((BaseUnityPlugin)this).Logger.LogWarning((object)("protos error " + ex.Message));
}
}
s = _protos;
}
else if (absolutePath.EndsWith("/techs"))
{
if (_techs == null)
{
try
{
_techs = BuildTechs();
}
catch (Exception ex2)
{
_techs = "{\"version\":1,\"techs\":[]}";
((BaseUnityPlugin)this).Logger.LogWarning((object)("techs error " + ex2.Message));
}
}
s = _techs;
}
else if (absolutePath.EndsWith("/rates"))
{
s = _rates;
}
else if (absolutePath.EndsWith("/deficits"))
{
s = _deficits;
}
else if (absolutePath.EndsWith("/config"))
{
if (ctx.Request.HttpMethod == "POST")
{
string form;
using (StreamReader streamReader = new StreamReader(ctx.Request.InputStream, Encoding.UTF8))
{
form = streamReader.ReadToEnd();
}
try
{
ApplyConfig(form);
}
catch (Exception ex3)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: config apply error " + ex3.Message));
}
}
s = BuildConfig();
}
else
{
s = _snapshot;
}
byte[] bytes = Encoding.UTF8.GetBytes(s);
response.ContentLength64 = bytes.Length;
response.OutputStream.Write(bytes, 0, bytes.Length);
response.OutputStream.Close();
}
catch (Exception ex4)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("DSP Planner Export: request error " + ex4.Message));
}
}
}
private void ServeEvents(HttpListenerContext ctx)
{
HttpListenerResponse response = ctx.Response;
Stream outputStream = response.OutputStream;
try
{
response.ContentType = "text/event-stream";
response.SendChunked = true;
string text = null;
string text2 = null;
while (_running)
{
string snapshot = _snapshot;
string rates = _rates;
StringBuilder stringBuilder = new StringBuilder();
if (snapshot != text)
{
stringBuilder.Append("event: state\ndata: ").Append(snapshot).Append("\n\n");
text = snapshot;
}
if (rates != text2)
{
stringBuilder.Append("event: rates\ndata: ").Append(rates).Append("\n\n");
text2 = rates;
}
if (stringBuilder.Length == 0)
{
stringBuilder.Append(": keepalive\n\n");
}
byte[] bytes = Encoding.UTF8.GetBytes(stringBuilder.ToString());
outputStream.Write(bytes, 0, bytes.Length);
outputStream.Flush();
Thread.Sleep(1000);
}
}
catch
{
}
try
{
outputStream.Close();
}
catch
{
}
}
private static string BuildJson()
{
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
StringBuilder stringBuilder = new StringBuilder(4096);
bool flag = (Object)(object)GameMain.instance != (Object)null && GameMain.history != null && !DSPGame.IsMenuDemo;
stringBuilder.Append("{\"version\":1,\"running\":").Append(flag ? "true" : "false").Append(",\"states\":[");
if (flag)
{
Dictionary<int, TechState> techStates = GameMain.history.techStates;
if (techStates != null)
{
bool flag2 = true;
foreach (KeyValuePair<int, TechState> item in techStates)
{
TechState value = item.Value;
if (value.unlocked || value.curLevel > 0)
{
if (!flag2)
{
stringBuilder.Append(',');
}
flag2 = false;
stringBuilder.Append("{\"id\":").Append(item.Key).Append(",\"level\":")
.Append(value.curLevel)
.Append(",\"max\":")
.Append(value.maxLevel)
.Append(",\"unlocked\":")
.Append(value.unlocked ? "true" : "false")
.Append('}');
}
}
}
}
stringBuilder.Append("]}");
return stringBuilder.ToString();
}
private static string BuildProtos()
{
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
StringBuilder stringBuilder = new StringBuilder(65536);
stringBuilder.Append("{\"version\":2,\"items\":[");
bool flag = true;
ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
foreach (ItemProto val in dataArray)
{
if (val == null || ((Proto)val).ID <= 0)
{
continue;
}
if (!flag)
{
stringBuilder.Append(',');
}
flag = false;
stringBuilder.Append("{\"id\":").Append(((Proto)val).ID).Append(",\"name\":\"")
.Append(JsonEscape(((Proto)val).name))
.Append('"')
.Append(",\"grid\":")
.Append(val.GridIndex);
if (val.ModelIndex <= 0)
{
stringBuilder.Append('}');
continue;
}
stringBuilder.Append(",\"model\":").Append(val.ModelIndex);
try
{
PrefabDesc val2 = ((ProtoSet<ModelProto>)(object)LDB.models).Select(val.ModelIndex)?.prefabDesc;
if (val2 != null)
{
Vector2 blueprintBoxSize = val2.blueprintBoxSize;
if (blueprintBoxSize.x > 0f && blueprintBoxSize.y > 0f)
{
stringBuilder.Append(",\"w\":").Append((int)blueprintBoxSize.x).Append(",\"h\":")
.Append((int)blueprintBoxSize.y);
}
float y = val2.lapJoint.y;
if (y > 0f)
{
stringBuilder.Append(",\"zstep\":").Append(y.ToString("0.###", CultureInfo.InvariantCulture));
}
Pose[] slotPoses = val2.slotPoses;
if (slotPoses != null && slotPoses.Length != 0)
{
int[] array = new int[4];
Pose[] array2 = slotPoses;
for (int j = 0; j < array2.Length; j++)
{
Vector3 position = array2[j].position;
int num = ((Math.Abs(position.x) >= Math.Abs(position.z)) ? ((position.x >= 0f) ? 1 : 3) : ((!(position.z >= 0f)) ? 2 : 0));
array[num]++;
}
stringBuilder.Append(",\"slots\":[").Append(array[0]).Append(',')
.Append(array[1])
.Append(',')
.Append(array[2])
.Append(',')
.Append(array[3])
.Append(']');
}
}
}
catch
{
}
stringBuilder.Append('}');
}
stringBuilder.Append("]}");
return stringBuilder.ToString();
}
private static string BuildTechs()
{
StringBuilder stringBuilder = new StringBuilder(65536);
stringBuilder.Append("{\"version\":1,\"techs\":[");
bool flag = true;
TechProto[] dataArray = ((ProtoSet<TechProto>)(object)LDB.techs).dataArray;
foreach (TechProto val in dataArray)
{
if (val == null || ((Proto)val).ID <= 0)
{
continue;
}
if (!flag)
{
stringBuilder.Append(',');
}
flag = false;
long hashNeeded;
try
{
hashNeeded = val.GetHashNeeded(val.Level);
}
catch
{
hashNeeded = val.HashNeeded;
}
stringBuilder.Append("{\"id\":").Append(((Proto)val).ID).Append(",\"name\":\"")
.Append(JsonEscape(((Proto)val).name))
.Append('"')
.Append(",\"level\":")
.Append(val.Level)
.Append(",\"max\":")
.Append(val.MaxLevel)
.Append(",\"hash\":")
.Append(hashNeeded);
if (val.MaxLevel > val.Level)
{
stringBuilder.Append(",\"hashByLevel\":[");
int num = Math.Min(val.MaxLevel, val.Level + 49);
for (int j = val.Level; j <= num; j++)
{
if (j > val.Level)
{
stringBuilder.Append(',');
}
long hashNeeded2;
try
{
hashNeeded2 = val.GetHashNeeded(j);
}
catch
{
hashNeeded2 = val.HashNeeded;
}
stringBuilder.Append(hashNeeded2);
}
stringBuilder.Append(']');
}
stringBuilder.Append(",\"pre\":");
AppendIntArray(stringBuilder, val.PreTechs);
stringBuilder.Append(",\"preImplicit\":");
AppendIntArray(stringBuilder, val.PreTechsImplicit);
stringBuilder.Append(",\"items\":");
AppendIntArray(stringBuilder, val.Items);
stringBuilder.Append(",\"points\":");
AppendIntArray(stringBuilder, val.ItemPoints);
stringBuilder.Append(",\"preItem\":");
AppendIntArray(stringBuilder, val.PreItem);
stringBuilder.Append('}');
}
stringBuilder.Append("]}");
return stringBuilder.ToString();
}
private static void AppendIntArray(StringBuilder sb, int[] a)
{
sb.Append('[');
if (a != null)
{
for (int i = 0; i < a.Length; i++)
{
if (i > 0)
{
sb.Append(',');
}
sb.Append(a[i]);
}
}
sb.Append(']');
}
private static string BuildRates()
{
bool flag = (Object)(object)GameMain.instance != (Object)null && GameMain.data != null && !DSPGame.IsMenuDemo;
StringBuilder stringBuilder = new StringBuilder(16384);
stringBuilder.Append("{\"version\":1,\"running\":").Append(flag ? "true" : "false").Append(",\"gameTick\":")
.Append(flag ? GameMain.gameTick : 0)
.Append(",\"items\":[");
if (flag)
{
bool flag2 = true;
for (int i = 0; i < ProductionTally.Produced.Length; i++)
{
long num = ProductionTally.Produced[i];
long num2 = ProductionTally.Consumed[i];
if (num != 0L || num2 != 0L)
{
if (!flag2)
{
stringBuilder.Append(',');
}
flag2 = false;
stringBuilder.Append("{\"id\":").Append(i).Append(",\"p\":")
.Append(num)
.Append(",\"c\":")
.Append(num2)
.Append('}');
}
}
}
stringBuilder.Append("]}");
return stringBuilder.ToString();
}
private void ComputeDeficits()
{
if (!(_inGame = (Object)(object)GameMain.instance != (Object)null && GameMain.data != null && !DSPGame.IsMenuDemo))
{
_deficits = "{\"version\":2,\"running\":false,\"items\":[]}";
_hudRows = new DefRow[0];
_snaps.Clear();
return;
}
int num = ProductionTally.Produced.Length;
if (_itemName == null)
{
_itemName = new string[num];
ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
foreach (ItemProto val in dataArray)
{
if (val != null && ((Proto)val).ID > 0 && ((Proto)val).ID < num)
{
_itemName[((Proto)val).ID] = ((Proto)val).name;
}
}
}
if (_negSeconds == null)
{
_negSeconds = new float[num];
}
long[] produced = ProductionTally.Produced;
long[] consumed = ProductionTally.Consumed;
long gameTick = GameMain.gameTick;
if (_snaps.Count > 0 && gameTick <= _snaps[_snaps.Count - 1].tick)
{
_snaps.Clear();
}
DeficitMode value = _cfgMode.Value;
int num2 = Math.Max(5, Math.Min(3600, _cfgWindowSeconds.Value));
int num3 = -1;
if (_snaps.Count > 0)
{
if (value == DeficitMode.Window)
{
long num4 = gameTick - (long)num2 * 60L;
num3 = 0;
for (int j = 0; j < _snaps.Count && _snaps[j].tick <= num4; j++)
{
num3 = j;
}
}
else
{
num3 = _snaps.Count - 1;
}
}
if (num3 < 0)
{
PushSnap(gameTick, produced, consumed, num);
return;
}
Snap snap = _snaps[num3];
long num5 = gameTick - snap.tick;
if (num5 <= 0)
{
PushSnap(gameTick, produced, consumed, num);
return;
}
float num6 = (float)num5 / 60f;
Dictionary<int, long> dictionary = new Dictionary<int, long>(snap.ids.Length);
Dictionary<int, long> dictionary2 = new Dictionary<int, long>(snap.ids.Length);
for (int k = 0; k < snap.ids.Length; k++)
{
dictionary[snap.ids[k]] = snap.p[k];
dictionary2[snap.ids[k]] = snap.c[k];
}
float value2 = _cfgMinMagnitude.Value;
int num7 = Math.Max(1, _cfgSustainSeconds.Value);
List<DefRow> list = new List<DefRow>();
for (int l = 0; l < num; l++)
{
long num8 = produced[l];
long num9 = consumed[l];
long value3;
long num10 = num8 - (dictionary.TryGetValue(l, out value3) ? value3 : 0);
long value4;
long num11 = num9 - (dictionary2.TryGetValue(l, out value4) ? value4 : 0);
if (num10 == 0L && num11 == 0L)
{
_negSeconds[l] = 0f;
continue;
}
float num12 = (float)((double)num10 / (double)num5 * 3600.0);
float num13 = (float)((double)num11 / (double)num5 * 3600.0);
float num14 = num12 - num13;
if (num14 >= 0f - value2)
{
_negSeconds[l] = 0f;
continue;
}
bool flagged;
int streak;
if (value == DeficitMode.Sustained)
{
_negSeconds[l] += 1f;
flagged = _negSeconds[l] >= (float)num7;
streak = (int)Math.Round(_negSeconds[l]);
}
else
{
flagged = true;
streak = (int)Math.Round(num6);
}
list.Add(new DefRow
{
id = l,
name = (_itemName[l] ?? ("#" + l)),
prod = num12,
cons = num13,
net = num14,
streak = streak,
flagged = flagged
});
}
PushSnap(gameTick, produced, consumed, num);
long num15 = ((value == DeficitMode.Window) ? num2 : 2) + 120;
long num16 = gameTick - num15 * 60;
while (_snaps.Count > 1 && _snaps[0].tick < num16)
{
_snaps.RemoveAt(0);
}
list.Sort((DefRow x, DefRow y) => x.net.CompareTo(y.net));
_hudRows = list.ToArray();
_deficits = BuildDeficitsJson(gameTick, num5, list);
}
private void PushSnap(long tick, long[] P, long[] C, int len)
{
int num = 0;
for (int i = 0; i < len; i++)
{
if (P[i] != 0L || C[i] != 0L)
{
num++;
}
}
int[] array = new int[num];
long[] array2 = new long[num];
long[] array3 = new long[num];
int num2 = 0;
for (int j = 0; j < len; j++)
{
if (P[j] != 0L || C[j] != 0L)
{
array[num2] = j;
array2[num2] = P[j];
array3[num2] = C[j];
num2++;
}
}
_snaps.Add(new Snap
{
tick = tick,
ids = array,
p = array2,
c = array3
});
}
private string BuildDeficitsJson(long tick, long dt, List<DefRow> rows)
{
StringBuilder stringBuilder = new StringBuilder(2048);
stringBuilder.Append("{\"version\":2,\"running\":true,\"gameTick\":").Append(tick).Append(",\"windowTicks\":")
.Append(dt)
.Append(",\"windowSec\":")
.Append(Fmt((float)dt / 60f))
.Append(",\"mode\":\"")
.Append(_cfgMode.Value.ToString().ToLowerInvariant())
.Append('"')
.Append(",\"sustainSeconds\":")
.Append(_cfgSustainSeconds.Value)
.Append(",\"requestedWindowSec\":")
.Append(Math.Max(5, Math.Min(3600, _cfgWindowSeconds.Value)))
.Append(",\"minMagnitude\":")
.Append(Fmt(_cfgMinMagnitude.Value))
.Append(",\"items\":[");
int num = Math.Min(rows.Count, 50);
for (int i = 0; i < num; i++)
{
DefRow defRow = rows[i];
if (i > 0)
{
stringBuilder.Append(',');
}
stringBuilder.Append("{\"id\":").Append(defRow.id).Append(",\"name\":\"")
.Append(JsonEscape(defRow.name))
.Append('"')
.Append(",\"produce\":")
.Append(Fmt(defRow.prod))
.Append(",\"consume\":")
.Append(Fmt(defRow.cons))
.Append(",\"net\":")
.Append(Fmt(defRow.net))
.Append(",\"streak\":")
.Append(defRow.streak)
.Append(",\"flagged\":")
.Append(defRow.flagged ? "true" : "false")
.Append('}');
}
stringBuilder.Append("]}");
return stringBuilder.ToString();
}
private static string Fmt(float v)
{
return v.ToString("0.#", CultureInfo.InvariantCulture);
}
private string BuildConfig()
{
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
StringBuilder stringBuilder = new StringBuilder(256);
stringBuilder.Append("{\"version\":2").Append(",\"mode\":\"").Append(_cfgMode.Value.ToString())
.Append('"')
.Append(",\"sustainSeconds\":")
.Append(_cfgSustainSeconds.Value)
.Append(",\"windowSeconds\":")
.Append(_cfgWindowSeconds.Value)
.Append(",\"minMagnitude\":")
.Append(Fmt(_cfgMinMagnitude.Value))
.Append(",\"hudEnabled\":")
.Append(_cfgEnabled.Value ? "true" : "false")
.Append(",\"toggleKey\":\"")
.Append(JsonEscape(((object)_cfgToggle.Value/*cast due to .constrained prefix*/).ToString()))
.Append('"')
.Append(",\"showPending\":")
.Append(_cfgShowPending.Value ? "true" : "false")
.Append(",\"corner\":\"")
.Append(_cfgCorner.Value.ToString())
.Append('"')
.Append(",\"scale\":")
.Append(Fmt(_cfgScale.Value))
.Append(",\"maxRows\":")
.Append(_cfgMaxRows.Value)
.Append(",\"hudX\":")
.Append(Fmt(_cfgHudX.Value))
.Append(",\"hudY\":")
.Append(Fmt(_cfgHudY.Value))
.Append(",\"dragged\":")
.Append((_cfgHudX.Value >= 0f && _cfgHudY.Value >= 0f) ? "true" : "false")
.Append('}');
return stringBuilder.ToString();
}
private void ApplyConfig(string form)
{
if (string.IsNullOrEmpty(form))
{
return;
}
CultureInfo invariantCulture = CultureInfo.InvariantCulture;
string[] array = form.Split(new char[1] { '&' });
foreach (string text in array)
{
int num = text.IndexOf('=');
if (num <= 0)
{
continue;
}
string text2 = text.Substring(0, num);
string text3 = Uri.UnescapeDataString(text.Substring(num + 1).Replace('+', ' ')).Trim();
try
{
if (text2 == null)
{
continue;
}
switch (text2.Length)
{
case 4:
switch (text2[3])
{
case 'e':
{
if (text2 == "mode" && Enum.TryParse<DeficitMode>(text3, ignoreCase: true, out var result4))
{
_cfgMode.Value = result4;
}
break;
}
case 'X':
{
if (text2 == "hudX" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result3))
{
_cfgHudX.Value = result3;
}
break;
}
case 'Y':
{
if (text2 == "hudY" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result2))
{
_cfgHudY.Value = result2;
}
break;
}
}
break;
case 14:
{
if (text2 == "sustainSeconds" && int.TryParse(text3, out var result8))
{
_cfgSustainSeconds.Value = Math.Max(1, result8);
}
break;
}
case 13:
{
if (text2 == "windowSeconds" && int.TryParse(text3, out var result5))
{
_cfgWindowSeconds.Value = Math.Max(5, Math.Min(3600, result5));
}
break;
}
case 12:
{
if (text2 == "minMagnitude" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result))
{
_cfgMinMagnitude.Value = Math.Max(0f, result);
}
break;
}
case 7:
{
if (text2 == "maxRows" && int.TryParse(text3, out var result9))
{
_cfgMaxRows.Value = Math.Max(1, result9);
}
break;
}
case 5:
{
if (text2 == "scale" && float.TryParse(text3, NumberStyles.Float, invariantCulture, out var result7))
{
_cfgScale.Value = Mathf.Clamp(result7, 0.5f, 3f);
}
break;
}
case 6:
{
if (text2 == "corner" && Enum.TryParse<Corner>(text3, ignoreCase: true, out var result6))
{
_cfgCorner.Value = result6;
}
break;
}
case 10:
if (text2 == "hudEnabled")
{
_cfgEnabled.Value = text3 == "true" || text3 == "1";
}
break;
case 11:
if (text2 == "showPending")
{
_cfgShowPending.Value = text3 == "true" || text3 == "1";
}
break;
case 8:
case 9:
break;
}
}
catch
{
}
}
}
private void OnGUI()
{
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Expected O, but got Unknown
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Expected O, but got Unknown
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Expected O, but got Unknown
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_0439: Unknown result type (might be due to invalid IL or missing references)
//IL_043e: Unknown result type (might be due to invalid IL or missing references)
//IL_0454: Unknown result type (might be due to invalid IL or missing references)
//IL_0466: Unknown result type (might be due to invalid IL or missing references)
//IL_02db: Unknown result type (might be due to invalid IL or missing references)
//IL_04aa: Unknown result type (might be due to invalid IL or missing references)
//IL_048f: Unknown result type (might be due to invalid IL or missing references)
//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
//IL_0355: Unknown result type (might be due to invalid IL or missing references)
//IL_035b: Invalid comparison between Unknown and I4
//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0517: Unknown result type (might be due to invalid IL or missing references)
//IL_0529: Unknown result type (might be due to invalid IL or missing references)
//IL_03f7: Unknown result type (might be due to invalid IL or missing references)
//IL_03fd: Invalid comparison between Unknown and I4
//IL_0363: Unknown result type (might be due to invalid IL or missing references)
//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
//IL_0324: Unknown result type (might be due to invalid IL or missing references)
//IL_032d: Unknown result type (might be due to invalid IL or missing references)
//IL_0332: Unknown result type (might be due to invalid IL or missing references)
//IL_0337: Unknown result type (might be due to invalid IL or missing references)
//IL_0401: Unknown result type (might be due to invalid IL or missing references)
//IL_0407: Invalid comparison between Unknown and I4
//IL_057f: Unknown result type (might be due to invalid IL or missing references)
//IL_0569: Unknown result type (might be due to invalid IL or missing references)
//IL_06a1: Unknown result type (might be due to invalid IL or missing references)
//IL_066b: Unknown result type (might be due to invalid IL or missing references)
//IL_0667: Unknown result type (might be due to invalid IL or missing references)
if (!_cfgEnabled.Value || !_hudVisible || !_inGame)
{
return;
}
DefRow[] hudRows = _hudRows;
bool value = _cfgShowPending.Value;
int num = 0;
int num2 = 0;
for (int i = 0; i < hudRows.Length; i++)
{
if (hudRows[i].flagged)
{
num2++;
num++;
}
else if (value)
{
num++;
}
}
if (_titleStyle == null)
{
_titleStyle = new GUIStyle(GUI.skin.label)
{
fontStyle = (FontStyle)1,
alignment = (TextAnchor)3
};
_rowStyle = new GUIStyle(GUI.skin.label)
{
alignment = (TextAnchor)3
};
}
if ((Object)(object)_px == (Object)null)
{
_px = new Texture2D(1, 1);
_px.SetPixel(0, 0, Color.white);
_px.Apply();
((Object)_px).hideFlags = (HideFlags)61;
}
float num3 = Mathf.Clamp(_cfgScale.Value, 0.5f, 3f);
int num4 = Mathf.RoundToInt(14f * num3);
_titleStyle.fontSize = num4;
_rowStyle.fontSize = num4;
float num5 = 10f * num3;
float num6 = 4f * num3;
float rowH = num4 + 8;
float num7 = 300f * num3;
float num8 = 14f * num3;
int num9 = Math.Max(1, _cfgMaxRows.Value);
int num10 = ((num <= 0) ? 1 : Math.Min(num, num9));
float num11 = num5 * 2f + rowH * (float)(num10 + 1);
Corner value2 = _cfgCorner.Value;
bool num12 = value2 == Corner.TopLeft || value2 == Corner.BottomLeft;
bool flag = value2 == Corner.TopLeft || value2 == Corner.TopRight;
float num13 = (num12 ? num8 : ((float)Screen.width - num7 - num8));
float num14 = (flag ? num8 : ((float)Screen.height - num11 - num8));
float num15 = (_dragging ? _hudX : ((_cfgHudX.Value >= 0f && _cfgHudY.Value >= 0f) ? _cfgHudX.Value : num13));
float num16 = (_dragging ? _hudY : ((_cfgHudX.Value >= 0f && _cfgHudY.Value >= 0f) ? _cfgHudY.Value : num14));
num15 = Mathf.Clamp(num15, 0f, Math.Max(0f, (float)Screen.width - num7));
num16 = Mathf.Clamp(num16, 0f, Math.Max(0f, (float)Screen.height - num11));
Event current = Event.current;
if (current != null)
{
if ((int)current.type == 0 && current.button == 0)
{
Rect val = new Rect(num15, num16, num7, num11);
if (((Rect)(ref val)).Contains(current.mousePosition))
{
_dragging = true;
_hudX = num15;
_hudY = num16;
_dragOffset = current.mousePosition - new Vector2(num15, num16);
current.Use();
goto IL_0439;
}
}
if (_dragging && (int)current.type == 3)
{
num15 = (_hudX = Mathf.Clamp(current.mousePosition.x - _dragOffset.x, 0f, Math.Max(0f, (float)Screen.width - num7)));
num16 = (_hudY = Mathf.Clamp(current.mousePosition.y - _dragOffset.y, 0f, Math.Max(0f, (float)Screen.height - num11)));
current.Use();
}
else if (_dragging && ((int)current.type == 1 || (int)current.rawType == 1))
{
_dragging = false;
_cfgHudX.Value = _hudX;
_cfgHudY.Value = _hudY;
current.Use();
}
}
goto IL_0439;
IL_0439:
Color color = GUI.color;
GUI.color = new Color(0.04f, 0.05f, 0.07f, 0.93f);
GUI.DrawTexture(new Rect(num15, num16, num7, num11), (Texture)(object)_px);
GUI.color = ((num2 > 0) ? new Color(1f, 0.45f, 0.35f, 0.95f) : new Color(0.55f, 0.6f, 0.7f, 0.9f));
GUI.DrawTexture(new Rect(num15, num16, num6, num11), (Texture)(object)_px);
GUI.color = color;
float tx = num15 + num5 + num6;
float tw = num7 - num5 * 2f - num6;
float num17 = num16 + num5;
if (num == 0)
{
L(num17, "✓ No deficits", new Color(0.55f, 0.95f, 0.6f), _titleStyle);
GUI.color = color;
return;
}
L(num17, (num2 > 0) ? ("▲ Live deficits (" + num2 + ")") : "Dipping — none sustained", (num2 > 0) ? new Color(1f, 0.78f, 0.32f) : new Color(0.92f, 0.94f, 1f), _titleStyle);
num17 += rowH;
Color val2 = default(Color);
((Color)(ref val2))..ctor(1f, 0.5f, 0.42f);
Color val3 = default(Color);
((Color)(ref val3))..ctor(0.9f, 0.92f, 0.97f);
int num18 = 0;
for (int j = 0; j < hudRows.Length; j++)
{
if (num18 >= num9)
{
break;
}
DefRow defRow = hudRows[j];
if (defRow.flagged || value)
{
string text = defRow.name + " " + defRow.net.ToString("0.0", CultureInfo.InvariantCulture) + "/min" + (defRow.flagged ? "" : (" (" + defRow.streak + "s)"));
L(num17, text, defRow.flagged ? val2 : val3, _rowStyle);
num17 += rowH;
num18++;
}
}
GUI.color = color;
void L(float y, string text2, Color col, GUIStyle st)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
st.normal.textColor = new Color(0f, 0f, 0f, 0.9f);
GUI.Label(new Rect(tx + 1.5f, y + 1.5f, tw, rowH), text2, st);
st.normal.textColor = col;
GUI.Label(new Rect(tx, y, tw, rowH), text2, st);
}
}
private static string JsonEscape(string s)
{
if (string.IsNullOrEmpty(s))
{
return "";
}
StringBuilder stringBuilder = new StringBuilder(s.Length + 8);
foreach (char c in s)
{
if (c == '"' || c == '\\')
{
stringBuilder.Append('\\').Append(c);
}
else if (c < ' ')
{
StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
int num = c;
stringBuilder2.Append(num.ToString("x4"));
}
else
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString();
}
private void OnDestroy()
{
_running = false;
try
{
_cfgWatcher?.Dispose();
}
catch
{
}
try
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
catch
{
}
try
{
_listener?.Stop();
}
catch
{
}
try
{
_listener?.Close();
}
catch
{
}
}
}
[HarmonyPatch(typeof(FactoryProductionStat), "GameTick")]
internal static class ProductionTally
{
internal static readonly long[] Produced = new long[12000];
internal static readonly long[] Consumed = new long[12000];
private static void Prefix(FactoryProductionStat __instance)
{
int[] productRegister = __instance.productRegister;
int[] consumeRegister = __instance.consumeRegister;
if (productRegister == null || consumeRegister == null)
{
return;
}
int num = Math.Min(Math.Min(productRegister.Length, consumeRegister.Length), Produced.Length);
for (int i = 0; i < num; i++)
{
if (productRegister[i] != 0)
{
Produced[i] += productRegister[i];
}
if (consumeRegister[i] != 0)
{
Consumed[i] += consumeRegister[i];
}
}
}
}
}