using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DropshipItemRecovery")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("This mod solves the problem that occurs when the host player disconnects from the game after ordering items from the store in the terminal.")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("DropshipItemRecovery")]
[assembly: AssemblyTitle("DropshipItemRecovery")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 DropshipItemRecovery
{
public static class BuyItemsServerRpcPatch
{
[Serializable]
private class SaveData
{
public List<int> OrderedItems = new List<int>();
public int BalanceSpent = 0;
public int lastFullBalance = 0;
public bool hasWarrantyTicket = false;
public bool vehicleInDropship = false;
public int orderedVehicleFromTerminal = 0;
public Item[] buyableItemsFromTerminal = Array.Empty<Item>();
}
private static readonly string SavePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
private static readonly byte[] EncKey = Encoding.UTF8.GetBytes("hrdb_xkey");
private static float _lastSaveTime = -999f;
[HarmonyPatch(typeof(Terminal), "LoadNewNodeIfAffordable")]
[HarmonyPrefix]
public static void BeforePurchases(Terminal __instance, out int __state)
{
__state = __instance.orderedItemsFromTerminal.Count;
}
[HarmonyPatch(typeof(Terminal), "LoadNewNodeIfAffordable")]
[HarmonyPostfix]
public static void SavePurchases(Terminal __instance, TerminalNode node, int __state)
{
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost)
{
return;
}
int num = __instance.orderedItemsFromTerminal.Count - __state;
if (node.buyItemIndex != -1 && num > 0)
{
float num2 = (float)__instance.itemSalesPercentages[node.buyItemIndex] / 100f;
int num3 = (int)Math.Round((float)node.itemCost * num2);
SaveData saveData = LoadSaveData();
for (int i = 0; i < num; i++)
{
saveData.OrderedItems.Add(node.buyItemIndex);
saveData.BalanceSpent += num3;
saveData.lastFullBalance = __instance.groupCredits;
}
saveData.buyableItemsFromTerminal = __instance.buyableItemsList;
SaveDataToFile(saveData);
}
}
[HarmonyPatch(typeof(Terminal), "BuyItemsServerRpc")]
[HarmonyPrefix]
public static void HandleClientPurchase(Terminal __instance, int[] boughtItems, int newGroupCredits, int numItemsInShip)
{
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost)
{
return;
}
try
{
SaveData saveData = LoadSaveData();
foreach (int num in boughtItems)
{
int num2 = 0;
if (num < __instance.buyableItemsList.Length)
{
num2 = __instance.buyableItemsList[num].creditsWorth;
}
else
{
int num3 = num - __instance.buyableItemsList.Length;
if (num3 >= 0 && num3 < __instance.buyableVehicles.Length)
{
num2 = __instance.buyableVehicles[num3].creditsWorth;
}
}
float num4 = (float)__instance.itemSalesPercentages[num] / 100f;
int num5 = (int)Math.Round((float)num2 * num4);
saveData.OrderedItems.Add(num);
saveData.BalanceSpent += num5;
saveData.lastFullBalance = __instance.groupCredits;
}
SaveDataToFile(saveData);
}
catch (Exception arg)
{
Plugin.Instance.SendLog($"Failed to save purchases on host in BuyItemsServerRpc: {arg}");
}
}
[HarmonyPatch(typeof(Terminal), "Start")]
[HarmonyPostfix]
public static void AwakePost(Terminal __instance)
{
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost)
{
return;
}
SaveData saveData = LoadSaveData();
__instance.orderedItemsFromTerminal.Clear();
int groupCredits = __instance.groupCredits;
int lastFullBalance = saveData.lastFullBalance;
if (saveData.OrderedItems.Count > 0)
{
__instance.numberOfItemsInDropship = saveData.OrderedItems.Count;
__instance.orderedItemsFromTerminal.AddRange(saveData.OrderedItems);
if (__instance.groupCredits >= lastFullBalance)
{
HUDManager.Instance.DisplayTip("DropshipItemRecovery", "Credits are corrupted. Restoring...", false, false, "LC_Tip1");
__instance.groupCredits = groupCredits - saveData.BalanceSpent;
}
}
else if (saveData.vehicleInDropship)
{
__instance.vehicleInDropship = saveData.vehicleInDropship;
__instance.hasWarrantyTicket = saveData.hasWarrantyTicket;
__instance.orderedVehicleFromTerminal = saveData.orderedVehicleFromTerminal;
if (__instance.groupCredits >= lastFullBalance)
{
HUDManager.Instance.DisplayTip("DropshipItemRecovery", "Credits are corrupted. Restoring...", false, false, "LC_Tip1");
__instance.groupCredits = groupCredits - saveData.BalanceSpent;
}
}
}
[HarmonyPatch(typeof(Terminal), "BuyVehicleClientRpc")]
[HarmonyPostfix]
public static void BuyVehicleClientRpc(Terminal __instance)
{
if (!((Object)(object)NetworkManager.Singleton != (Object)null) || !NetworkManager.Singleton.IsHost || Time.time - _lastSaveTime < 0.5f)
{
return;
}
_lastSaveTime = Time.time;
try
{
SaveData saveData = LoadSaveData();
if (saveData.OrderedItems.Count <= 0)
{
saveData.hasWarrantyTicket = __instance.hasWarrantyTicket;
saveData.vehicleInDropship = __instance.vehicleInDropship;
saveData.orderedVehicleFromTerminal = __instance.orderedVehicleFromTerminal;
int orderedVehicleFromTerminal = __instance.orderedVehicleFromTerminal;
if (orderedVehicleFromTerminal >= 0 && orderedVehicleFromTerminal < __instance.itemSalesPercentages.Length)
{
float num = (float)__instance.itemSalesPercentages[orderedVehicleFromTerminal] / 100f;
int creditsWorth = __instance.buyableVehicles[orderedVehicleFromTerminal].creditsWorth;
int num2 = (int)Math.Round((float)creditsWorth * num);
saveData.BalanceSpent += num2;
saveData.lastFullBalance = __instance.groupCredits;
}
SaveDataToFile(saveData);
}
}
catch (Exception arg)
{
Plugin.Instance.SendLog($"Failed to save purchases on host in BuyItemsServerRpc: {arg}");
}
}
[HarmonyPatch(typeof(Terminal), "TextPostProcess")]
[HarmonyPrefix]
private static bool TextPostProcess_Prefix(string modifiedDisplayText, TerminalNode node, ref string __result, Terminal __instance)
{
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost)
{
return true;
}
if (string.IsNullOrEmpty(modifiedDisplayText) || !modifiedDisplayText.Contains("[numberOfItemsOnRoute]"))
{
return true;
}
string text;
if (__instance.vehicleInDropship)
{
text = "1 purchased items on route:\n- Cruiser";
}
else if (__instance.numberOfItemsInDropship <= 0)
{
text = "";
}
else
{
text = $"{__instance.numberOfItemsInDropship} purchased items on route:";
foreach (int item in __instance.orderedItemsFromTerminal)
{
if (item >= 0 && item < __instance.buyableItemsList.Length)
{
text = text + "\n- " + __instance.buyableItemsList[item].itemName;
}
}
}
__result = modifiedDisplayText.Replace("[numberOfItemsOnRoute]", text);
return false;
}
[HarmonyPatch(typeof(ItemDropship), "FinishDeliveringVehicleClientRpc")]
[HarmonyPostfix]
public static void FinishDeliveringVehicleClientRpc(ItemDropship __instance)
{
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost)
{
return;
}
try
{
string saveFilePath = GetSaveFilePath();
if (File.Exists(saveFilePath))
{
File.Delete(saveFilePath);
}
}
catch (Exception ex)
{
Plugin.Instance.SendLog(ex.ToString());
}
}
[HarmonyPatch(typeof(ItemDropship), "LandShipClientRpc")]
[HarmonyPostfix]
public static void ShipLeavePatch(ItemDropship __instance)
{
if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost)
{
return;
}
try
{
string saveFilePath = GetSaveFilePath();
if (File.Exists(saveFilePath))
{
File.Delete(saveFilePath);
}
}
catch (Exception ex)
{
Plugin.Instance.SendLog(ex.ToString());
}
}
private static void SaveDataToFile(SaveData data)
{
try
{
if (!Directory.Exists(SavePath))
{
Directory.CreateDirectory(SavePath);
}
if (data.OrderedItems.Count > 12)
{
int num = data.OrderedItems.Count - 12;
data.OrderedItems.RemoveRange(data.OrderedItems.Count - num, num);
}
string input = JsonUtility.ToJson((object)data);
string contents = EncryptString(input);
File.WriteAllText(GetSaveFilePath(), contents);
}
catch (Exception arg)
{
Plugin.Instance.SendLog($"Failed to save ordered items: {arg}");
}
}
private static SaveData LoadSaveData()
{
try
{
string saveFilePath = GetSaveFilePath();
if (!File.Exists(saveFilePath))
{
return new SaveData();
}
string input = File.ReadAllText(saveFilePath);
string text = DecryptString(input);
return JsonUtility.FromJson<SaveData>(text);
}
catch (Exception arg)
{
Plugin.Instance.SendLog($"Failed to load ordered items: {arg}");
return new SaveData();
}
}
private static string GetSaveFilePath()
{
return Path.Combine(SavePath, GameNetworkManager.Instance.currentSaveFileName + "_ordItems.data");
}
private static string EncryptString(string input)
{
byte[] bytes = Encoding.UTF8.GetBytes(input);
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] ^= EncKey[i % EncKey.Length];
}
return Convert.ToBase64String(bytes);
}
private static string DecryptString(string input)
{
byte[] array = Convert.FromBase64String(input);
for (int i = 0; i < array.Length; i++)
{
array[i] ^= EncKey[i % EncKey.Length];
}
return Encoding.UTF8.GetString(array);
}
}
[BepInPlugin("DropshipItemRecovery", "DropshipItemRecovery", "1.0.3")]
public class Plugin : BaseUnityPlugin
{
public static Plugin Instance { get; private set; }
private void Awake()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Expected O, but got Unknown
Instance = this;
((BaseUnityPlugin)this).Logger.LogInfo((object)"DropshipItemRecovery is successfully loaded!");
try
{
Harmony val = new Harmony("DropshipItemRecovery");
Harmony.CreateAndPatchAll(typeof(BuyItemsServerRpcPatch), (string)null);
val.PatchAll();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Patches patched!");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)ex);
}
}
public void SendLog(string text)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)(text ?? ""));
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "DropshipItemRecovery";
public const string PLUGIN_NAME = "DropshipItemRecovery";
public const string PLUGIN_VERSION = "1.0.3";
}
}