1
0
mirror of https://github.com/soarqin/DSP_Mods.git synced 2025-12-09 08:13:35 +08:00

WIP for logistic_hub

This commit is contained in:
2024-12-02 21:55:35 +08:00
parent be9de43492
commit 6934607fca
14 changed files with 927 additions and 557 deletions

View File

@@ -0,0 +1,33 @@
using System.Linq;
namespace LogisticHub.Module;
using UXAssist.Common;
public static class AuxData
{
public static (long, bool)[] Fuels;
public static void Init()
{
GameLogic.OnDataLoaded += () =>
{
var maxId = LDB.items.dataArray.Select(data => data.ID).Prepend(0).Max();
Fuels = new (long, bool)[maxId + 1];
foreach (var data in LDB.items.dataArray)
Fuels[data.ID] = (data.HeatValue, data.Productive);
};
}
public static int AlignUpToPowerOf2(int n)
{
if (n < 16) return 16;
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
}

View File

338
LogisticHub/Module/Miner.cs Normal file
View File

@@ -0,0 +1,338 @@
using System;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UXAssist.Common;
using Random = UnityEngine.Random;
namespace LogisticHub.Module;
public class Miner : PatchImpl<Miner>
{
public static ConfigEntry<bool> Enabled;
public static ConfigEntry<long> OreEnergyConsume;
public static ConfigEntry<long> OilEnergyConsume;
public static ConfigEntry<long> WaterEnergyConsume;
public static ConfigEntry<int> WaterSpeed;
public static ConfigEntry<int> MiningScale;
public static ConfigEntry<int> FuelIlsSlot;
public static ConfigEntry<int> FuelPlsSlot;
private static float _frame;
private static float _miningCostRateByTech;
private static float _miningSpeedScaleByTech;
private static float _miningFrames;
private static long _miningSpeedScaleLong;
private static bool _advancedMiningMachineUnlocked;
private static uint _miningCostBarrier;
private static uint _miningCostBarrierOil;
private static int[] _mineIndex;
private static uint _miningSeed = (uint)Random.Range(0, int.MaxValue);
private static readonly (int, int)[] VeinList =
[
(0, 1000),
(1, 1001),
(2, 1002),
(3, 1003),
(4, 1004),
(5, 1005),
(6, 1006),
(7, 1007),
(11, 1011),
(12, 1012),
(13, 1013),
(14, 1014),
(15, 1015),
(16, 1016)
];
public static void Init()
{
Enabled.SettingChanged += (_, _) => { Enable(Enabled.Value); };
Enable(Enabled.Value);
}
public static void Uninit()
{
Enable(false);
}
protected override void OnEnable()
{
GameLogic.OnGameBegin += OnGameBegin;
}
protected override void OnDisable()
{
GameLogic.OnGameBegin -= OnGameBegin;
}
private static void OnGameBegin()
{
VeinManager.Clear();
_frame = 0f;
UpdateMiningCostRate();
UpdateSpeedScale();
CheckRecipes();
}
private static int SplitIncLevel(ref int n, ref int m, int p)
{
var level = m / n;
var left = m - level * n;
n -= p;
left -= n;
m -= left > 0 ? level * p + left : level * p;
return level;
}
private static void CheckRecipes()
{
_advancedMiningMachineUnlocked = GameMain.history.recipeUnlocked.Contains(119);
}
private static void UpdateMiningCostRate()
{
_miningCostRateByTech = GameMain.history.miningCostRate;
_miningCostBarrier = (uint)(int)Math.Ceiling(2147483646.0 * _miningCostRateByTech);
_miningCostBarrierOil = (uint)(int)Math.Ceiling(2147483646.0 * _miningCostRateByTech * 0.401116669f / Math.Max(DSPGame.GameDesc.resourceMultiplier, 0.416666657f));
}
private static void UpdateSpeedScale()
{
_miningSpeedScaleByTech = GameMain.history.miningSpeedScale;
_miningSpeedScaleLong = (long)(_miningSpeedScaleByTech * 100);
_miningFrames = _miningSpeedScaleByTech * 600000f;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameHistoryData), nameof(GameHistoryData.UnlockTechFunction))]
private static void OnUnlockTech(int func)
{
switch (func)
{
case 20:
UpdateMiningCostRate();
break;
case 21:
UpdateSpeedScale();
break;
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(GameData), "GameTick")]
private static void GameData_GameTick_Prefix()
{
var main = GameMain.instance;
if (main.isMenuDemo) return;
if (_miningSpeedScaleLong <= 0) return;
PerformanceMonitor.BeginSample(ECpuWorkEntry.Miner);
if (main.timei % 60 != 0) return;
_frame += _miningFrames;
var frameCounter = Mathf.FloorToInt(_frame / 1200000f);
if (frameCounter <= 0) return;
_frame -= frameCounter * 1200000f;
// LogisticHub.Logger.LogDebug($"FrameCounter: {frameCounter}");
var data = GameMain.data;
for (var factoryIndex = data.factoryCount - 1; factoryIndex >= 0; factoryIndex--)
{
var factory = data.factories[factoryIndex];
var veins = VeinManager.GetVeins(factoryIndex);
if (veins == null) return;
var stations = StationManager.GetStations(factoryIndex);
var planetTransport = factory.transport;
var factoryProductionStat = GameMain.statistics.production.factoryStatPool[factoryIndex];
var productRegister = factoryProductionStat?.productRegister;
var demands = stations.StorageIndices[1];
if (_mineIndex == null || factoryIndex >= _mineIndex.Length)
Array.Resize(ref _mineIndex, factoryIndex + 1);
foreach (var (itemIndex, itemId) in VeinList)
{
foreach (var storageIndex in demands[itemIndex])
{
var station = planetTransport.stationPool[storageIndex / 100];
if (station == null)
continue;
ref var storage = ref station.storage[storageIndex % 100];
int amount;
long energyConsume;
var miningScale = MiningScale.Value;
if (miningScale == 0)
{
miningScale = _advancedMiningMachineUnlocked ? 300 : 100;
}
if (miningScale > 100 && storage.count * 2 > storage.max)
{
miningScale = 100 + ((miningScale - 100) * (storage.max - storage.count) * 2 + storage.max - 1) / storage.max;
}
if (itemIndex > 0)
{
(amount, energyConsume) = Mine(factory, veins, itemId, miningScale, frameCounter, station.energy);
if (amount < 0) continue;
}
else
{
energyConsume = (WaterEnergyConsume.Value * frameCounter * miningScale * miningScale + 9999L) / 10000L;
if (station.energy < energyConsume) continue;
amount = WaterSpeed.Value * miningScale / 100;
}
if (amount <= 0) continue;
storage.count += amount;
if (factoryProductionStat != null)
productRegister[itemId] += amount;
station.energy -= energyConsume;
}
}
for (var i = planetTransport.stationCursor - 1; i > 0; i--)
{
var stationComponent = planetTransport.stationPool[i];
if (stationComponent.isCollector || stationComponent.isVeinCollector || stationComponent.energy * 2 >= stationComponent.energyMax) continue;
var index = (stationComponent.isStellar ? FuelIlsSlot.Value : FuelPlsSlot.Value) - 1;
var storage = stationComponent.storage;
if (index < 0 || index >= storage.Length)
continue;
var fuelCount = storage[index].count;
if (fuelCount == 0) continue;
var (heat, prod) = AuxData.Fuels[storage[index].itemId];
if (heat <= 0)
continue;
/* Sprayed fuels */
int pretendIncLevel;
if (prod && (pretendIncLevel = storage[index].inc / storage[index].count) > 0)
{
var count = (int)((stationComponent.energyMax - stationComponent.energy) * 1000L / Cargo.incTable[pretendIncLevel] / 7L);
if (count > fuelCount)
count = fuelCount;
var incLevel = SplitIncLevel(ref storage[index].count, ref storage[index].inc, count);
if (incLevel > 10)
incLevel = 10;
stationComponent.energy += heat * count * (1000L + Cargo.incTable[incLevel]) / 1000L;
}
else
{
var count = (int)((stationComponent.energyMax - stationComponent.energy) / heat);
if (count > fuelCount)
count = fuelCount;
SplitIncLevel(ref storage[index].count, ref storage[index].inc, count);
stationComponent.energy += heat * count;
}
}
}
PerformanceMonitor.EndSample(ECpuWorkEntry.Miner);
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameHistoryData), nameof(GameHistoryData.UnlockRecipe))]
private static void OnUnlockRecipe(int recipeId)
{
if (recipeId == 119)
{
CheckRecipes();
}
}
private static (int, long) Mine(PlanetFactory factory, ProductVeinData[] allVeins, int productId, int percent, int counter, long energyMax)
{
var veins = allVeins[productId - 1000];
if (veins == null)
return (-1, -1L);
var veinIndices = veins.VeinIndices;
uint barrier;
int limit;
int count;
long energy;
var length = veinIndices.Count - 1;
/* if is Oil */
if (productId == 1007)
{
energy = (OilEnergyConsume.Value * length * percent * percent + 9999L) / 10000L;
if (energy > energyMax)
return (-1, -1L);
var countf = 0f;
var veinsPool = factory.veinPool;
for (var i = length; i > 0; i--)
{
countf += veinsPool[veinIndices[i]].amount * 4 * VeinData.oilSpeedMultiplier;
}
count = ((int)countf * counter * percent + 99) / 100;
if (count == 0)
return (-1, -1L);
barrier = _miningCostBarrierOil;
limit = 2500;
}
else
{
count = (length * counter * percent + 99) / 100;
if (count == 0)
return (-1, -1L);
energy = (OreEnergyConsume.Value * veins.GroupCount * percent * percent + 9999L) / 10000L;
if (energy > energyMax)
return (-1, -1L);
barrier = _miningCostBarrier;
limit = 0;
}
var veinsData = factory.veinPool;
var total = 0;
var factoryIndex = factory.index;
var mineIndex = _mineIndex[factoryIndex];
for (; count > 0; count--)
{
mineIndex = mineIndex % length + 1;
var index = veinIndices[mineIndex];
ref var vd = ref veinsData[index];
int groupIndex;
if (vd.amount <= 0)
{
groupIndex = vd.groupIndex;
factory.veinGroups[groupIndex].count--;
factory.RemoveVeinWithComponents(index);
factory.RecalculateVeinGroup(groupIndex);
length = veinIndices.Count - 1;
if (length <= 0) break;
continue;
}
total++;
if (vd.amount <= limit) continue;
var consume = true;
if (barrier < 2147483646u)
{
_miningSeed = (uint)((int)((ulong)((_miningSeed % 2147483646u + 1) * 48271L) % 2147483647uL) - 1);
consume = _miningSeed < barrier;
}
if (!consume) continue;
vd.amount--;
groupIndex = vd.groupIndex;
factory.veinGroups[groupIndex].amount--;
if (vd.amount > 0) continue;
factory.veinGroups[groupIndex].count--;
factory.RemoveVeinWithComponents(index);
factory.RecalculateVeinGroup(groupIndex);
length = veinIndices.Count - 1;
if (length <= 0) break;
}
_mineIndex[factoryIndex] = mineIndex;
return (total, energy);
}
}

View File

@@ -0,0 +1,321 @@
using System;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using UXAssist.Common;
namespace LogisticHub.Module;
public class PlanetStations
{
public readonly List<int>[][] StorageIndices = [null, null];
public void AddStationStorage(bool demand, int id, int stationId, int storageIdx)
{
var stations = StorageIndices[demand ? 1 : 0];
if (stations == null || id >= stations.Length)
{
Array.Resize(ref stations, AuxData.AlignUpToPowerOf2(id + 1));
StorageIndices[demand ? 1 : 0] = stations;
}
var list = stations[id];
if (list == null)
{
list = [];
stations[id] = list;
}
var value = stationId * 100 + storageIdx;
var index = list.BinarySearch(value);
if (index < 0)
list.Insert(~index, value);
}
public void RemoveStationStorage(bool demand, int id, int stationId, int storageIdx)
{
var stations = StorageIndices[demand ? 1 : 0];
if (stations == null || id >= stations.Length)
return;
var list = stations[id];
if (list == null)
return;
var value = stationId * 100 + storageIdx;
var index = list.BinarySearch(value);
if (index >= 0)
list.RemoveAt(index);
}
}
public class StationManager : PatchImpl<StationManager>
{
private static PlanetStations[] _stations;
public static void Init()
{
GameLogic.OnGameBegin += () =>
{
_stations = null;
var data = GameMain.data;
for (var index = data.factoryCount - 1; index >= 0; index--)
{
var factory = data.factories[index];
if (factory == null || factory.index != index) continue;
var planetIndex = factory.index;
var stations = StationsByPlanet(planetIndex);
var transport = factory.transport;
var pool = transport.stationPool;
for (var i = transport.stationCursor - 1; i > 0; i--)
{
var station = pool[i];
if (station == null || station.id != i || station.isCollector || station.isVeinCollector) continue;
UpdateStationInfo(stations, station);
}
}
};
Enable(true);
}
public static void Uninit()
{
Enable(false);
}
private static int ItemIdToIndex(int itemId)
{
return itemId switch
{
>= 1000 and < 2000 => itemId - 1000,
>= 5000 and < 5050 => itemId - 5000 + 900,
>= 6000 and < 6050 => itemId - 6000 + 950,
_ => -1
};
}
public static int IndexToItemId(int index)
{
return index switch
{
< 900 => index + 1000,
< 950 => index - 900 + 5000,
< 1000 => index - 950 + 6000,
_ => -1
};
}
public static PlanetStations GetStations(int planetIndex)
{
return _stations != null && planetIndex < _stations.Length ? _stations[planetIndex] : null;
}
private static PlanetStations StationsByPlanet(int planetIndex)
{
if (_stations == null || _stations.Length <= planetIndex)
Array.Resize(ref _stations, AuxData.AlignUpToPowerOf2(planetIndex + 1));
var stations = _stations[planetIndex];
if (stations != null) return stations;
stations = new PlanetStations();
_stations[planetIndex] = stations;
return stations;
}
private static void DebugLog()
{
for (var idx = 0; idx < _stations.Length; idx++)
{
var stations = _stations[idx];
if (stations == null) continue;
LogisticHub.Logger.LogDebug($"Planet {idx}:");
for (var i = 0; i < 2; i++)
{
var storage = stations.StorageIndices[i];
if (storage == null) continue;
LogisticHub.Logger.LogDebug(i == 1 ? " Demand:" : " Supply:");
for (var j = 0; j < storage.Length; j++)
{
var list = storage[j];
if (list == null) continue;
var count = list.Count;
if (count <= 0) continue;
var itemId = IndexToItemId(j);
LogisticHub.Logger.LogDebug($" {itemId}: {string.Join(", ", list)}");
}
}
}
}
private static void UpdateStationInfo(PlanetStations stations, StationComponent station, int storageIdx = -1)
{
var storage = station.storage;
var stationId = station.id;
if (storageIdx >= 0)
{
if (storageIdx >= storage.Length) return;
var itemId = ItemIdToIndex(storage[storageIdx].itemId);
if (itemId <= 0) return;
var logic = storage[storageIdx].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.AddStationStorage(true, itemId, stationId, storageIdx);
break;
case ELogisticStorage.Supply:
stations.AddStationStorage(false, itemId, stationId, storageIdx);
break;
case ELogisticStorage.None:
default:
break;
}
return;
}
for (var i = storage.Length - 1; i >= 0; i--)
{
var itemId = ItemIdToIndex(storage[i].itemId);
if (itemId <= 0) continue;
var logic = storage[i].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.AddStationStorage(true, itemId, stationId, i);
break;
case ELogisticStorage.Supply:
stations.AddStationStorage(false, itemId, stationId, i);
break;
case ELogisticStorage.None:
default:
break;
}
}
}
private static void RemoveStationInfo(PlanetStations stations, StationComponent station, int storageIdx = -1)
{
var storage = station.storage;
var stationId = station.id;
if (storageIdx >= 0)
{
var itemId = ItemIdToIndex(storage[storageIdx].itemId);
if (itemId <= 0) return;
var logic = storage[storageIdx].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.RemoveStationStorage(true, itemId, stationId, storageIdx);
break;
case ELogisticStorage.Supply:
stations.RemoveStationStorage(false, itemId, stationId, storageIdx);
break;
case ELogisticStorage.None:
default:
break;
}
return;
}
for (var i = storage.Length - 1; i >= 0; i--)
{
var itemId = ItemIdToIndex(storage[i].itemId);
if (itemId <= 0) continue;
var logic = storage[i].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.RemoveStationStorage(true, itemId, stationId, i);
break;
case ELogisticStorage.Supply:
stations.RemoveStationStorage(false, itemId, stationId, i);
break;
case ELogisticStorage.None:
default:
break;
}
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.Init))]
private static void PlanetTransport_Init_Postfix(PlanetTransport __instance)
{
var factory = __instance.factory;
var planetIndex = factory.index;
if (_stations == null || _stations.Length <= planetIndex)
Array.Resize(ref _stations, AuxData.AlignUpToPowerOf2(planetIndex + 1));
var stations = new PlanetStations();
_stations[planetIndex] = stations;
var pool = __instance.stationPool;
for (var i = __instance.stationCursor - 1; i > 0; i--)
{
var station = pool[i];
if (station == null || station.id != i || station.isCollector || station.isVeinCollector) continue;
UpdateStationInfo(stations, station);
}
// DebugLog();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(BuildingParameters), nameof(BuildingParameters.ApplyPrebuildParametersToEntity))]
private static void BuildingParameters_ApplyPrebuildParametersToEntity_Postfix(int entityId, PlanetFactory factory)
{
if (entityId <= 0) return;
ref var entity = ref factory.entityPool[entityId];
var stationId = entity.stationId;
if (stationId <= 0) return;
var station = factory.transport.stationPool[stationId];
if (station == null || station.id != stationId || station.isCollector || station.isVeinCollector) return;
UpdateStationInfo(StationsByPlanet(factory.index), station);
// DebugLog();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.RemoveStationComponent))]
private static void PlanetTransport_RemoveStationComponent_Prefix(PlanetTransport __instance, int id)
{
if (id <= 0) return;
var station = __instance.stationPool[id];
if (station == null || station.id != id || station.isCollector || station.isVeinCollector) return;
RemoveStationInfo(StationsByPlanet(__instance.factory.index), station);
// DebugLog();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.SetStationStorage))]
private static void PlanetTransport_SetStationStorage_Prefix(PlanetTransport __instance, int stationId, int storageIdx, int itemId, ELogisticStorage localLogic, out bool __state)
{
var station = __instance.stationPool[stationId];
if (station == null || station.id != stationId || station.isCollector || station.isVeinCollector || storageIdx < 0 || storageIdx >= station.storage.Length)
{
__state = false;
return;
}
ref var storage = ref station.storage[storageIdx];
var oldItemId = storage.itemId;
var oldLocalLogic = storage.localLogic;
if (localLogic == oldLocalLogic && itemId == oldItemId)
{
__state = false;
return;
}
if (oldItemId > 0 && oldLocalLogic != ELogisticStorage.None)
RemoveStationInfo(StationsByPlanet(__instance.factory.index), station, storageIdx);
__state = localLogic != ELogisticStorage.None;
// if (!__state) DebugLog();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.SetStationStorage))]
private static void PlanetTransport_SetStationStorage_Postfix(PlanetTransport __instance, int stationId, int storageIdx, bool __state)
{
if (!__state) return;
var station = __instance.stationPool[stationId];
UpdateStationInfo(StationsByPlanet(__instance.factory.index), station, storageIdx);
// DebugLog();
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using UXAssist.Common;
namespace LogisticHub.Module;
public class ProductVeinData
{
public readonly List<int> VeinIndices = [];
public readonly HashSet<int> GroupIndices = [];
public int GroupCount = 0;
}
public class VeinManager : PatchImpl<VeinManager>
{
private static ProductVeinData[][] _veins;
public static void Init()
{
Enable(true);
}
public static void Uninit()
{
Enable(false);
}
public static void Clear()
{
_veins = null;
}
public static ProductVeinData[] GetVeins(int planetIndex)
{
if (_veins == null || _veins.Length <= planetIndex)
return null;
return _veins[planetIndex];
}
private static ProductVeinData[] GetOrCreateVeins(int planetIndex)
{
if (_veins == null || _veins.Length <= planetIndex)
Array.Resize(ref _veins, AuxData.AlignUpToPowerOf2(planetIndex + 1));
var veins = _veins[planetIndex];
if (veins != null) return veins;
veins = new ProductVeinData[20];
_veins[planetIndex] = veins;
return veins;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.Init))]
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.RecalculateVeinGroup))]
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.RecalculateAllVeinGroups))]
private static void PlanetFactory_RecalculateAllVeinGroups_Postfix(PlanetFactory __instance)
{
RecalcVeins(__instance);
}
private static void DebugLog()
{
foreach (var veins in _veins)
{
if (veins == null) continue;
for (var i = 0; i < veins.Length; i++)
{
var pvd = veins[i];
if (pvd == null) continue;
LogisticHub.Logger.LogInfo($"Product {i} VeinTypeCount={pvd.VeinIndices.Count} GroupCount={pvd.GroupCount}");
}
}
}
private static void RecalcVeins(PlanetFactory factory)
{
var planetIndex = factory.index;
var veins = GetOrCreateVeins(planetIndex);
var veinPool = factory.veinPool;
foreach (var pvd in veins)
{
if (pvd == null) continue;
pvd.VeinIndices.Clear();
pvd.GroupIndices.Clear();
pvd.GroupCount = 0;
}
for (var i = factory.veinCursor - 1; i > 0; i--)
{
if (veinPool[i].id != i || veinPool[i].amount <= 0 || veinPool[i].type == EVeinType.None) continue;
var productId = veinPool[i].productId - 1000;
if (productId is < 0 or >= 20) continue;
var pvd = veins[productId];
if (pvd == null)
{
pvd = new ProductVeinData();
veins[productId] = pvd;
}
pvd.VeinIndices.Add(i);
pvd.GroupIndices.Add(veinPool[i].groupIndex);
}
foreach (var pvd in veins)
{
if (pvd == null) continue;
pvd.GroupCount = pvd.GroupIndices.Count;
}
// DebugLog();
}
}