diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0130d6e..8c826fc92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ ## Changelog + 0.9.8: - @AlienXAXS: Added Online Player UI (Backtick key by default) - @AlienXAXS: Updated Nebula to be compatible with Dyson Sphere Program v0.10.30.23292 +- @starfi5h: Temporarily disable Logistics Control Panel (I) interactions +- @starfi5h: Fix a bug that battle notification toggle in multiplayer chat settings has no effect 0.9.7: - @AlienXAXS: Headless now calculates planetary shields on CPU diff --git a/NebulaModel/Utils/ChatUtils.cs b/NebulaModel/Utils/ChatUtils.cs index 69f8b8f3d..70b76183e 100644 --- a/NebulaModel/Utils/ChatUtils.cs +++ b/NebulaModel/Utils/ChatUtils.cs @@ -87,41 +87,23 @@ public static string SanitizeText(string input) public static Color GetMessageColor(ChatMessageType messageType) { - switch (messageType) + return messageType switch { - case ChatMessageType.PlayerMessage: - return Color.white; - - case ChatMessageType.SystemInfoMessage: - return Color.cyan; - - case ChatMessageType.SystemWarnMessage: - return new Color(1, 0.95f, 0, 1); - - case ChatMessageType.BattleMessage: - return Color.cyan; - - case ChatMessageType.CommandUsageMessage: - return new Color(1, 0.65f, 0, 1); - - case ChatMessageType.CommandOutputMessage: - return new Color(0.8f, 0.8f, 0.8f, 1); - - case ChatMessageType.CommandErrorMessage: - return Color.red; - - case ChatMessageType.PlayerMessagePrivate: - return Color.green; - - default: - Console.WriteLine($"Requested color for unexpected chat message type {messageType}"); - return Color.white; - } + ChatMessageType.PlayerMessage => Color.white, + ChatMessageType.SystemInfoMessage => Color.cyan, + ChatMessageType.SystemWarnMessage => new Color(1, 0.95f, 0, 1), + ChatMessageType.BattleMessage => Color.cyan, + ChatMessageType.CommandUsageMessage => new Color(1, 0.65f, 0, 1), + ChatMessageType.CommandOutputMessage => new Color(0.8f, 0.8f, 0.8f, 1), + ChatMessageType.CommandErrorMessage => Color.red, + ChatMessageType.PlayerMessagePrivate => Color.green, + _ => Color.white, // Default chat color is white + }; } - public static bool IsCommandMessage(this ChatMessageType type) + public static bool IsPlayerMessage(this ChatMessageType type) { - return type is ChatMessageType.CommandOutputMessage or ChatMessageType.CommandUsageMessage or ChatMessageType.CommandErrorMessage or ChatMessageType.SystemWarnMessage or ChatMessageType.SystemInfoMessage; + return type is ChatMessageType.PlayerMessage or ChatMessageType.PlayerMessagePrivate; } public static bool Contains(this string source, string toCheck, StringComparison comp) diff --git a/NebulaNetwork/PacketProcessors/Combat/DFRelay/DFRelayArriveBaseProcessor.cs b/NebulaNetwork/PacketProcessors/Combat/DFRelay/DFRelayArriveBaseProcessor.cs index 6d176a79c..dd312632a 100644 --- a/NebulaNetwork/PacketProcessors/Combat/DFRelay/DFRelayArriveBaseProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Combat/DFRelay/DFRelayArriveBaseProcessor.cs @@ -1,6 +1,5 @@ #region -using NebulaAPI.DataStructures; using NebulaAPI.Packets; using NebulaModel.Networking; using NebulaModel.Packets; @@ -45,7 +44,7 @@ protected override void ProcessPacket(DFRelayArriveBasePacket packet, NebulaConn dfrelayComponent.ArriveBase(); // Display message when DF relay is successfully landed and the planet has buildings - if (packet.HasFactory && dfrelayComponent.baseId > 0) + if (packet.HasFactory) { Multiplayer.Session.Enemies.DisplayPlanetPingMessage("DF relay landed on planet".Translate(), dfrelayComponent.targetAstroId, dfrelayComponent.targetLPos); } diff --git a/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGFormationAddUnitProcessor.cs b/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGFormationAddUnitProcessor.cs index 2123f0a64..2fbfaf687 100644 --- a/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGFormationAddUnitProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGFormationAddUnitProcessor.cs @@ -1,6 +1,7 @@ #region using NebulaAPI.Packets; +using NebulaModel.Logger; using NebulaModel.Networking; using NebulaModel.Packets; using NebulaModel.Packets.Combat.GroundEnemy; @@ -25,6 +26,13 @@ protected override void ProcessPacket(DFGFormationAddUnitPacket packet, NebulaCo { // Set the next id in EnemyFormation var enemyFrom = dFBase.forms[packet.FormId]; + + if (enemyFrom.vacancyCursor <= 0) + { + // If vacancyCursor desync, abort the AddUnit action + Log.Warn($"DFGFormationAddUnitPacket vacancyCursor desync. Pid={packet.PlanetId} Base={packet.BaseId} FormId={packet.FormId}"); + return; + } enemyFrom.vacancies[enemyFrom.vacancyCursor - 1] = packet.PortId; var portId = enemyFrom.AddUnit(); diff --git a/NebulaNetwork/PacketProcessors/Combat/SpaceEnemy/DFSFormationAddUnitProcessor.cs b/NebulaNetwork/PacketProcessors/Combat/SpaceEnemy/DFSFormationAddUnitProcessor.cs index fdf21f147..bb972703d 100644 --- a/NebulaNetwork/PacketProcessors/Combat/SpaceEnemy/DFSFormationAddUnitProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Combat/SpaceEnemy/DFSFormationAddUnitProcessor.cs @@ -1,6 +1,7 @@ #region using NebulaAPI.Packets; +using NebulaModel.Logger; using NebulaModel.Networking; using NebulaModel.Packets; using NebulaModel.Packets.Combat.SpaceEnemy; @@ -8,7 +9,7 @@ #endregion -namespace NebulaNetwork.PacketProcessors.Combat.GroundEnemy; +namespace NebulaNetwork.PacketProcessors.Combat.SpaceEnemy; [RegisterPacketProcessor] public class DFSFormationAddUnitProcessor : PacketProcessor @@ -22,6 +23,13 @@ protected override void ProcessPacket(DFSFormationAddUnitPacket packet, NebulaCo { // Set the next id in EnemyFormation var enemyFrom = hive.forms[packet.FormId]; + + if (enemyFrom.vacancyCursor <= 0) + { + // If vacancyCursor desync, abort the AddUnit action + Log.Warn($"DFSFormationAddUnitPacket vacancyCursor desync. HiveAstroId={packet.HiveAstroId} FormId={packet.FormId}"); + return; + } enemyFrom.vacancies[enemyFrom.vacancyCursor - 1] = packet.PortId; var portId = enemyFrom.AddUnit(); diff --git a/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs b/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs index 7bde91127..9dd2c6708 100644 --- a/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs @@ -33,6 +33,16 @@ namespace NebulaPatcher.Patches.Dynamic; [HarmonyPatch(typeof(PlanetFactory))] internal class PlanetFactory_patch { + [HarmonyPrefix] + [HarmonyPatch(nameof(PlanetFactory.FlushPools))] + public static bool FlushPools_Prefix() + { + // In vanilla, FlushPools is triggered by unload planet event which will happen at different time for each player + // So this optimize pool function is disabled in multiplayer to keep pool in sync + return !Multiplayer.IsActive; + } + + [HarmonyPostfix] [HarmonyPatch(nameof(PlanetFactory.AddPrebuildData))] public static void AddPrebuildData_Postfix(PlanetFactory __instance, ref int __result) diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelAdvancedMinerEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelAdvancedMinerEntry_Patch.cs new file mode 100644 index 000000000..f5931a3f2 --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelAdvancedMinerEntry_Patch.cs @@ -0,0 +1,22 @@ +#region + +using HarmonyLib; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(UIControlPanelAdvancedMinerEntry))] +internal class UIControlPanelAdvancedMinerEntry_Patch +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelAdvancedMinerEntry.OnFillNecessaryButtonClick))] + public static bool OnFillNecessaryButtonClick_Prefix() + { + if (!Multiplayer.IsActive) return true; + + // Temporarily disable fill item button. We will sync in the future + return false; + } +} diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelDispenserEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelDispenserEntry_Patch.cs new file mode 100644 index 000000000..ab272fb0f --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelDispenserEntry_Patch.cs @@ -0,0 +1,22 @@ +#region + +using HarmonyLib; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(UIControlPanelDispenserEntry))] +internal class UIControlPanelDispenserEntry_Patch +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelDispenserEntry.OnFillNecessaryButtonClick))] + public static bool OnFillNecessaryButtonClick_Prefix(UIControlPanelDispenserEntry __instance) + { + if (!Multiplayer.IsActive) return true; + + // Temporarily disable fill item button. We will sync in the future + return false; + } +} diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelStationEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelStationEntry_Patch.cs new file mode 100644 index 000000000..64d7ee990 --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelStationEntry_Patch.cs @@ -0,0 +1,22 @@ +#region + +using HarmonyLib; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(UIControlPanelStationEntry))] +internal class UIControlPanelStationEntry_Patch +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelStationEntry.OnFillNecessaryButtonClick))] + public static bool OnFillNecessaryButtonClick_Prefix() + { + if (!Multiplayer.IsActive) return true; + + // Temporarily disable fill item button. We will sync in the future + return false; + } +} diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelStationInspector_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelStationInspector_Patch.cs new file mode 100644 index 000000000..14ac5256d --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelStationInspector_Patch.cs @@ -0,0 +1,22 @@ +#region + +using HarmonyLib; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(UIControlPanelStationInspector))] +internal class UIControlPanelStationInspector_Patch +{ + [HarmonyPostfix, HarmonyPriority(Priority.Last)] + [HarmonyPatch(nameof(UIControlPanelStationInspector._OnOpen))] + public static void OnOpen_Postfix(UIControlPanelStationInspector __instance) + { + if (!Multiplayer.IsActive) return; + + // Temporarily disable the station window, as we need to deal with remote station and sync in the future + __instance._Close(); + } +} diff --git a/NebulaPatcher/Patches/Transpilers/UIStatisticsWindow_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/UIStatisticsWindow_Transpiler.cs index ed466cfab..de96d455c 100644 --- a/NebulaPatcher/Patches/Transpilers/UIStatisticsWindow_Transpiler.cs +++ b/NebulaPatcher/Patches/Transpilers/UIStatisticsWindow_Transpiler.cs @@ -16,57 +16,29 @@ namespace NebulaPatcher.Patches.Transpilers; [HarmonyPatch(typeof(UIStatisticsWindow))] public static class UIStatisticsWindow_Transpiler { - - /* - * AlienX: After looking at the code which v0.10.30.23292 has (this method changed), it would seem that: - * - It creates two lists private in scope to the method: items and itemsData - * - Populates these lists - * - Does nothing with them - * - * Is this method completely redundant now??? - [HarmonyTranspiler] [HarmonyPatch(nameof(UIStatisticsWindow.RefreshAstroBox))] private static IEnumerable RefreshAstroBox_Transpiler(IEnumerable instructions) { - //Change: this.gameData.factoryCount - //To: GetFactoryCount() - //Change: this.gameData.factories[i].planetId - //To: GetPlanetData(i).id - //Change: this.gameData.factories[i].planet - //To: GetPlanetData(i) + // Update the drop list, so client is able to select the planets that are not loaded + // Change: if (planetData.factory != null) + // To : if (HasFactory(planetData)) + var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); try { - instructions = ReplaceFactoryCount(codeInstructions); - - var matcher = new CodeMatcher(instructions) - .MatchForward(false, - new CodeMatch(OpCodes.Callvirt, - AccessTools.DeclaredPropertyGetter(typeof(PlanetFactory), nameof(PlanetFactory.planetId))) - ) - .Advance(-5); - var factoryIndexOp = matcher.InstructionAt(3).opcode; - matcher.SetAndAdvance(factoryIndexOp, null) - .InsertAndAdvance( - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(UIStatisticsWindow_Transpiler), nameof(GetPlanetData))), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PlanetData), nameof(PlanetData.id))) - ) - .RemoveInstructions(5) + return new CodeMatcher(instructions) .MatchForward(false, - new CodeMatch(OpCodes.Callvirt, - AccessTools.DeclaredPropertyGetter(typeof(PlanetFactory), nameof(PlanetFactory.planet))) + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Ldfld, + AccessTools.Field(typeof(PlanetData), nameof(PlanetData.factory))), + new CodeMatch(OpCodes.Brfalse) ) - .Advance(-5); - factoryIndexOp = matcher.InstructionAt(3).opcode; - matcher.SetAndAdvance(factoryIndexOp, null) - .InsertAndAdvance( - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(UIStatisticsWindow_Transpiler), nameof(GetPlanetData))) + .Repeat(matcher => matcher + .Advance(1) + .SetAndAdvance(OpCodes.Call, AccessTools.Method(typeof(UIStatisticsWindow_Transpiler), nameof(HasFactory))) ) - .RemoveInstructions(5); - return matcher.InstructionEnumeration(); + .InstructionEnumeration(); } catch { @@ -74,7 +46,6 @@ private static IEnumerable RefreshAstroBox_Transpiler(IEnumerab return codeInstructions; } } - */ [HarmonyTranspiler] [HarmonyPatch(nameof(UIStatisticsWindow.ComputeDisplayProductEntries))] @@ -236,4 +207,13 @@ private static int GetFactoryIndex(PlanetData planet) } return Multiplayer.Session.Statistics.GetFactoryIndex(planet); } + + private static bool HasFactory(PlanetData planet) + { + if (!Multiplayer.IsActive || Multiplayer.Session.LocalPlayer.IsHost) + { + return planet.factory != null; + } + return Multiplayer.Session.Statistics.HasFactory(planet); + } } diff --git a/NebulaWorld/Chat/Commands/ClearCommandHandler.cs b/NebulaWorld/Chat/Commands/ClearCommandHandler.cs index d7fcd0251..83bafcd69 100644 --- a/NebulaWorld/Chat/Commands/ClearCommandHandler.cs +++ b/NebulaWorld/Chat/Commands/ClearCommandHandler.cs @@ -20,7 +20,7 @@ public void Execute(ChatWindow window, string[] parameters) return; } } - window.ClearChat(message => message.MessageType.IsCommandMessage()); + window.ClearChat(message => !message.MessageType.IsPlayerMessage()); } public string[] GetUsage() diff --git a/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs b/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs index 63b2030a8..20d4f84d5 100644 --- a/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs +++ b/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs @@ -193,7 +193,7 @@ private void QueueOutgoingChatMessage(string message, ChatMessageType chatMesage public ChatMessage SendLocalChatMessage(string text, ChatMessageType messageType) { - if (!messageType.IsCommandMessage()) + if (messageType.IsPlayerMessage()) { text = ChatUtils.SanitizeText(text); } @@ -205,22 +205,6 @@ public ChatMessage SendLocalChatMessage(string text, ChatMessageType messageType case ChatMessageType.SystemWarnMessage when !Config.Options.EnableWarnMessage: case ChatMessageType.BattleMessage when !Config.Options.EnableBattleMessage: return null; - case ChatMessageType.PlayerMessage: - break; - case ChatMessageType.CommandUsageMessage: - break; - case ChatMessageType.CommandOutputMessage: - break; - case ChatMessageType.CommandErrorMessage: - break; - case ChatMessageType.PlayerMessagePrivate: - break; - case ChatMessageType.SystemInfoMessage when Config.Options.EnableInfoMessage: - break; - case ChatMessageType.SystemWarnMessage when Config.Options.EnableWarnMessage: - break; - default: - throw new ArgumentOutOfRangeException(nameof(messageType), "Invalid message type: " + messageType); } } @@ -246,7 +230,7 @@ public ChatMessage SendLocalChatMessage(string text, ChatMessageType messageType { return newMsg; } - if (Config.Options.AutoOpenChat && !messageType.IsCommandMessage()) + if (Config.Options.AutoOpenChat && messageType.IsPlayerMessage()) { Toggle(false, false); } diff --git a/NebulaWorld/Statistics/StatisticsManager.cs b/NebulaWorld/Statistics/StatisticsManager.cs index 056469dc8..1f0ef74f4 100644 --- a/NebulaWorld/Statistics/StatisticsManager.cs +++ b/NebulaWorld/Statistics/StatisticsManager.cs @@ -255,8 +255,14 @@ public int GetFactoryIndex(PlanetData planet) return -1; } + public bool HasFactory(PlanetData planet) + { + return factoryIndexMap.ContainsKey(planet.id); + } + public long UpdateTotalChargedEnergy(int factoryIndex) { + if (PowerEnergyStoredData == null || factoryIndex >= PowerEnergyStoredData.Length) return 0; return PowerEnergyStoredData[factoryIndex]; } diff --git a/README.md b/README.md index a2178507e..2c66cb806 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Most of the battle aspects are sync, only few features are still WIP. - [x] Power network syncing (charger and request power from dyson sphere) - [x] Warning alarm syncing - [ ] Broadcast notification syncing +- [ ] Logistics Control Panel (I) syncing