diff --git a/NebulaClient/NebulaClient.csproj b/NebulaClient/NebulaClient.csproj
index 7de658d4c..d8480cabc 100644
--- a/NebulaClient/NebulaClient.csproj
+++ b/NebulaClient/NebulaClient.csproj
@@ -42,6 +42,8 @@
+
+
diff --git a/NebulaClient/PacketProcessors/Factory/Belt/BeltUpdatePickupItemsProcessor.cs b/NebulaClient/PacketProcessors/Factory/Belt/BeltUpdatePickupItemsProcessor.cs
new file mode 100644
index 000000000..49193306d
--- /dev/null
+++ b/NebulaClient/PacketProcessors/Factory/Belt/BeltUpdatePickupItemsProcessor.cs
@@ -0,0 +1,25 @@
+using NebulaModel.Attributes;
+using NebulaModel.Networking;
+using NebulaModel.Packets.Belt;
+using NebulaModel.Packets.Processors;
+
+namespace NebulaClient.PacketProcessors.Factory.Belt
+{
+ [RegisterPacketProcessor]
+ class BeltUpdatePickupItemsProcessor : IPacketProcessor
+ {
+ public void ProcessPacket(BeltUpdatePickupItemsPacket packet, NebulaConnection conn)
+ {
+ if (GameMain.data.factories[packet.FactoryIndex]?.cargoTraffic != null)
+ {
+ //Iterate though belt updates and remove target items
+ for (int i = 0; i < packet.BeltUpdates.Length; i++)
+ {
+ CargoTraffic traffic = GameMain.data.factories[packet.FactoryIndex].cargoTraffic;
+ CargoPath cargoPath = traffic.GetCargoPath(traffic.beltPool[packet.BeltUpdates[i].BeltId].segPathId);
+ cargoPath.TryPickItem(packet.BeltUpdates[i].SegId - 4 - 1, 12);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NebulaClient/PacketProcessors/Factory/Belt/BeltUpdatePutItemOnProcessor.cs b/NebulaClient/PacketProcessors/Factory/Belt/BeltUpdatePutItemOnProcessor.cs
new file mode 100644
index 000000000..dae5f606e
--- /dev/null
+++ b/NebulaClient/PacketProcessors/Factory/Belt/BeltUpdatePutItemOnProcessor.cs
@@ -0,0 +1,22 @@
+using NebulaModel.Attributes;
+using NebulaModel.Networking;
+using NebulaModel.Packets.Belt;
+using NebulaModel.Packets.Processors;
+using NebulaWorld.Factory;
+
+namespace NebulaClient.PacketProcessors.Factory.Belt
+{
+ [RegisterPacketProcessor]
+ class BeltUpdatePutItemOnProcessor : IPacketProcessor
+ {
+ public void ProcessPacket(BeltUpdatePutItemOnPacket packet, NebulaConnection conn)
+ {
+ if (GameMain.data.factories[packet.FactoryIndex]?.cargoTraffic != null)
+ {
+ FactoryManager.EventFromServer = true;
+ GameMain.data.factories[packet.FactoryIndex].cargoTraffic.PutItemOnBelt(packet.BeltId, packet.ItemId);
+ FactoryManager.EventFromServer = false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NebulaHost/NebulaHost.csproj b/NebulaHost/NebulaHost.csproj
index 99591a1c9..5a761a883 100644
--- a/NebulaHost/NebulaHost.csproj
+++ b/NebulaHost/NebulaHost.csproj
@@ -43,6 +43,8 @@
+
+
diff --git a/NebulaHost/PacketProcessors/Factory/Belt/BeltUpdatePickupItemsProcessor.cs b/NebulaHost/PacketProcessors/Factory/Belt/BeltUpdatePickupItemsProcessor.cs
new file mode 100644
index 000000000..07ee77d89
--- /dev/null
+++ b/NebulaHost/PacketProcessors/Factory/Belt/BeltUpdatePickupItemsProcessor.cs
@@ -0,0 +1,22 @@
+using NebulaModel.Attributes;
+using NebulaModel.Networking;
+using NebulaModel.Packets.Belt;
+using NebulaModel.Packets.Processors;
+
+namespace NebulaHost.PacketProcessors.Factory.Belt
+{
+ [RegisterPacketProcessor]
+ class BeltUpdatePickupItemsProcessor : IPacketProcessor
+ {
+ public void ProcessPacket(BeltUpdatePickupItemsPacket packet, NebulaConnection conn)
+ {
+ //Iterate though belt updates and remove target items
+ for (int i = 0; i < packet.BeltUpdates.Length; i++)
+ {
+ CargoTraffic traffic = GameMain.data.factories[packet.FactoryIndex].cargoTraffic;
+ CargoPath cargoPath = traffic.GetCargoPath(traffic.beltPool[packet.BeltUpdates[i].BeltId].segPathId);
+ cargoPath.TryPickItem(packet.BeltUpdates[i].SegId - 4 - 1, 12);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NebulaHost/PacketProcessors/Factory/Belt/BeltUpdatePutItemOnProcessor.cs b/NebulaHost/PacketProcessors/Factory/Belt/BeltUpdatePutItemOnProcessor.cs
new file mode 100644
index 000000000..d31e7b9a7
--- /dev/null
+++ b/NebulaHost/PacketProcessors/Factory/Belt/BeltUpdatePutItemOnProcessor.cs
@@ -0,0 +1,19 @@
+using NebulaModel.Attributes;
+using NebulaModel.Networking;
+using NebulaModel.Packets.Belt;
+using NebulaModel.Packets.Processors;
+using NebulaWorld.Factory;
+
+namespace NebulaHost.PacketProcessors.Factory.Belt
+{
+ [RegisterPacketProcessor]
+ class BeltUpdatePutItemOnProcessor : IPacketProcessor
+ {
+ public void ProcessPacket(BeltUpdatePutItemOnPacket packet, NebulaConnection conn)
+ {
+ FactoryManager.EventFromClient = true;
+ GameMain.data.factories[packet.FactoryIndex].cargoTraffic.PutItemOnBelt(packet.BeltId, packet.ItemId);
+ FactoryManager.EventFromClient = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/NebulaModel/NebulaModel.csproj b/NebulaModel/NebulaModel.csproj
index f329331e8..68dbaf3bb 100644
--- a/NebulaModel/NebulaModel.csproj
+++ b/NebulaModel/NebulaModel.csproj
@@ -59,6 +59,8 @@
+
+
diff --git a/NebulaModel/Packets/Belt/BeltUpdatePickupItemsPacket.cs b/NebulaModel/Packets/Belt/BeltUpdatePickupItemsPacket.cs
new file mode 100644
index 000000000..2d8874f94
--- /dev/null
+++ b/NebulaModel/Packets/Belt/BeltUpdatePickupItemsPacket.cs
@@ -0,0 +1,51 @@
+using NebulaModel.Attributes;
+using NebulaModel.Networking.Serialization;
+
+namespace NebulaModel.Packets.Belt
+{
+ public class BeltUpdatePickupItemsPacket
+ {
+ public int FactoryIndex { get; set; }
+ public BeltUpdate[] BeltUpdates { get; set; }
+
+ public BeltUpdatePickupItemsPacket() { }
+
+ public BeltUpdatePickupItemsPacket(BeltUpdate[] beltUpdates, int factoryIndex)
+ {
+ BeltUpdates = beltUpdates;
+ FactoryIndex = factoryIndex;
+ }
+ }
+
+ [RegisterNestedType]
+ public struct BeltUpdate : INetSerializable
+ {
+ public int ItemId { get; set; }
+ public int Count { get; set; }
+ public int BeltId { get; set; }
+ public int SegId { get; set; }
+ public BeltUpdate(int itemId, int count, int beltId, int segId)
+ {
+ SegId = segId;
+ ItemId = itemId;
+ Count = count;
+ BeltId = beltId;
+ }
+
+ public void Serialize(NetDataWriter writer)
+ {
+ writer.Put(ItemId);
+ writer.Put(Count);
+ writer.Put(BeltId);
+ writer.Put(SegId);
+ }
+
+ public void Deserialize(NetDataReader reader)
+ {
+ ItemId = reader.GetInt();
+ Count = reader.GetInt();
+ BeltId = reader.GetInt();
+ SegId = reader.GetInt();
+ }
+ }
+}
diff --git a/NebulaModel/Packets/Belt/BeltUpdatePutItemOnPacket.cs b/NebulaModel/Packets/Belt/BeltUpdatePutItemOnPacket.cs
new file mode 100644
index 000000000..e53e31abf
--- /dev/null
+++ b/NebulaModel/Packets/Belt/BeltUpdatePutItemOnPacket.cs
@@ -0,0 +1,16 @@
+namespace NebulaModel.Packets.Belt
+{
+ public class BeltUpdatePutItemOnPacket
+ {
+ public int BeltId { get; set; }
+ public int ItemId { get; set; }
+ public int FactoryIndex { get; set; }
+ public BeltUpdatePutItemOnPacket() { }
+ public BeltUpdatePutItemOnPacket(int beltId, int itemId, int factoryIndex)
+ {
+ BeltId = beltId;
+ ItemId = itemId;
+ FactoryIndex = factoryIndex;
+ }
+ }
+}
diff --git a/NebulaPatcher/NebulaPatcher.csproj b/NebulaPatcher/NebulaPatcher.csproj
index 96a40c5a2..5292edeb3 100644
--- a/NebulaPatcher/NebulaPatcher.csproj
+++ b/NebulaPatcher/NebulaPatcher.csproj
@@ -40,6 +40,7 @@
+
@@ -81,6 +82,7 @@
+
diff --git a/NebulaPatcher/Patches/Dynamic/CargoTraffic_Patch.cs b/NebulaPatcher/Patches/Dynamic/CargoTraffic_Patch.cs
new file mode 100644
index 000000000..569c2b7e6
--- /dev/null
+++ b/NebulaPatcher/Patches/Dynamic/CargoTraffic_Patch.cs
@@ -0,0 +1,41 @@
+using HarmonyLib;
+using NebulaModel.Packets.Belt;
+using NebulaWorld;
+using NebulaWorld.Factory;
+
+namespace NebulaPatcher.Patches.Dynamic
+{
+ [HarmonyPatch(typeof(CargoTraffic))]
+ class CargoTraffic_Patch
+ {
+ [HarmonyPrefix]
+ [HarmonyPatch("PickupBeltItems")]
+ public static void PickupBeltItems_Prefix()
+ {
+ if (SimulatedWorld.Initialized)
+ {
+ BeltManager.BeltPickupStarted();
+ }
+ }
+
+ [HarmonyPostfix]
+ [HarmonyPatch("PickupBeltItems")]
+ public static void PickupBeltItems_Postfix()
+ {
+ if (SimulatedWorld.Initialized)
+ {
+ BeltManager.BeltPickupEnded();
+ }
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch("PutItemOnBelt")]
+ public static void PutItemOnBelt_Prefix(int beltId, int itemId)
+ {
+ if (SimulatedWorld.Initialized && !FactoryManager.EventFromServer && !FactoryManager.EventFromClient)
+ {
+ LocalPlayer.SendPacketToLocalStar(new BeltUpdatePutItemOnPacket(beltId, itemId, GameMain.data.localPlanet.factoryIndex));
+ }
+ }
+ }
+}
diff --git a/NebulaPatcher/Patches/Transpilers/CargoTraffic_Patch.cs b/NebulaPatcher/Patches/Transpilers/CargoTraffic_Patch.cs
new file mode 100644
index 000000000..dce7379c1
--- /dev/null
+++ b/NebulaPatcher/Patches/Transpilers/CargoTraffic_Patch.cs
@@ -0,0 +1,51 @@
+using HarmonyLib;
+using NebulaWorld.Factory;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+
+namespace NebulaPatcher.Patches.Transpiler
+{
+ /* Change this:
+ if (num3 > 0)
+ {
+ UIItemup.Up(itemId, num3);
+ }
+
+ * To this:
+ if (num3 > 0)
+ {
+ BeltManager.RegisterBeltPickupUpdate(itemId, count, beltId, segId);
+ UIItemup.Up(itemId, num3);
+ }
+ */
+ [HarmonyPatch(typeof(CargoTraffic))]
+ class CargoTraffic_Patch
+ {
+ [HarmonyTranspiler]
+ [HarmonyPatch("PickupBeltItems")]
+ static IEnumerable PickupBeltItems_Transpiler(ILGenerator gen, IEnumerable instructions)
+ {
+ var codes = new List(instructions);
+ for (int i = 0; i < codes.Count; i++)
+ {
+ if (codes[i].opcode == OpCodes.Ble &&
+ codes[i - 1].opcode == OpCodes.Ldc_I4_0 &&
+ codes[i - 2].opcode == OpCodes.Ldloc_S &&
+ codes[i - 3].opcode == OpCodes.Stloc_S &&
+ codes[i - 4].opcode == OpCodes.Callvirt &&
+ codes[i - 5].opcode == OpCodes.Ldfld)
+ {
+ codes.InsertRange(i + 1, new CodeInstruction[] {
+ new CodeInstruction(OpCodes.Ldloc_S, 4),
+ new CodeInstruction(OpCodes.Ldloc_S, 5),
+ new CodeInstruction(OpCodes.Ldarg_2),
+ new CodeInstruction(OpCodes.Ldloc_3),
+ new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(BeltManager), "RegisterBeltPickupUpdate", new System.Type[] { typeof(int), typeof(int), typeof(int), typeof(int)})),
+ });
+ break;
+ }
+ }
+ return codes;
+ }
+ }
+}
diff --git a/NebulaWorld/Factory/BeltManager.cs b/NebulaWorld/Factory/BeltManager.cs
new file mode 100644
index 000000000..c4a13bf9c
--- /dev/null
+++ b/NebulaWorld/Factory/BeltManager.cs
@@ -0,0 +1,26 @@
+using NebulaModel.Packets.Belt;
+using System.Collections.Generic;
+
+namespace NebulaWorld.Factory
+{
+ public static class BeltManager
+ {
+ public static List BeltUpdates = new List();
+ public static void BeltPickupStarted()
+ {
+ BeltUpdates.Clear();
+ }
+ public static void RegisterBeltPickupUpdate(int itemId, int count, int beltId, int segId)
+ {
+ if (SimulatedWorld.Initialized)
+ {
+ BeltUpdates.Add(new BeltUpdate(itemId, count, beltId, segId));
+ }
+ }
+ public static void BeltPickupEnded()
+ {
+ LocalPlayer.SendPacketToLocalStar(new BeltUpdatePickupItemsPacket(BeltUpdates.ToArray(), GameMain.data.localPlanet.factoryIndex));
+ BeltUpdates.Clear();
+ }
+ }
+}
diff --git a/NebulaWorld/NebulaWorld.csproj b/NebulaWorld/NebulaWorld.csproj
index 2d11596d0..8a8755e24 100644
--- a/NebulaWorld/NebulaWorld.csproj
+++ b/NebulaWorld/NebulaWorld.csproj
@@ -39,6 +39,7 @@
+