using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using AutoFuelRestocker.Configuration; using AutoFuelRestocker.Localization; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using IceBoxModLib.Config; using IceBoxModLib.Localization; using IceBoxModLib.UI; using IceBoxModLib.Utilities; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Networking; using Store; using Store.Pump; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("AutoFuelRestocker")] [assembly: AssemblyDescription("Auto Fuel Restocker mod for Roadside Research by Ice Box Studio")] [assembly: AssemblyCompany("Ice Box Studio")] [assembly: AssemblyProduct("AutoFuelRestocker")] [assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("cf665b75-4a7d-4c38-aaa1-5449a93b0ab3")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("1.0.0.0")] namespace AutoFuelRestocker { [BepInPlugin("IceBoxStudio.RoadsideResearch.AutoFuelRestocker", "AutoFuelRestocker", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class AutoFuelRestocker : BasePlugin { public static AutoFuelRestocker _Instance; private Harmony _harmony; private bool patchesApplied; public static AutoFuelRestocker Instance => _Instance; internal static ManualLogSource Log { get; private set; } public override void Load() { //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown _Instance = this; Log = ((BasePlugin)this).Log; bool flag = default(bool); try { Log.LogInfo((object)"============================================="); ManualLogSource log = Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(1, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("AutoFuelRestocker"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(LocalizationManager.Instance.GetLocalizedText("plugin.initializing")); } log.LogInfo(val); ManualLogSource log2 = Log; val = new BepInExInfoLogInterpolatedStringHandler(54, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(LocalizationManager.Instance.GetLocalizedText("plugin.author_prefix")); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Ice Box Studio(https://steamcommunity.com/id/ibox666/)"); } log2.LogInfo(val); ConfigManager.Initialize(((BasePlugin)this).Config); ApplyPatches(); ManualLogSource log3 = Log; val = new BepInExInfoLogInterpolatedStringHandler(1, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("AutoFuelRestocker"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(LocalizationManager.Instance.GetLocalizedText("plugin.initialized")); } log3.LogInfo(val); Log.LogInfo((object)"============================================="); } catch (Exception ex) { ManualLogSource log4 = Log; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(9, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted("AutoFuelRestocker"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" 初始化错误: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.StackTrace); } log4.LogError(val2); } } private void ApplyPatches() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (!patchesApplied) { try { _harmony = new Harmony("IceBoxStudio.RoadsideResearch.AutoFuelRestocker"); _harmony.PatchAll(); patchesApplied = true; return; } catch (Exception ex) { ManualLogSource log = Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(10, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("AutoFuelRestocker"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" 应用补丁错误: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.StackTrace); } log.LogError(val); return; } } Log.LogInfo((object)LocalizationManager.Instance.GetLocalizedText("plugin.patches_skipped")); } } public static class PluginInfo { public const string PLUGIN_GUID = "IceBoxStudio.RoadsideResearch.AutoFuelRestocker"; public const string PLUGIN_NAME = "AutoFuelRestocker"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace AutoFuelRestocker.Patches { [HarmonyPatch(typeof(FuelInventory))] internal static class FuelInventoryPatch { private static readonly GasTypes[] GasTypes; private static readonly HashSet Working; private static readonly Dictionary LastTryTime; private static readonly Dictionary LastOutOfStockTime; private const float TryCooldown = 0.75f; private const float OutOfStockCooldown = 5f; private const int MaxTransfersPerCheck = 8; [HarmonyPatch("TryRemoveGas")] [HarmonyPostfix] private static void PostfixTryRemoveGas(FuelInventory __instance, GasTypes gasType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) TryRestock(__instance, gasType, ignoreCooldown: false, showOutOfStock: true, showSuccess: true); } [HarmonyPatch("LoadData")] [HarmonyPostfix] private static void PostfixLoadData(FuelInventory __instance) { for (int i = 0; i < GasTypes.Length; i++) { TryRestock(__instance, GasTypes[i], ignoreCooldown: true, showOutOfStock: false, showSuccess: false); } } [HarmonyPatch("Render")] [HarmonyPostfix] private static void PostfixRender(FuelInventory __instance) { for (int i = 0; i < GasTypes.Length; i++) { TryRestock(__instance, GasTypes[i], ignoreCooldown: false, showOutOfStock: false, showSuccess: true); } } [HarmonyPatch(typeof(PumpBehaviour), "OnChangeCurrentPumpState")] [HarmonyPostfix] private static void PostfixOnChangeCurrentPumpState(PumpBehaviour __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance._fuelInventory == (Object)null)) { for (int i = 0; i < GasTypes.Length; i++) { TryRestock(__instance._fuelInventory, GasTypes[i], ignoreCooldown: true, showOutOfStock: false, showSuccess: true); } } } private static void TryRestock(FuelInventory fuelInventory, GasTypes gasType, bool ignoreCooldown, bool showOutOfStock, bool showSuccess) { //IL_0001: 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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (!CanWork(fuelInventory, gasType)) { return; } string key = GetKey(fuelInventory, gasType); float restockThreshold = ConfigManager.Instance.GetRestockThreshold(); fuelInventory.GetFuelPercent(gasType); if ((!ignoreCooldown && IsCoolingDown(key)) || !Working.Add(key)) { return; } try { LastTryTime[key] = Time.realtimeSinceStartup; DoRestock(fuelInventory, gasType, showOutOfStock, showSuccess); } finally { Working.Remove(key); } } private static void DoRestock(FuelInventory fuelInventory, GasTypes gasType, bool showOutOfStock, bool showSuccess) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (!NeedsRestock(fuelInventory, gasType)) { return; } PumpBehaviour val = FindPump(fuelInventory); if ((Object)(object)val == (Object)null) { return; } ItemFuel fuelItem = val.GetFuelItem(gasType); if ((Object)(object)fuelItem == (Object)null) { return; } int num = 0; for (int i = 0; i < 8; i++) { int amountToAdd = GetAmountToAdd(fuelInventory, gasType); if (amountToAdd <= 0) { break; } if (!TryPickSource(fuelItem, out var bestSource)) { if (num == 0 && showOutOfStock) { ShowOutOfStock(gasType); } break; } Item item = bestSource.Item; int num2 = bestSource.TryRemoveItem(Mathf.Min(bestSource.Amount, amountToAdd)); if (num2 <= 0) { break; } ItemContainerRefreshHelper.Refresh(bestSource, item); fuelInventory.TryAddGas(gasType, (float)num2); num += num2; } if (num > 0 && showSuccess) { ShowRestocked(gasType, num); } } private static bool CanWork(FuelInventory fuelInventory, GasTypes gasType) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if ((Object)(object)fuelInventory == (Object)null || (int)gasType == 4) { return false; } if (ConfigManager.Instance == null || !ConfigManager.Instance.IsAutoRestockEnabled()) { return false; } if ((Object)(object)RunnerHolder._runner != (Object)null) { return RunnerHolder.IsHost(); } return false; } private static bool NeedsRestock(FuelInventory fuelInventory, GasTypes gasType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (fuelInventory.MaxFuel(gasType) <= 0f) { return false; } return fuelInventory.GetFuelPercent(gasType) <= ConfigManager.Instance.GetRestockThreshold(); } private static int GetAmountToAdd(FuelInventory fuelInventory, GasTypes gasType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) float num = fuelInventory.MaxFuel(gasType); float num2 = fuelInventory.CurrentFuel(gasType, false); return Mathf.FloorToInt(Mathf.Max(0f, num - num2)); } private static PumpBehaviour FindPump(FuelInventory fuelInventory) { PumpBehaviour[] array = Il2CppArrayBase.op_Implicit(Resources.FindObjectsOfTypeAll()); foreach (PumpBehaviour val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val._fuelInventory != (Object)null && ((Il2CppObjectBase)val._fuelInventory).Pointer == ((Il2CppObjectBase)fuelInventory).Pointer) { return val; } } return null; } private static bool TryPickSource(ItemFuel fuelItem, out ItemContainer bestSource) { bestSource = null; if ((Object)(object)fuelItem == (Object)null) { return false; } ItemContainer[] array = Il2CppArrayBase.op_Implicit(Resources.FindObjectsOfTypeAll()); int num = int.MaxValue; int num2 = int.MaxValue; foreach (ItemContainer val in array) { if (IsUsableSource(val, fuelItem)) { int amount = val.Amount; int sourceRank = GetSourceRank(val); if (amount <= num && (amount != num || sourceRank < num2)) { bestSource = val; num = amount; num2 = sourceRank; } } } return (Object)(object)bestSource != (Object)null; } private static bool IsUsableSource(ItemContainer container, ItemFuel fuelItem) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null || container is GasPumpInteractableBehavior) { return false; } if ((Object)(object)container.Item == (Object)null || container.Amount <= 1) { return false; } ItemFuel val = ((Il2CppObjectBase)container.Item).TryCast(); if ((Object)(object)val != (Object)null && (Object)(object)val.FuelData != (Object)null && (Object)(object)fuelItem.FuelData != (Object)null) { return val.FuelData.Type == fuelItem.FuelData.Type; } return false; } private static int GetSourceRank(ItemContainer container) { if (container is Shelf) { return 2; } if (container is Box) { return 1; } return 0; } private static bool IsCoolingDown(string key) { if (!LastTryTime.TryGetValue(key, out var value)) { return false; } return Time.realtimeSinceStartup - value < 0.75f; } private static void ShowOutOfStock(GasTypes gasType) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (ConfigManager.Instance != null && ConfigManager.Instance.ShouldShowOutOfStockNotification()) { string key = ((object)(GasTypes)(ref gasType)).ToString(); if (!LastOutOfStockTime.TryGetValue(key, out var value) || !(Time.realtimeSinceStartup - value < 5f)) { LastOutOfStockTime[key] = Time.realtimeSinceStartup; ModNotification.Show(LocalizationManager.Instance.GetLocalizedText("notification.out_of_stock", GetFuelName(gasType)), 5f, (Color?)null); } } } private static void ShowRestocked(GasTypes gasType, int addedAmount) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) string fuelName = GetFuelName(gasType); ModNotification.Show(LocalizationManager.Instance.GetLocalizedText("notification.restocked", fuelName, addedAmount), 5f, (Color?)null); } private static string GetFuelName(GasTypes gasType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected I4, but got Unknown return (int)gasType switch { 0 => LocalizationManager.Instance.GetLocalizedText("fuel.d"), 1 => LocalizationManager.Instance.GetLocalizedText("fuel.g87"), 2 => LocalizationManager.Instance.GetLocalizedText("fuel.g89"), 3 => LocalizationManager.Instance.GetLocalizedText("fuel.g93"), _ => LocalizationManager.Instance.GetLocalizedText("fuel.none"), }; } private static string GetKey(FuelInventory fuelInventory, GasTypes gasType) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown return $"{((Il2CppObjectBase)fuelInventory).Pointer}:{(int)gasType}"; } static FuelInventoryPatch() { GasTypes[] array = new GasTypes[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); GasTypes = (GasTypes[])(object)array; Working = new HashSet(); LastTryTime = new Dictionary(); LastOutOfStockTime = new Dictionary(); } } } namespace AutoFuelRestocker.Localization { public static class LocalizationHelper { public static Dictionary GetDefaultTranslations(string language) { Dictionary dictionary = new Dictionary(); switch (language) { case "简体中文": dictionary.Add("plugin.initializing", "开始初始化..."); dictionary.Add("plugin.author_prefix", "作者:"); dictionary.Add("plugin.initialized", "初始化成功!"); dictionary.Add("plugin.patches_skipped", "补丁已应用,跳过..."); dictionary.Add("config.enable_auto_restock.desc", "启用自动补充加油机库存"); dictionary.Add("config.restock_below_percent.desc", "当燃油库存百分比低于此值时自动补桶"); dictionary.Add("config.enable_out_of_stock_notification.desc", "没有对应油桶库存时显示提示"); dictionary.Add("notification.out_of_stock", "{0} 油桶库存不足,无法自动补充"); dictionary.Add("notification.restocked", "已自动补充 {0}({1} 加仑)"); dictionary.Add("fuel.d", "柴油"); dictionary.Add("fuel.g87", "87 号汽油"); dictionary.Add("fuel.g89", "89 号汽油"); dictionary.Add("fuel.g93", "93 号汽油"); dictionary.Add("fuel.none", "未知燃油"); break; case "繁體中文": dictionary.Add("plugin.initializing", "開始初始化..."); dictionary.Add("plugin.author_prefix", "作者:"); dictionary.Add("plugin.initialized", "初始化成功!"); dictionary.Add("plugin.patches_skipped", "補丁已應用,跳過..."); dictionary.Add("config.enable_auto_restock.desc", "啟用自動補充加油機庫存"); dictionary.Add("config.restock_below_percent.desc", "當燃油庫存百分比低於此值時自動補桶"); dictionary.Add("config.enable_out_of_stock_notification.desc", "沒有對應油桶庫存時顯示提示"); dictionary.Add("notification.out_of_stock", "{0} 油桶庫存不足,無法自動補充"); dictionary.Add("notification.restocked", "已自動補充 {0}({1} 加仑)"); dictionary.Add("fuel.d", "柴油"); dictionary.Add("fuel.g87", "87 號汽油"); dictionary.Add("fuel.g89", "89 號汽油"); dictionary.Add("fuel.g93", "93 號汽油"); dictionary.Add("fuel.none", "未知燃油"); break; case "English": dictionary.Add("plugin.initializing", "Starting initialization..."); dictionary.Add("plugin.author_prefix", "Author: "); dictionary.Add("plugin.initialized", "Initialization successful!"); dictionary.Add("plugin.patches_skipped", "Patches already applied, skipping..."); dictionary.Add("config.enable_auto_restock.desc", "Enable automatic fuel restocking"); dictionary.Add("config.restock_below_percent.desc", "Automatically restock when the fuel percentage drops below this value"); dictionary.Add("config.enable_out_of_stock_notification.desc", "Show a notification when no matching fuel barrel is available"); dictionary.Add("notification.out_of_stock", "No {0} barrels are available for automatic restocking"); dictionary.Add("notification.restocked", "Automatically restocked {0} ({1} gallons)"); dictionary.Add("fuel.d", "Diesel"); dictionary.Add("fuel.g87", "Gas 87"); dictionary.Add("fuel.g89", "Gas 89"); dictionary.Add("fuel.g93", "Gas 93"); dictionary.Add("fuel.none", "Unknown Fuel"); break; case "日本語": dictionary.Add("plugin.initializing", "初期化中..."); dictionary.Add("plugin.author_prefix", "作者:"); dictionary.Add("plugin.initialized", "初期化に成功しました!"); dictionary.Add("plugin.patches_skipped", "パッチは既に適用されています。スキップします..."); dictionary.Add("config.enable_auto_restock.desc", "給油機の自動補充を有効にする"); dictionary.Add("config.restock_below_percent.desc", "燃料残量がこの割合を下回ると自動で補充する"); dictionary.Add("config.enable_out_of_stock_notification.desc", "対応する燃料缶の在庫がない時に通知を表示する"); dictionary.Add("notification.out_of_stock", "{0} の在庫がないため自動補充できません"); dictionary.Add("notification.restocked", "{0} を自動補充しました({1} ガロン)"); dictionary.Add("fuel.d", "ディーゼル"); dictionary.Add("fuel.g87", "87 オクタン"); dictionary.Add("fuel.g89", "89 オクタン"); dictionary.Add("fuel.g93", "93 オクタン"); dictionary.Add("fuel.none", "不明な燃料"); break; } return dictionary; } } public class LocalizationManager { private static LocalizationManager _instance; private readonly ModLocalizationService _service; public static readonly string[] SupportedLanguages = new string[4] { "简体中文", "繁體中文", "English", "日本語" }; public static LocalizationManager Instance => _instance ?? (_instance = new LocalizationManager()); private LocalizationManager() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown _service = new ModLocalizationService((IEnumerable)SupportedLanguages, "English"); Initialize(); } public void Initialize() { string[] supportedLanguages = SupportedLanguages; foreach (string text in supportedLanguages) { Dictionary defaultTranslations = LocalizationHelper.GetDefaultTranslations(text); if (defaultTranslations != null && defaultTranslations.Count > 0) { _service.AddTranslations(text, defaultTranslations); } } } public string GetLocalizedText(string key, params object[] args) { return _service.GetText(key, args); } } } namespace AutoFuelRestocker.Configuration { public class ConfigManager : ModConfigBase { private static ConfigManager _instance; public ConfigEntry EnableAutoRestock { get; private set; } public ConfigEntry RestockBelowPercent { get; private set; } public ConfigEntry EnableOutOfStockNotification { get; private set; } public static ConfigManager Instance => _instance; private ConfigManager(ConfigFile configFile) : base(configFile) { InitializeDefaultConfigs(); } public static void Initialize(ConfigFile configFile) { if (_instance == null) { _instance = new ConfigManager(configFile); } } private void InitializeDefaultConfigs() { EnableAutoRestock = ((ModConfigBase)this).RegisterBool("General", "EnableAutoRestock", GetLocalizedDescription("config.enable_auto_restock.desc"), true); RestockBelowPercent = ((ModConfigBase)this).RegisterFloatRange("Function", "RestockBelowPercent", GetLocalizedDescription("config.restock_below_percent.desc"), 20f, 0f, 100f); EnableOutOfStockNotification = ((ModConfigBase)this).RegisterBool("Notification", "EnableOutOfStockNotification", GetLocalizedDescription("config.enable_out_of_stock_notification.desc"), true); } public bool IsAutoRestockEnabled() { if (EnableAutoRestock != null) { return EnableAutoRestock.Value; } return false; } public float GetRestockThreshold() { if (RestockBelowPercent == null) { return 0.2f; } return Math.Clamp(RestockBelowPercent.Value / 100f, 0f, 1f); } public bool ShouldShowOutOfStockNotification() { if (EnableOutOfStockNotification != null) { return EnableOutOfStockNotification.Value; } return false; } private string GetLocalizedDescription(string key) { return LocalizationManager.Instance?.GetLocalizedText(key) ?? key; } } }