using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using HutongGames.PlayMaker.Actions;
using Mirror;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("StockPlus")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("StockPlus")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d42b6645-fb2e-40d8-a560-df5c5a8d2ccd")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace StockPlus;
public class GASession
{
private string modId;
private string modName;
private string modVersion;
private string measurementId;
private string screenTitle;
private string renewScreenTitle;
private bool sendSystemInfo;
private int sessionLengthMins;
private HttpClient HTTPclient = new HttpClient();
private DateTimeOffset sessionStartTime = DateTimeOffset.UtcNow;
private DateTimeOffset lastEng = DateTimeOffset.UtcNow;
private int GAHitCount = 1;
private int GASessionCount = 1;
private string GASessionID = Regex.Replace(Guid.NewGuid().ToString(), "[^\\d]", "").Substring(0, 10);
internal GASession(string modId, string modName, string modVersion, string measurementId, string screenTitle, string renewScreenTitle, bool sendSystemInfo, int sessionLengthMins)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Expected O, but got Unknown
this.modId = modId;
this.modName = modName;
this.modVersion = modVersion;
this.measurementId = measurementId;
this.screenTitle = screenTitle;
this.renewScreenTitle = renewScreenTitle;
this.sendSystemInfo = sendSystemInfo;
this.sessionLengthMins = sessionLengthMins;
if (isFirstRun())
{
SendUserGA("page_view", startingSession: true, "&_fv=1");
}
else
{
SendUserGA("page_view", startingSession: true, "");
}
}
private static string getSystemArch()
{
if (Environment.Is64BitProcess)
{
return "x86_64";
}
return "x86";
}
private static string getSystemBits()
{
if (Environment.Is64BitProcess)
{
return "64";
}
return "32";
}
private bool isFirstRun()
{
if (!PlayerPrefs.HasKey(modId + ".newUser"))
{
PlayerPrefs.SetInt(modId + ".newUser", 1);
PlayerPrefs.Save();
return true;
}
return false;
}
private bool recentlyUpdated()
{
if (PlayerPrefs.GetString(modId + ".v") != modVersion)
{
PlayerPrefs.SetString(modId + ".v", modVersion);
PlayerPrefs.Save();
return true;
}
return false;
}
private void startNewGASession()
{
GASessionCount++;
GASessionID = Regex.Replace(Guid.NewGuid().ToString(), "[^\\d]", "").Substring(0, 10);
sessionStartTime = DateTimeOffset.UtcNow;
lastEng = DateTime.UtcNow;
GAHitCount = 1;
SendUserGA("page_view", startingSession: false, "");
}
public void sendGAEvent(string event_category, string event_name, Dictionary<string, string> event_params, bool isEngaged = true)
{
//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
//IL_0201: Unknown result type (might be due to invalid IL or missing references)
//IL_0206: Unknown result type (might be due to invalid IL or missing references)
if (!StockPlusPlugin.telemetryEnabled.Value)
{
return;
}
if ((DateTime.UtcNow - lastEng).TotalMinutes >= (double)sessionLengthMins)
{
startNewGASession();
}
event_name = WebUtility.UrlEncode(event_name);
event_category = WebUtility.UrlEncode(event_category);
string text = "";
foreach (KeyValuePair<string, string> event_param in event_params)
{
text = ((!int.TryParse(event_param.Value, out var result)) ? (text + "&ep." + WebUtility.UrlEncode(event_param.Key) + "=" + WebUtility.UrlEncode(event_param.Value)) : (text + "&epn." + WebUtility.UrlEncode(event_param.Key) + "=" + result));
}
string text2 = "";
if (isEngaged)
{
text2 = text2 + "&_et = " + (long)Math.Floor((DateTime.UtcNow - lastEng).TotalMilliseconds);
}
GAHitCount++;
HttpClient hTTPclient = HTTPclient;
string[] obj = new string[31]
{
"https://www.google-analytics.com/g/collect?v=2&tid=",
measurementId,
"&cid=",
SystemInfo.deviceUniqueIdentifier,
"&sid=",
GASessionID,
"&ul=",
CultureInfo.CurrentCulture.Name,
"&sr=",
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null
};
Resolution currentResolution = Screen.currentResolution;
obj[9] = ((Resolution)(ref currentResolution)).width.ToString();
obj[10] = "x";
currentResolution = Screen.currentResolution;
obj[11] = ((Resolution)(ref currentResolution)).height.ToString();
obj[12] = "&uap=";
obj[13] = WebUtility.UrlEncode(Environment.OSVersion.Platform.ToString());
obj[14] = "&uam=";
obj[15] = WebUtility.UrlEncode(Application.version);
obj[16] = "&uapv=";
obj[17] = WebUtility.UrlEncode(modVersion);
obj[18] = "&en=";
obj[19] = event_name;
obj[20] = "&ep.category=";
obj[21] = event_category;
obj[22] = text;
obj[23] = text2;
obj[24] = "&tfd=";
obj[25] = ((long)Math.Floor((DateTime.UtcNow - sessionStartTime).TotalMilliseconds)).ToString();
obj[26] = "&uamb=0&uaw=0&pscdl=noapi&_s=";
obj[27] = GAHitCount.ToString();
obj[28] = "&sct=";
obj[29] = GASessionCount.ToString();
obj[30] = "&seg=1&_ee=1&npa=0&dma=0&frm=0&are=1";
hTTPclient.GetAsync(string.Concat(obj));
if (isEngaged)
{
lastEng = DateTime.UtcNow;
}
}
private void SendUserGA(string e, bool startingSession, string args)
{
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
if (StockPlusPlugin.telemetryEnabled.Value)
{
string text = "";
string text2 = "";
if (sendSystemInfo)
{
text = text + "&ep.cpu=" + SystemInfo.processorType + "&ep.gpu=" + SystemInfo.graphicsDeviceName;
}
text2 = ((!startingSession) ? renewScreenTitle : screenTitle);
HttpClient hTTPclient = HTTPclient;
string[] obj = new string[33]
{
"https://www.google-analytics.com/g/collect?v=2&tid=",
measurementId,
"&cid=",
SystemInfo.deviceUniqueIdentifier,
"&sid=",
GASessionID,
"&ul=",
CultureInfo.CurrentCulture.Name,
"&sr=",
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null
};
Resolution currentResolution = Screen.currentResolution;
obj[9] = ((Resolution)(ref currentResolution)).width.ToString();
obj[10] = "x";
currentResolution = Screen.currentResolution;
obj[11] = ((Resolution)(ref currentResolution)).height.ToString();
obj[12] = "&uap=";
obj[13] = WebUtility.UrlEncode(Environment.OSVersion.Platform.ToString());
obj[14] = "&uam=";
obj[15] = WebUtility.UrlEncode(Application.version);
obj[16] = "&uapv=";
obj[17] = WebUtility.UrlEncode(modVersion);
obj[18] = "&en=";
obj[19] = e;
obj[20] = text;
obj[21] = "&tfd=";
obj[22] = ((long)Math.Floor((DateTime.UtcNow - sessionStartTime).TotalMilliseconds)).ToString();
obj[23] = "&uaa=";
obj[24] = getSystemArch();
obj[25] = "&uab=";
obj[26] = getSystemBits();
obj[27] = "&uamb=0&uaw=0&dt=";
obj[28] = WebUtility.UrlEncode(text2 + " (v" + modVersion + ")");
obj[29] = "&are=1&frm=0&pscdl=noapi&seg=1&npa=0&_s=1&sct=";
obj[30] = GASessionCount.ToString();
obj[31] = "&dma=0&_ss=1&_nsi=1&_ee=1";
obj[32] = args;
hTTPclient.GetAsync(string.Concat(obj));
}
}
}
[BepInPlugin("RB007.plugins.StockPlus", "StockPlus", "1.0.0")]
public class StockPlusPlugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(NetworkManager), "OnStartHost")]
public static class OnHost
{
[HarmonyPostfix]
public static void Postfix(NetworkManager __instance)
{
isHost = true;
((MonoBehaviour)__instance).StartCoroutine(CheckStock());
}
private static IEnumerator CheckStock()
{
while (true)
{
yield return (object)new WaitForSeconds(checkInterval);
if (isStocking.Value)
{
AddLowStock(buyToo: true);
}
}
}
}
[HarmonyPatch(typeof(NetworkManager), "OnStartClient")]
public static class OnClient
{
[HarmonyPostfix]
public static void Postfix(NetworkManager __instance)
{
}
}
[HarmonyPatch(typeof(PlayerNetwork), "OnStartClient")]
public static class CreateUI
{
[HarmonyPostfix]
public static void Postfix(PlayerNetwork __instance)
{
ManagerBlackboard val = Object.FindFirstObjectByType<ManagerBlackboard>();
if ((Object)(object)val == (Object)null)
{
Debug.LogWarning((object)"Failed to create StockPlus UI!");
}
}
}
[HarmonyPatch(typeof(ApplicationQuit), "OnEnter")]
public static class QuitEvent
{
[HarmonyPostfix]
public static void Prefix(ApplicationQuit __instance)
{
gaSession.sendGAEvent("state", "session_end", new Dictionary<string, string> { ["closed"] = "true" });
}
}
private const string GUID = "RB007.plugins.StockPlus";
private const string PluginName = "StockPlus";
private const string VersionString = "1.0.0";
internal static Harmony Harmony;
internal static bool isHost = false;
public static ConfigEntry<bool> isStocking;
public static ConfigEntry<int> minStockThreshold;
public static ConfigEntry<bool> telemetryEnabled;
internal static float checkInterval = 20f;
internal static GASession gaSession;
private void Awake()
{
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Expected O, but got Unknown
isStocking = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Auto Stocking Enabled", true, "Any available items (when they are fewer than the threshold below) will be purchased automatically.");
minStockThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("Settings", "Minimum Stock Threshold", 16, "When there are fewer of each item (than this value) available, they will be added to the shopping list (and purchased).");
telemetryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Misc", "Enable Telemetry", true, "Help me improve this mod by sending logs of small mod/game events.");
Harmony = new Harmony("RB007.plugins.StockPlus");
Harmony.PatchAll();
gaSession = new GASession("RB007.plugins.StockPlus", "StockPlus", "1.0.0", "G-0BREKNCPF9", "MainMenu", "InGame", sendSystemInfo: true, 30);
Debug.Log((object)"Loaded StockPlus successfully!");
}
public static void AddLowStock(bool buyToo)
{
ManagerBlackboard val = Object.FindFirstObjectByType<ManagerBlackboard>();
ProductListing val2 = Object.FindFirstObjectByType<ProductListing>();
if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
{
Debug.LogWarning((object)"Failed to add low stock to shopping list!");
return;
}
FieldInfo field = typeof(ManagerBlackboard).GetField("isSpawning", BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null)
{
bool flag = (bool)field.GetValue(val);
if (flag && buyToo)
{
return;
}
}
int num = 0;
int num2 = 0;
float num3 = 0f;
foreach (int availableProduct in val2.availableProducts)
{
Data_Product component = val2.productPrefabs[availableProduct].GetComponent<Data_Product>();
if ((Object)(object)component == (Object)null || !val2.unlockedProductTiers[component.productTier])
{
return;
}
MethodInfo method = typeof(ManagerBlackboard).GetMethod("GetProductsExistences", BindingFlags.Instance | BindingFlags.NonPublic);
if (method != null)
{
int[] source = (int[])method.Invoke(val, new object[1] { availableProduct });
if (source.Sum() < minStockThreshold.Value)
{
float num4 = component.basePricePerUnit * val2.tierInflation[component.productTier];
num4 *= (float)component.maxItemsPerBox;
num4 = Mathf.Round(num4 * 100f) / 100f;
val.AddShoppingListProduct(availableProduct, num4);
num++;
num2 += component.maxItemsPerBox;
num3 += num4;
}
}
}
if (buyToo && val.shoppingTotalCharge > 0f)
{
BuyStock();
}
if (num > 0)
{
gaSession.sendGAEvent("game", "restock", new Dictionary<string, string>
{
["boxes"] = num.ToString(),
["items"] = num2.ToString(),
["cost"] = num3.ToString(),
["buy"] = buyToo.ToString()
});
}
else
{
gaSession.sendGAEvent("game", "scan", new Dictionary<string, string>
{
["threshold"] = minStockThreshold.Value.ToString(),
["buy"] = buyToo.ToString()
});
}
}
public static void BuyStock()
{
ManagerBlackboard val = Object.FindFirstObjectByType<ManagerBlackboard>();
if ((Object)(object)val == (Object)null)
{
Debug.LogWarning((object)"Failed to buy shopping list!");
}
else
{
val.BuyCargo();
}
}
}