diff --git a/.gitignore b/.gitignore index dd63655..fcfaa2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# C# project files +*.user + # Rider project files /.idea/ @@ -5,6 +8,9 @@ bin/ obj/ +# C++ build folders +build/ + # packaged files /*/package/*.zip /*/package/patchers/ diff --git a/DSP_Mods.sln b/DSP_Mods.sln index 11bcc6e..f6f2f9a 100644 --- a/DSP_Mods.sln +++ b/DSP_Mods.sln @@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LabOpt", "LabOpt\LabOpt.csp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LabOptPreloader", "LabOptPreloader\LabOptPreloader.csproj", "{F2D5EBE8-7D9D-4572-9A3E-4DD1114FD99F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DustbinPreloader", "DustbinPreloader\DustbinPreloader.csproj", "{B8EB3D8D-5613-42F0-9040-EAA11A38C6AC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,5 +77,9 @@ Global {F2D5EBE8-7D9D-4572-9A3E-4DD1114FD99F}.Debug|Any CPU.Build.0 = Debug|Any CPU {F2D5EBE8-7D9D-4572-9A3E-4DD1114FD99F}.Release|Any CPU.ActiveCfg = Release|Any CPU {F2D5EBE8-7D9D-4572-9A3E-4DD1114FD99F}.Release|Any CPU.Build.0 = Release|Any CPU + {B8EB3D8D-5613-42F0-9040-EAA11A38C6AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8EB3D8D-5613-42F0-9040-EAA11A38C6AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8EB3D8D-5613-42F0-9040-EAA11A38C6AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8EB3D8D-5613-42F0-9040-EAA11A38C6AC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Dustbin/Dustbin.cs b/Dustbin/Dustbin.cs index 7fc8177..43070aa 100644 --- a/Dustbin/Dustbin.cs +++ b/Dustbin/Dustbin.cs @@ -1,18 +1,23 @@ -using System; -using System.Collections; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using BepInEx; using crecheng.DSPModSave; using HarmonyLib; +using NebulaAPI; namespace Dustbin; [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] [BepInDependency(DSPModSavePlugin.MODGUID)] -public class Dustbin : BaseUnityPlugin, IModCanSave +[BepInDependency(NebulaModAPI.API_GUID)] +public class Dustbin : BaseUnityPlugin, IModCanSave, IMultiplayerMod { + public string Version => PluginInfo.PLUGIN_VERSION; + private const ushort ModSaveVersion = 1; + public new static readonly BepInEx.Logging.ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME); @@ -20,6 +25,11 @@ public class Dustbin : BaseUnityPlugin, IModCanSave public static readonly int[] SandsFactors = { 0, 1, 5, 10, 100 }; public static bool[] IsFluid; + public bool CheckVersion(string hostVersion, string clientVersion) + { + return hostVersion.Equals(clientVersion); + } + private void Awake() { _cfgEnabled = Config.Bind("General", "Enabled", _cfgEnabled, "enable/disable this plugin").Value; @@ -31,6 +41,9 @@ public class Dustbin : BaseUnityPlugin, IModCanSave Harmony.CreateAndPatchAll(typeof(Dustbin)); Harmony.CreateAndPatchAll(typeof(StoragePatch)); Harmony.CreateAndPatchAll(typeof(TankPatch)); + + NebulaModAPI.RegisterPackets(Assembly.GetExecutingAssembly()); + NebulaModAPI.OnPlanetLoadFinished += RequestPlanetDustbinData; } [HarmonyPrefix] @@ -61,14 +74,97 @@ public class Dustbin : BaseUnityPlugin, IModCanSave public void Import(BinaryReader r) { var version = r.ReadUInt16(); - if (version > 0) - { - StoragePatch.Import(r); - TankPatch.Import(r); - } + if (version <= 0) return; + StoragePatch.Import(r); + TankPatch.Import(r); } public void IntoOtherSave() { } + + public static byte[] ExportData(PlanetFactory factory) + { + var planetId = factory.planetId; + var storageIds = new List(); + var tankIds = new List(); + + var factoryStorage = factory.factoryStorage; + var storagePool = factoryStorage.storagePool; + for (var i = 1; i < factoryStorage.storageCursor; i++) + { + var storage = storagePool[i]; + if (storage == null || storage.id != i) continue; + if ((bool)StoragePatch.IsDustbinField.GetValue(storage)) + storageIds.Add(i); + } + + var tankPool = factoryStorage.tankPool; + for (var i = 1; i < factoryStorage.tankCursor; i++) + { + ref var tank = ref tankPool[i]; + if (tank.id != i) continue; + if ((bool)TankPatch.IsDustbinField.GetValue(tank)) + tankIds.Add(i); + } + + using var p = NebulaModAPI.GetBinaryWriter(); + using var w = p.BinaryWriter; + w.Write(planetId); + w.Write(storageIds.Count); + foreach (var storageId in storageIds) + w.Write(storageId); + w.Write(tankIds.Count); + foreach (var tankId in tankIds) + w.Write(tankId); + return p.CloseAndGetBytes(); + } + + public static void ImportData(byte[] bytes) + { + using var p = NebulaModAPI.GetBinaryReader(bytes); + using var r = p.BinaryReader; + var planetId = r.ReadInt32(); + var factory = GameMain.galaxy.PlanetById(planetId)?.factory; + if (factory == null) return; + + var factoryStorage = factory.factoryStorage; + var count = r.ReadInt32(); + var storagePool = factoryStorage.storagePool; + var cursor = factoryStorage.storageCursor; + for (var i = 1; i < cursor; i++) + { + var storage = storagePool[i]; + if (storage == null || storage.id != i) continue; + StoragePatch.IsDustbinField.SetValue(storage, false); + } + + for (var i = 0; i < count; i++) + { + var id = r.ReadInt32(); + StoragePatch.IsDustbinField.SetValue(storagePool[id], true); + } + + count = r.ReadInt32(); + var tankPool = factoryStorage.tankPool; + cursor = factoryStorage.tankCursor; + for (var i = 1; i < cursor; i++) + { + ref var tank = ref tankPool[i]; + if (tank.id != i) continue; + StoragePatch.IsDustbinField.SetValueDirect(__makeref(tank), false); + } + + for (var i = 0; i < count; i++) + { + var id = r.ReadInt32(); + StoragePatch.IsDustbinField.SetValueDirect(__makeref(tankPool[id]), true); + } + } + + public void RequestPlanetDustbinData(int planetId) + { + if (NebulaModAPI.IsMultiplayerActive && NebulaModAPI.MultiplayerSession.LocalPlayer.IsClient) + NebulaModAPI.MultiplayerSession.Network.SendPacket(new NebulaSupport.Packet.ToggleEvent(planetId, 0, false)); + } } diff --git a/Dustbin/Dustbin.csproj b/Dustbin/Dustbin.csproj index f0e5ac2..8097725 100644 --- a/Dustbin/Dustbin.csproj +++ b/Dustbin/Dustbin.csproj @@ -4,7 +4,7 @@ net472 org.soardev.dustbin DSP MOD - Dustbin - 1.2.1 + 1.3.0 true latest Dustbin @@ -16,6 +16,7 @@ + @@ -23,7 +24,8 @@ + - + diff --git a/Dustbin/MyCheckbox.cs b/Dustbin/MyCheckbox.cs index 56c2d7b..8bfbc45 100644 --- a/Dustbin/MyCheckbox.cs +++ b/Dustbin/MyCheckbox.cs @@ -4,7 +4,7 @@ using UnityEngine.UI; namespace Dustbin; -// MyCheckbox modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/LSTM.cs +// MyCheckBox modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyCheckBox.cs public class MyCheckBox : MonoBehaviour { public UIButton uiButton; @@ -27,14 +27,18 @@ public class MyCheckBox : MonoBehaviour public static MyCheckBox CreateCheckBox(bool check, Transform parent = null, float x = 0f, float y = 0f, string label = "", int fontSize = 15) { - UIBuildMenu buildMenu = UIRoot.instance.uiGame.buildMenu; - UIButton src = buildMenu.uxFacilityCheck; + var buildMenu = UIRoot.instance.uiGame.buildMenu; + var src = buildMenu.uxFacilityCheck; - GameObject go = GameObject.Instantiate(src.gameObject); + var go = Instantiate(src.gameObject); + var rect = go.transform as RectTransform; + if (rect == null) + { + return null; + } go.name = "my-checkbox"; - MyCheckBox cb = go.AddComponent(); + var cb = go.AddComponent(); cb._checked = check; - RectTransform rect = go.transform as RectTransform; if (parent != null) { rect.SetParent(parent); @@ -47,14 +51,11 @@ public class MyCheckBox : MonoBehaviour cb.rectTrans = rect; cb.uiButton = go.GetComponent(); cb.checkImage = go.transform.Find("checked")?.GetComponent(); - //ResetAnchor(cb.checkImage.rectTransform); - //text - Transform child = go.transform.Find("text"); + var child = go.transform.Find("text"); if (child != null) { - //ResetAnchor(child as RectTransform); - GameObject.DestroyImmediate(child.GetComponent()); + DestroyImmediate(child.GetComponent()); cb.labelText = child.GetComponent(); cb.labelText.fontSize = fontSize; cb.SetLabelText(label); @@ -62,7 +63,10 @@ public class MyCheckBox : MonoBehaviour //value cb.uiButton.onClick += cb.OnClick; - cb.checkImage.enabled = check; + if (cb.checkImage != null) + { + cb.checkImage.enabled = check; + } return cb; } diff --git a/Dustbin/NebulaSupport/Packets.cs b/Dustbin/NebulaSupport/Packets.cs new file mode 100644 index 0000000..8a95145 --- /dev/null +++ b/Dustbin/NebulaSupport/Packets.cs @@ -0,0 +1,77 @@ +using NebulaAPI; + +namespace Dustbin.NebulaSupport +{ + namespace Packet + { + public class SyncPlanetData + { + public byte[] Data { get; set; } + + public SyncPlanetData() + { + } + + public SyncPlanetData(byte[] data) + { + Data = data; + } + } + + public class ToggleEvent + { + public int PlanetId { get; set; } + public int StorageId { get; set; } + public bool Enable { get; set; } + + public ToggleEvent() + { + } + + public ToggleEvent(int planetId, int storageId, bool enable) + { + PlanetId = planetId; + StorageId = storageId; + Enable = enable; + } + } + + [RegisterPacketProcessor] + internal class SyncPlanetDataProcessor : BasePacketProcessor + { + public override void ProcessPacket(SyncPlanetData packet, INebulaConnection conn) + { + Dustbin.ImportData(packet.Data); + } + } + + [RegisterPacketProcessor] + internal class ToggleEventProcessor : BasePacketProcessor + { + public override void ProcessPacket(ToggleEvent packet, INebulaConnection conn) + { + var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; + if (factory == null) return; + var storageId = packet.StorageId; + switch (storageId) + { + case 0: + NebulaModAPI.MultiplayerSession.Network.SendPacket(new SyncPlanetData(Dustbin.ExportData(factory))); + return; + case < 0: + { + var tankPool = factory.factoryStorage.tankPool; + TankPatch.IsDustbinField.SetValue(tankPool[-storageId], packet.Enable); + return; + } + default: + { + var storagePool = factory.factoryStorage.storagePool; + StoragePatch.IsDustbinField.SetValue(storagePool[storageId], packet.Enable); + return; + } + } + } + } + } +} \ No newline at end of file diff --git a/Dustbin/README.md b/Dustbin/README.md index 0ddf012..2b95c6a 100644 --- a/Dustbin/README.md +++ b/Dustbin/README.md @@ -5,6 +5,11 @@ ## Updates +* 1.3.0 + * Reworked dustbin support for Tanks, to improve performance and resolve known bugs. + * Support for [Nebula Mupltiplayer Mod](https://dsp.thunderstore.io/package/nebula/NebulaMultiplayerMod/). + * Adopted bug fixes from [ModFixerOne](https://dsp.thunderstore.io/package/starfi5h/ModFixerOne/) by [starfi5h](https://github.com/starfi5h/). + * 1.2.1 * Fix dynamic array bug in codes, which causes various bugs and errors. @@ -28,7 +33,6 @@ * Can get sands from destroyed items (with factors configurable): * Get 10/100 sands from each silicon/fractal silicon ore * Get 1 sand from any other normal item but fluid -* Known bug: Tank with some fluids inside cannot output to belts if turned into dusbin, you can put a dustbin upon a normal tank to resolve this problem. ## 使用说明 @@ -37,4 +41,3 @@ * 可以从移除的物品中获得沙子(可配置,下为默认值): * 从硅石和分形硅石中获得10/100个沙子。 * 从普通物品中获得1个沙子,但液体不会给沙子。 -* 已知问题:装有液体的储液罐变为垃圾桶后无法输出液体,可以在普通储液罐上面叠一个垃圾桶来解决这个问题。 diff --git a/Dustbin/StoragePatch.cs b/Dustbin/StoragePatch.cs index 251b2bc..8e2fdbe 100644 --- a/Dustbin/StoragePatch.cs +++ b/Dustbin/StoragePatch.cs @@ -1,23 +1,15 @@ -using System.Collections.Generic; -using System.IO; -using System.Reflection.Emit; +using System.IO; +using System.Reflection; using HarmonyLib; +using NebulaAPI; using UnityEngine; namespace Dustbin; -public class StorageComponentWithDustbin : StorageComponent -{ - public bool IsDusbin; - public StorageComponentWithDustbin(int size): base(size) - { - IsDusbin = false; - } -} - [HarmonyPatch] public static class StoragePatch { + public static readonly FieldInfo IsDustbinField = AccessTools.Field(typeof(StorageComponent), "IsDustbin"); private static MyCheckBox _storageDustbinCheckBox; private static int _lastStorageId; @@ -43,7 +35,7 @@ public static class StoragePatch for (var j = 1; j < cursor; j++) { if (storagePool[j] == null || storagePool[j].id != j) continue; - if (storagePool[j] is not StorageComponentWithDustbin { IsDusbin: true }) continue; + if (!(bool)IsDustbinField.GetValue(storagePool[j])) continue; tempWriter.Write(j); count++; } @@ -81,10 +73,9 @@ public static class StoragePatch for (var count = r.ReadInt32(); count > 0; count--) { var id = r.ReadInt32(); - if (id > 0 && id < storagePool.Length && storagePool[id] != null && storagePool[id].id == id && - storagePool[id] is StorageComponentWithDustbin comp) + if (id > 0 && id < storagePool.Length && storagePool[id] != null && storagePool[id].id == id) { - comp.IsDusbin = true; + IsDustbinField.SetValue(storagePool[id], true); } } } @@ -102,8 +93,11 @@ public static class StoragePatch if (storageId <= 0) return; var storagePool = window.factoryStorage.storagePool; if (storagePool[storageId].id != storageId) return; - if (storagePool[storageId] is not StorageComponentWithDustbin comp) return; - comp.IsDusbin = _storageDustbinCheckBox.Checked; + var enabled = _storageDustbinCheckBox.Checked; + IsDustbinField.SetValue(storagePool[storageId], enabled); + if (!NebulaModAPI.IsMultiplayerActive) return; + var planetId = window.factory.planetId; + NebulaModAPI.MultiplayerSession.Network.SendPacketToLocalStar(new NebulaSupport.Packet.ToggleEvent(planetId, storageId, enabled)); }; } @@ -117,8 +111,7 @@ public static class StoragePatch if (storageId <= 0) return; var storagePool = __instance.factoryStorage.storagePool; if (storagePool[storageId].id != storageId) return; - if (storagePool[storageId] is not StorageComponentWithDustbin comp) return; - _storageDustbinCheckBox.Checked = comp.IsDusbin; + _storageDustbinCheckBox.Checked = (bool)IsDustbinField.GetValue(storagePool[storageId]); if (__instance.transform is RectTransform rectTrans) { _storageDustbinCheckBox.rectTrans.anchoredPosition3D = new Vector3(50, 58 - rectTrans.sizeDelta.y, 0); @@ -144,7 +137,7 @@ public static class StoragePatch private static bool StorageComponent_AddItem_Prefix(ref int __result, StorageComponent __instance, int itemId, int count, int inc, ref int remainInc, bool useBan = false) { - if (__instance is not StorageComponentWithDustbin { IsDusbin: true }) return true; + if (!(bool)IsDustbinField.GetValue(__instance)) return true; remainInc = inc; __result = count; var fluidArr = Dustbin.IsFluid; @@ -162,9 +155,6 @@ public static class StoragePatch var addCount = count * sandsPerItem; player.sandCount += addCount; GameMain.history.OnSandCountChange(player.sandCount, addCount); - /* Following line crashes game, seems that it should not be called in this working thread: - * UIRoot.instance.uiGame.OnSandCountChanged(player.sandCount, addCount); - */ return false; } @@ -173,29 +163,9 @@ public static class StoragePatch [HarmonyPatch(typeof(StorageComponent), "Import")] private static void StorageComponent_Import_Postfix(StorageComponent __instance) { - if (__instance.bans >= 0 || __instance is not StorageComponentWithDustbin comp) + if (__instance.bans >= 0) return; __instance.bans = -__instance.bans - 1; - comp.IsDusbin = true; - } - - /* Replace: new StorageComponent(int) => new StorageComponentWithDustbin(int) */ - [HarmonyTranspiler] - [HarmonyPatch(typeof(FactoryStorage), "Import")] - [HarmonyPatch(typeof(FactoryStorage), "NewStorageComponent")] - private static IEnumerable FactoryStorage_NewStorageComponent_Transpiler( - IEnumerable instructions) - { - foreach (var instr in instructions) - { - if (instr.opcode == OpCodes.Newobj && instr.OperandIs(AccessTools.Constructor(typeof(StorageComponent), new [] { typeof(int) }))) - { - yield return new CodeInstruction(OpCodes.Newobj, AccessTools.Constructor(typeof(StorageComponentWithDustbin), new [] { typeof(int) })); - } - else - { - yield return instr; - } - } + IsDustbinField.SetValue(__instance, true); } } diff --git a/Dustbin/TankPatch.cs b/Dustbin/TankPatch.cs index e315ec4..fdb8918 100644 --- a/Dustbin/TankPatch.cs +++ b/Dustbin/TankPatch.cs @@ -1,138 +1,58 @@ -using System; -using System.IO; +using System.IO; +using System.Reflection; using HarmonyLib; -using HarmonyLib.Tools; +using NebulaAPI; namespace Dustbin; -using IsDustbinIndexer = DynamicObjectArray>>; - -public class DynamicValueArray where T: struct -{ - private T[] _store = new T[64]; - - public T this[int index] - { - get - { - if (index < 0 || index >= _store.Length) return default; - return _store[index]; - } - set - { - if (index >= _store.Length) - { - var count = index | (index >> 1); - count |= count >> 2; - count |= count >> 4; - count |= count >> 8; - count |= count >> 16; - Array.Resize(ref _store, count + 1); - } - _store[index] = value; - } - } - - public void Reset() - { - _store = new T[16]; - } - - public delegate void ForEachFunc(int id, T value); - public void ForEach(ForEachFunc func) - { - var len = _store.Length; - for (var i = 0; i < len; i++) - { - func(i, _store[i]); - } - } -} - -public class DynamicObjectArray where T: class, new() -{ - private T[] _store = new T[16]; - - public T this[int index] - { - get - { - if (index < 0) return null; - if (index >= _store.Length) - { - var count = index | (index >> 1); - count |= count >> 2; - count |= count >> 4; - count |= count >> 8; - count |= count >> 16; - Array.Resize(ref _store, count + 1); - } - T result = _store[index]; - if (result != null) - return result; - result = new T(); - _store[index] = result; - return result; - } - } - - public void Reset() - { - _store = new T[16]; - } - - public delegate void ForEachFunc(int id, T value); - public void ForEach(ForEachFunc func) - { - var len = _store.Length; - for (var i = 0; i < len; i++) - { - func(i, _store[i]); - } - } -} - [HarmonyPatch] public static class TankPatch { + public static readonly FieldInfo IsDustbinField = AccessTools.Field(typeof(TankComponent), "IsDustbin"); private static MyCheckBox _tankDustbinCheckBox; - private static int lastTankId; - private static IsDustbinIndexer tankIsDustbin = new(); + private static int _lastTankId; public static void Reset() { - lastTankId = 0; - tankIsDustbin.Reset(); + _lastTankId = 0; } public static void Export(BinaryWriter w) { var tempStream = new MemoryStream(); var tempWriter = new BinaryWriter(tempStream); - tankIsDustbin.ForEach((i, star) => - { - star?.ForEach((j, planet) => - { - var count = 0; - planet?.ForEach((id, v) => - { - if (!v) return; - tempWriter.Write(id); - count++; - }); - if (count == 0) return; - tempWriter.Flush(); - tempStream.Position = 0; - w.Write((byte)2); - var planetId = i * 100 + j; - w.Write(planetId); - w.Write(count); - /* FixMe: May BinaryWriter not sync with its BaseStream while subclass overrides Write()? */ - tempStream.CopyTo(w.BaseStream); - tempStream.SetLength(0); - }); - }); + var factories = GameMain.data.factories; + var factoryCount = GameMain.data.factoryCount; + for (var i = 0; i < factoryCount; i++) + { + var factory = factories[i]; + if (factory == null) continue; + var storage = factory.factoryStorage; + var tankPool = storage.tankPool; + var cursor = storage.tankCursor; + var count = 0; + + for (var j = 1; j < cursor; j++) + { + if (tankPool[j].id != j) continue; + if (!(bool)IsDustbinField.GetValue(tankPool[j])) continue; + tempWriter.Write(j); + count++; + } + + if (count == 0) continue; + + tempWriter.Flush(); + tempStream.Position = 0; + w.Write((byte)2); + w.Write(factory.planetId); + w.Write(count); + /* FixMe: May BinaryWriter not sync with its BaseStream while subclass overrides Write()? */ + tempStream.CopyTo(w.BaseStream); + tempStream.SetLength(0); + } + tempWriter.Dispose(); tempStream.Dispose(); } @@ -143,13 +63,25 @@ public static class TankPatch { r.ReadByte(); var planetId = r.ReadInt32(); - var data = tankIsDustbin[15]; - data[0][0] = true; - data = tankIsDustbin[20]; - data[0][0] = true; + var planet = GameMain.data.galaxy.PlanetById(planetId); + var tankPool = planet?.factory.factoryStorage.tankPool; + if (tankPool == null) + { + for (var count = r.ReadInt32(); count > 0; count--) + { + r.ReadInt32(); + } + + continue; + } + for (var count = r.ReadInt32(); count > 0; count--) { - tankIsDustbin[planetId / 100][planetId % 100][r.ReadInt32()] = true; + var id = r.ReadInt32(); + if (id > 0 && id < tankPool.Length && tankPool[id].id == id) + { + IsDustbinField.SetValueDirect(__makeref(tankPool[id]), true); + } } } } @@ -163,91 +95,155 @@ public static class TankPatch _tankDustbinCheckBox.OnChecked += () => { var tankId = window.tankId; - if (tankId <= 0 || window.storage.tankPool[tankId].id != tankId) return; - var planetId = window.storage.planet.id; - tankIsDustbin[planetId / 100][planetId % 100][tankId] = _tankDustbinCheckBox.Checked; + if (tankId <= 0) return; + var tankPool = window.storage.tankPool; + if (tankPool[tankId].id != tankId) return; + var enabled = _tankDustbinCheckBox.Checked; + IsDustbinField.SetValueDirect(__makeref(tankPool[tankId]), enabled); + if (!NebulaModAPI.IsMultiplayerActive) return; + var planetId = window.factory.planetId; + NebulaModAPI.MultiplayerSession.Network.SendPacketToLocalStar(new NebulaSupport.Packet.ToggleEvent(planetId, -tankId, enabled)); }; } - + [HarmonyPostfix] [HarmonyPatch(typeof(UITankWindow), "_OnUpdate")] private static void UITankWindow__OnUpdate_Postfix(UITankWindow __instance) { var tankId = __instance.tankId; - if (lastTankId == tankId) return; - lastTankId = tankId; - if (tankId > 0 && __instance.storage.tankPool[tankId].id == tankId) - { - var planetId = __instance.storage.planet.id; - _tankDustbinCheckBox.Checked = tankIsDustbin[planetId / 100][planetId % 100][tankId]; - } - } + if (_lastTankId == tankId) return; + _lastTankId = tankId; - private struct TankState - { - public int TankId; - public int FluidId; - public int FluidCount; - public int FluidInc; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(TankComponent), "GameTick")] - private static void TankComponent_GameTick_Prefix(ref TankComponent __instance, ref TankState __state, PlanetFactory factory) - { - var planetId = factory.planetId; - var data = tankIsDustbin[planetId / 100][planetId % 100]; - ref var tank = ref __instance; - while (true) - { - if (data[tank.id]) - { - __state.TankId = tank.id; - __state.FluidId = tank.fluidId; - __state.FluidCount = tank.fluidCount; - __state.FluidInc = tank.fluidInc; - tank.fluidId = tank.fluidCount = tank.fluidInc = 0; - return; - } - if (tank.fluidCount < tank.fluidCapacity || tank.nextTankId <= 0) - { - __state.TankId = -1; - return; - } - var nextTankId = tank.nextTankId; - tank = ref factory.factoryStorage.tankPool[nextTankId]; - if (tank.id == nextTankId) continue; - __state.TankId = -1; - return; - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(TankComponent), "GameTick")] - private static void TankComponent_GameTick_Postfix(ref TankComponent __instance, ref TankState __state, PlanetFactory factory) - { - if (__state.TankId < 0) return; - var tankId = __state.TankId; - if (__instance.id == tankId) - { - __instance.fluidId = __state.FluidId; - __instance.fluidCount = __state.FluidCount; - __instance.fluidInc = __state.FluidInc; - return; - } - ref var tank = ref factory.factoryStorage.tankPool[tankId]; - if (tank.id != tankId) return; - tank.fluidId = __state.FluidId; - tank.fluidCount = __state.FluidCount; - tank.fluidInc = __state.FluidInc; + if (tankId <= 0) return; + var tankPool = __instance.storage.tankPool; + if (tankPool[tankId].id != tankId) return; + _tankDustbinCheckBox.Checked = (bool)IsDustbinField.GetValue(tankPool[tankId]); } [HarmonyPrefix] - [HarmonyPatch(typeof(FactoryStorage), "RemoveTankComponent")] - private static void FactoryStorage_RemoveTankComponent_Prefix(FactoryStorage __instance, int id) + [HarmonyPatch(typeof(TankComponent), "GameTick")] + private static bool TankComponent_GameTick_Prefix(ref TankComponent __instance, PlanetFactory factory) { - if (__instance.tankPool[id].id <= 0) return; - var planetId = __instance.planet.id; - tankIsDustbin[planetId / 100][planetId % 100][id] = false; + if (__instance.fluidInc < 0) + __instance.fluidInc = 0; + + if (!__instance.isBottom) + return false; + + var cargoTraffic = factory.cargoTraffic; + var tankPool = factory.factoryStorage.tankPool; + var belt = __instance.belt0; + if (belt > 0) + { + TankComponentUpdateBelt(ref __instance, belt, __instance.isOutput0, ref cargoTraffic, ref tankPool); + } + + belt = __instance.belt1; + if (belt > 0) + { + TankComponentUpdateBelt(ref __instance, belt, __instance.isOutput1, ref cargoTraffic, ref tankPool); + } + + belt = __instance.belt2; + if (belt > 0) + { + TankComponentUpdateBelt(ref __instance, belt, __instance.isOutput2, ref cargoTraffic, ref tankPool); + } + + belt = __instance.belt3; + if (belt > 0) + { + TankComponentUpdateBelt(ref __instance, belt, __instance.isOutput3, ref cargoTraffic, ref tankPool); + } + + return false; } -} + + private static void TankComponentUpdateBelt(ref TankComponent thisTank, int belt, bool isOutput, ref CargoTraffic cargoTraffic, ref TankComponent[] tankPool) + { + switch (isOutput) + { + case true when thisTank.outputSwitch: + { + if (thisTank.fluidId <= 0 || thisTank.fluidCount <= 0) return; + var inc = thisTank.fluidInc == 0 ? 0 : thisTank.fluidInc / thisTank.fluidCount; + if (!cargoTraffic.TryInsertItemAtHead(belt, thisTank.fluidId, 1, (byte)inc)) return; + thisTank.fluidCount--; + thisTank.fluidInc -= inc; + return; + } + case false when thisTank.inputSwitch: + { + byte stack; + byte inc; + switch (thisTank.fluidId) + { + case > 0 when thisTank.fluidCount < thisTank.fluidCapacity && cargoTraffic.TryPickItemAtRear(belt, thisTank.fluidId, null, out stack, out inc) > 0 && + !(bool)IsDustbinField.GetValue(thisTank): + thisTank.fluidCount += stack; + thisTank.fluidInc += inc; + return; + case 0: + { + var count = cargoTraffic.TryPickItemAtRear(belt, 0, ItemProto.fluids, out stack, out inc); + if (count <= 0 || (bool)IsDustbinField.GetValue(thisTank)) return; + thisTank.fluidId = count; + thisTank.fluidCount += stack; + thisTank.fluidInc += inc; + return; + } + } + + if (thisTank.fluidCount < thisTank.fluidCapacity || cargoTraffic.GetItemIdAtRear(belt) != thisTank.fluidId || thisTank.nextTankId <= 0) return; + ref var targetTank = ref tankPool[thisTank.nextTankId]; + while (targetTank.fluidCount >= targetTank.fluidCapacity) + { + ref var lastTank = ref tankPool[targetTank.lastTankId]; + if (targetTank.fluidId != lastTank.fluidId) + { + targetTank = ref lastTank; + break; + } + + if (!targetTank.inputSwitch) + { + targetTank = ref lastTank; + break; + } + + if (targetTank.nextTankId <= 0) + { + break; + } + + targetTank = ref tankPool[targetTank.nextTankId]; + } + + ref var lastTank2 = ref tankPool[targetTank.lastTankId]; + if (!targetTank.inputSwitch) + { + targetTank = ref lastTank2; + } + else + { + var fluidId = targetTank.fluidId; + if (fluidId != 0 && fluidId != lastTank2.fluidId) + { + targetTank = ref lastTank2; + } + } + + if (targetTank.id == thisTank.id || targetTank.fluidCount >= targetTank.fluidCapacity || !lastTank2.outputSwitch || + cargoTraffic.TryPickItemAtRear(belt, thisTank.fluidId, null, out stack, out inc) <= 0 || (bool)IsDustbinField.GetValue(targetTank)) return; + if (targetTank.fluidCount == 0) + { + targetTank.fluidId = thisTank.fluidId; + } + + targetTank.fluidCount += stack; + targetTank.fluidInc += inc; + return; + } + } + } +} \ No newline at end of file diff --git a/Dustbin/package/manifest.json b/Dustbin/package/manifest.json index 64177a5..b2bf9c2 100644 --- a/Dustbin/package/manifest.json +++ b/Dustbin/package/manifest.json @@ -1,10 +1,11 @@ { "name": "Dustbin", - "version_number": "1.2.1", + "version_number": "1.3.0", "website_url": "https://github.com/soarqin/DSP_Mods/tree/master/Dustbin", "description": "Can turn Storages and Tanks into Dustbin(Destroy incoming items) / 储物仓和储液罐可以转变为垃圾桶(销毁送进的物品)", "dependencies": [ "xiaoye97-BepInEx-5.4.17", - "CommonAPI-DSPModSave-1.1.4" + "CommonAPI-DSPModSave-1.1.4", + "nebula-NebulaMultiplayerModApi-1.3.1" ] } diff --git a/DustbinPreloader/DustbinPreloader.cs b/DustbinPreloader/DustbinPreloader.cs new file mode 100644 index 0000000..2632c4f --- /dev/null +++ b/DustbinPreloader/DustbinPreloader.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using BepInEx.Logging; +using Mono.Cecil; + +namespace DustbinPreloader; + +public static class Preloader +{ + private static readonly ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource("Dustbin Preloader"); + public static IEnumerable TargetDLLs { get; } = new[] { "Assembly-CSharp.dll" }; + + public static void Patch(AssemblyDefinition assembly) + { + var gameModule = assembly.MainModule; + try + { + // Add field: int StorageComponent.IsDustbin; + gameModule.GetType("StorageComponent").AddFied("IsDustbin", gameModule.TypeSystem.Boolean); + } + catch (Exception e) + { + Logger.LogError("Failed to add `bool StorageComponent.IsDustbin`!"); + Logger.LogError(e); + } + try + { + // Add field: int TankComponent.IsDustbin; + gameModule.GetType("TankComponent").AddFied("IsDustbin", gameModule.TypeSystem.Boolean); + } + catch (Exception e) + { + Logger.LogError("Failed to add `bool TankComponent.IsDustbin`!"); + Logger.LogError(e); + } + } + + private static void AddFied(this TypeDefinition typeDefinition, string fieldName, TypeReference fieldType) + { + var newField = new FieldDefinition(fieldName, FieldAttributes.Public, fieldType); + typeDefinition.Fields.Add(newField); + Logger.LogDebug("Add " + newField); + } +} diff --git a/DustbinPreloader/DustbinPreloader.csproj b/DustbinPreloader/DustbinPreloader.csproj new file mode 100644 index 0000000..847468a --- /dev/null +++ b/DustbinPreloader/DustbinPreloader.csproj @@ -0,0 +1,26 @@ + + + + net472 + DustbinPreloader + DSP MOD - Prealoder for Dustbin + 1.3.0 + true + latest + + + + + + + + + + + + + + + + + diff --git a/LabOptPreloader/LabOptPreloader.cs b/LabOptPreloader/LabOptPreloader.cs index ad76c90..b697d49 100644 --- a/LabOptPreloader/LabOptPreloader.cs +++ b/LabOptPreloader/LabOptPreloader.cs @@ -12,7 +12,7 @@ namespace LabOptPreloader; public static class Preloader { - private static readonly ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource("ModFixerOne Preloader"); + private static readonly ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource("LabOpt Preloader"); public static IEnumerable TargetDLLs { get; } = new[] { "Assembly-CSharp.dll" }; public static void Patch(AssemblyDefinition assembly)