diff --git a/CHANGELOG.md b/CHANGELOG.md index dd5e87472..3cc839205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## Changelog +0.9.6: +- @AlienXAXS: Fix headless server throwing a small error during boot sequence due to the UI being disabled +- @PhantomGamers: Add additional error description to ngrokmanager +- @starfi5h: Enable Log.Debug messages +- @starfi5h: Fix DF relay landed on planet message in client +- @starfi5h: Fix client's attacks won't increase DF threat when host player is dead +- @starfi5h: Fix ACH_BroadcastStar.OnGameTick error on client +- @starfi5h: Prevent server from sending out construction drones in headless mode + 0.9.5: - @starfi5h: Sync Dark Fog communicator (aggressiveness and truce) - @starfi5h: Show server last save time in client esc menu diff --git a/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGKillEnemyProcessor.cs b/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGKillEnemyProcessor.cs index f98d09696..16832acd6 100644 --- a/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGKillEnemyProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Combat/GroundEnemy/DFGKillEnemyProcessor.cs @@ -16,7 +16,7 @@ public class DFGKillEnemyProcessor : PacketProcessor<DFGKillEnemyPacket> protected override void ProcessPacket(DFGKillEnemyPacket packet, NebulaConnection conn) { var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; - if (factory == null) return; + if (factory == null || packet.EnemyId >= factory.enemyPool.Length) return; if (IsHost) { diff --git a/NebulaPatcher/Patches/Dynamic/ACH_BroadcastStar_Patch.cs b/NebulaPatcher/Patches/Dynamic/ACH_BroadcastStar_Patch.cs new file mode 100644 index 000000000..8f979a1ee --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/ACH_BroadcastStar_Patch.cs @@ -0,0 +1,25 @@ +#region + +using HarmonyLib; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(ACH_BroadcastStar))] +internal class ACH_BroadcastStar_Patch +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(ACH_BroadcastStar.OnGameTick))] + [HarmonyPatch(nameof(ACH_BroadcastStar.TryAlterEntity))] + public static bool Block_On_Client_Prefix() + { + // ACH_BroadcastStar is for the achievement "Let there be light!" + // which needs to light up Artificial Star (2210) on certain planets + // Clients only have partial factories loaded so they won't get the correct stats. + // Therefore it's disabled to prevent errors. + // TODO: Try to handle global achievements syncing? + return !Multiplayer.IsActive || Multiplayer.Session.IsServer; + } +} diff --git a/NebulaPatcher/Patches/Dynamic/Dedicated_Server_Patch.cs b/NebulaPatcher/Patches/Dynamic/Dedicated_Server_Patch.cs index 13c3ceeaf..c47e646f5 100644 --- a/NebulaPatcher/Patches/Dynamic/Dedicated_Server_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/Dedicated_Server_Patch.cs @@ -41,6 +41,8 @@ public static void GameMainBegin_Postfix() { // Don't let the player of dedicated server to interact with enemies GameMain.mainPlayer.isAlive = false; + // Don't let the player of dedicated server send out construction drones + GameMain.mainPlayer.mecha.constructionModule.droneEnabled = false; } } @@ -64,7 +66,7 @@ public static bool OnLateUpdate() return false; } - // Destory gameObject so Update() won't execute + // Destroy gameObject so Update() won't execute [HarmonyPrefix] [HarmonyPatch(typeof(PlanetAtmoBlur), nameof(PlanetAtmoBlur.Start))] public static bool PlanetAtmoBlur_Start(PlanetAtmoBlur __instance) diff --git a/NebulaPatcher/Patches/Dynamic/SkillSystem_Common_Patch.cs b/NebulaPatcher/Patches/Dynamic/SkillSystem_Common_Patch.cs index e9631d518..cc51d0f1e 100644 --- a/NebulaPatcher/Patches/Dynamic/SkillSystem_Common_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/SkillSystem_Common_Patch.cs @@ -33,13 +33,14 @@ public static bool GeneralShieldBurst_Prefix(ref GeneralShieldBurst __instance, return false; } - static void SwtichPlayerState(int playerId) + static void SwitchPlayerState(int playerId) { if (!Multiplayer.Session.Combat.IndexByPlayerId.TryGetValue(playerId, out var index)) return; ref var playerData = ref Multiplayer.Session.Combat.Players[index]; var skillSystem = GameMain.data.spaceSector.skillSystem; skillSystem.mecha = playerData.mecha; + skillSystem.playerAlive = playerData.isAlive; skillSystem.playerSkillTargetL = playerData.skillTargetL; skillSystem.playerSkillTargetULast = playerData.skillTargetULast; skillSystem.playerSkillTargetU = playerData.skillTargetU; @@ -49,13 +50,14 @@ static void SwtichPlayerState(int playerId) skillSystem.playerSkillCastRightU = playerData.skillTargetU; } - static void SwtichTargetPlayerWithCollider(int playerId) + static void SwitchTargetPlayerWithCollider(int playerId) { if (!Multiplayer.Session.Combat.IndexByPlayerId.TryGetValue(playerId, out var index)) return; ref var playerData = ref Multiplayer.Session.Combat.Players[index]; var skillSystem = GameMain.data.spaceSector.skillSystem; skillSystem.mecha = playerData.mecha; + skillSystem.playerAlive = playerData.isAlive; skillSystem.playerSkillTargetL = playerData.skillTargetL; skillSystem.playerSkillTargetULast = playerData.skillTargetULast; skillSystem.playerSkillTargetU = playerData.skillTargetU; @@ -66,8 +68,8 @@ static void SwtichTargetPlayerWithCollider(int playerId) public static void GeneralExpImpProjectile_Prefix(ref GeneralExpImpProjectile __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if (__instance.target.type == ETargetType.Player) SwtichTargetPlayerWithCollider(__instance.target.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if (__instance.target.type == ETargetType.Player) SwitchTargetPlayerWithCollider(__instance.target.id); } [HarmonyPrefix] @@ -75,8 +77,8 @@ public static void GeneralExpImpProjectile_Prefix(ref GeneralExpImpProjectile __ public static void GeneralProjectile_Prefix(ref GeneralMissile __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if (__instance.target.type == ETargetType.Player) SwtichTargetPlayerWithCollider(__instance.target.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if (__instance.target.type == ETargetType.Player) SwitchTargetPlayerWithCollider(__instance.target.id); } [HarmonyPrefix] @@ -84,8 +86,8 @@ public static void GeneralProjectile_Prefix(ref GeneralMissile __instance) public static void GeneralProjectile_Prefix(ref GeneralProjectile __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if (__instance.target.type == ETargetType.Player) SwtichTargetPlayerWithCollider(__instance.target.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if (__instance.target.type == ETargetType.Player) SwitchTargetPlayerWithCollider(__instance.target.id); } [HarmonyPrefix] @@ -93,8 +95,8 @@ public static void GeneralProjectile_Prefix(ref GeneralProjectile __instance) public static void LocalGeneralProjectile_Prefix(ref LocalGeneralProjectile __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if (__instance.target.type == ETargetType.Player) SwtichTargetPlayerWithCollider(__instance.target.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if (__instance.target.type == ETargetType.Player) SwitchTargetPlayerWithCollider(__instance.target.id); } [HarmonyPrefix] @@ -102,8 +104,8 @@ public static void LocalGeneralProjectile_Prefix(ref LocalGeneralProjectile __in public static void LocalLaserContinuous_Prefix(ref LocalLaserContinuous __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if (__instance.target.type == ETargetType.Player) SwtichPlayerState(__instance.target.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if (__instance.target.type == ETargetType.Player) SwitchPlayerState(__instance.target.id); } [HarmonyPrefix] @@ -111,8 +113,8 @@ public static void LocalLaserContinuous_Prefix(ref LocalLaserContinuous __instan public static void LocalLaserOneShot_Prefix(ref LocalLaserOneShot __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if (__instance.target.type == ETargetType.Player) SwtichPlayerState(__instance.target.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if (__instance.target.type == ETargetType.Player) SwitchPlayerState(__instance.target.id); } [HarmonyPrefix] @@ -120,7 +122,7 @@ public static void LocalLaserOneShot_Prefix(ref LocalLaserOneShot __instance) public static void LocalCannonade_Prefix(ref LocalCannonade __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); } [HarmonyPrefix] @@ -128,8 +130,8 @@ public static void LocalCannonade_Prefix(ref LocalCannonade __instance) public static void SpaceLaserOneShot_Prefix(ref SpaceLaserOneShot __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if (__instance.target.type == ETargetType.Player) SwtichPlayerState(__instance.target.id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if (__instance.target.type == ETargetType.Player) SwitchPlayerState(__instance.target.id); } [HarmonyPrefix] @@ -137,7 +139,7 @@ public static void SpaceLaserOneShot_Prefix(ref SpaceLaserOneShot __instance) public static void SpaceLaserSweep_Prefix(ref SpaceLaserSweep __instance) { if (!Multiplayer.IsActive) return; - if (__instance.caster.type == ETargetType.Player) SwtichPlayerState(__instance.caster.id); - if ((__instance.mask & ETargetTypeMask.Player) != 0) SwtichTargetPlayerWithCollider(Multiplayer.Session.LocalPlayer.Id); + if (__instance.caster.type == ETargetType.Player) SwitchPlayerState(__instance.caster.id); + if ((__instance.mask & ETargetTypeMask.Player) != 0) SwitchTargetPlayerWithCollider(Multiplayer.Session.LocalPlayer.Id); } } diff --git a/NebulaPatcher/Patches/Dynamic/SkillSystem_Patch.cs b/NebulaPatcher/Patches/Dynamic/SkillSystem_Patch.cs index ac4db07ca..e35927ea6 100644 --- a/NebulaPatcher/Patches/Dynamic/SkillSystem_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/SkillSystem_Patch.cs @@ -49,6 +49,17 @@ public static bool Import_Prefix(SkillSystem __instance, BinaryReader r) return false; } + [HarmonyPostfix] + [HarmonyPatch(nameof(SkillSystem.CollectPlayerStates))] + public static void CollectPlayerStates_Postfix(SkillSystem __instance) + { + if (!Multiplayer.IsActive) return; + + // Set those flags to false so AddSpaceEnemyHatred can add threat correctly for client's skill in host + __instance.playerIsSailing = false; + __instance.playerIsWarping = false; + } + [HarmonyPostfix] [HarmonyPatch(nameof(SkillSystem.AfterTick))] public static void AfterTick_Postfix(SkillSystem __instance) diff --git a/NebulaPatcher/Patches/Transpilers/ACH_BroadcastStar_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/ACH_BroadcastStar_Transpiler.cs deleted file mode 100644 index dac10a763..000000000 --- a/NebulaPatcher/Patches/Transpilers/ACH_BroadcastStar_Transpiler.cs +++ /dev/null @@ -1,69 +0,0 @@ -#region - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using HarmonyLib; -using NebulaModel.Logger; -using NebulaWorld; - -#endregion - -namespace NebulaPatcher.Patches.Transpilers; - -[HarmonyPatch(typeof(ACH_BroadcastStar))] -internal class ACH_BroadcastStar_Transpiler -{ - /* - * Returns early if - * instance.gameData.factories[factoryId]?.powerSystem?.genPool[generatorId] == null || instance.gameData.factories[factoryId].index == -1 - * while in a multiplayer session as a client - */ - [HarmonyTranspiler] - [HarmonyPatch(nameof(ACH_BroadcastStar.OnGameTick))] - private static IEnumerable<CodeInstruction> OnGameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator il) - { - var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); - try - { - var codeMatcher = new CodeMatcher(codeInstructions, il) - .MatchForward(false, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "gameData"), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "factories"), - new CodeMatch(i => i.IsLdloc()), - new CodeMatch(OpCodes.Ldelem_Ref) - ); - - var factoryIdInstruction = codeMatcher.InstructionAt(3); - var generatorIdInstruction = codeMatcher.InstructionAt(7); - - return codeMatcher - .CreateLabel(out var label) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) - .InsertAndAdvance(factoryIdInstruction) - .InsertAndAdvance(generatorIdInstruction) - .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate<Func<ACH_BroadcastStar, int, int, bool>>( - (instance, factoryId, generatorId) => - { - if (!Multiplayer.IsActive || Multiplayer.Session.LocalPlayer.IsHost) - { - return true; - } - - return instance.gameData.factories[factoryId]?.powerSystem?.genPool[generatorId] != null && - instance.gameData.factories[factoryId].index != -1; - })) - .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue, label)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ret)) - .InstructionEnumeration(); - } - catch - { - Log.Error("ACH_BroadcastStar.OnGameTick_Transpiler failed. Mod version not compatible with game version."); - return codeInstructions; - } - } -} diff --git a/NebulaPatcher/Patches/Transpilers/EnemyDFHiveSystem_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/EnemyDFHiveSystem_Transpiler.cs index 47a8289fa..d130655b0 100644 --- a/NebulaPatcher/Patches/Transpilers/EnemyDFHiveSystem_Transpiler.cs +++ b/NebulaPatcher/Patches/Transpilers/EnemyDFHiveSystem_Transpiler.cs @@ -37,6 +37,43 @@ public static IEnumerable<CodeInstruction> GameTickLogic_Transpiler(IEnumerable< .MatchForward(true, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(DFRelayComponent), nameof(DFRelayComponent.RealizePlanetBase)))) .Set(OpCodes.Call, AccessTools.Method(typeof(EnemyDFHiveSystem_Transpiler), nameof(RealizePlanetBase))); + + // Insert null guard in EnemyUnitComponent loop + // if (ptr10.id == k) + // { + // ref EnemyData ptr11 = ref enemyPool[ptr10.enemyId]; + // PrefabDesc prefabDesc = SpaceSector.PrefabDescByModelIndex[(int)ptr11.modelIndex]; + // if (prefabDesc == null) continue; // null guard + + codeMatcher.MatchForward(true, + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EnemyUnitComponent), nameof(EnemyUnitComponent.id))), + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Bne_Un)); + if (codeMatcher.IsInvalid) + { + Log.Warn("EnemyDFHiveSystem.GameTickLogic: Can't find operand_continue"); + return codeMatcher.InstructionEnumeration(); + } + var operand_continue = codeMatcher.Operand; + + codeMatcher.MatchForward(true, + new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(SpaceSector), nameof(SpaceSector.PrefabDescByModelIndex))), + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Ldfld), + new CodeMatch(OpCodes.Ldelem_Ref), + new CodeMatch(OpCodes.Stloc_S)); + if (codeMatcher.IsInvalid) + { + Log.Warn("EnemyDFHiveSystem.GameTickLogic: Can't find operand_prefabDesc"); + return codeMatcher.InstructionEnumeration(); + } + var operand_prefabDesc = codeMatcher.Operand; + + codeMatcher.Advance(1).Insert( + new CodeInstruction(OpCodes.Ldloc_S, operand_prefabDesc), + new CodeInstruction(OpCodes.Brfalse_S, operand_continue)); + return codeMatcher.InstructionEnumeration(); } catch (System.Exception e) diff --git a/NebulaWorld/SimulatedWorld.cs b/NebulaWorld/SimulatedWorld.cs index e65ddfc89..aa889feed 100644 --- a/NebulaWorld/SimulatedWorld.cs +++ b/NebulaWorld/SimulatedWorld.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using NebulaAPI; using NebulaAPI.DataStructures; @@ -24,7 +23,6 @@ using UnityEngine; using UnityEngine.UI; using Object = UnityEngine.Object; -using Random = UnityEngine.Random; #endregion @@ -127,6 +125,7 @@ public void SetupInitialPlayerState() GameMain.mainPlayer.mecha.coreEnergy = GameMain.mainPlayer.mecha.coreEnergyCap; GameMain.mainPlayer.mecha.energyShieldEnergy = GameMain.mainPlayer.mecha.energyShieldCapacity; GameMain.mainPlayer.mecha.hp = GameMain.mainPlayer.mecha.hpMaxApplied; + GameMain.mainPlayer.mecha.constructionModule.droneEnabled = true; if (GameMain.history.logisticShipWarpDrive) { // If warp has unlocked, give new client few warpers @@ -188,7 +187,7 @@ public void SetupInitialPlayerState() } // Finally we need add the local player components to the player character localPlayerMovement = GameMain.mainPlayer.gameObject.AddComponentIfMissing<LocalPlayerMovement>(); - // ChatManager should continuous exsit until the game is closed + // ChatManager should exist continuously until the game is closed GameMain.mainPlayer.gameObject.AddComponentIfMissing<ChatManager>(); } @@ -234,7 +233,7 @@ public static void OnPlayerJoinedGame(INebulaPlayer player) { Multiplayer.Session.World.SpawnRemotePlayerModel(player.Data); - // Load overriden Planet and Star names + // Sync overrideName of planets and stars player.SendPacket(new NameInputPacket(GameMain.galaxy)); // add together player sand count and tell others if we are syncing soil @@ -316,10 +315,10 @@ public void SpawnRemotePlayerModel(IPlayerData playerData) model.Movement.localPlanetId = playerData.LocalPlanetId; remotePlayersModels.Add(playerData.PlayerId, model); - // Show conneted message - var planetname = GameMain.galaxy.PlanetById(playerData.LocalPlanetId)?.displayName ?? "In space"; + // Show connected message + var planetName = GameMain.galaxy.PlanetById(playerData.LocalPlanetId)?.displayName ?? "In space"; var message = string.Format("[{0:HH:mm}] {1} connected ({2})".Translate(), DateTime.Now, playerData.Username, - planetname); + planetName); ChatManager.Instance.SendChatMessage(message, ChatMessageType.SystemInfoMessage); } } diff --git a/README.md b/README.md index ba3adcdd6..a2178507e 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ The chat window can opened/closed using Alt + Backtick (configurable in Settings Major refactors will happen while the project grows or game updates. Join the [Discord Server](https://discord.gg/UHeB2QvgDa) if you want to see to latest state of our development. Check [Wiki](https://github.com/NebulaModTeam/nebula/wiki/About-Nebula) for overview of features. -The multiplayer mod now supports Dark Fog combat mode in the latest game version (0.10.29.x). -Most of the battle aspect are sync, only few features are still WIP. +The multiplayer mod now supports Dark Fog combat mode in the latest game version (0.10.30.x). +Most of the battle aspects are sync, only few features are still WIP. <details> <summary>List of peace mode syncing features</summary> @@ -62,7 +62,7 @@ Most of the battle aspect are sync, only few features are still WIP. - [x] Factories statistics syncing - [x] Containers inventory syncing - [x] Building Interaction syncing -- [x] Belts syncing +- [x] Belts interaction syncing (pickup, putdown) - [x] Trash (dropped items) syncing - [x] Interstellar Station syncing - [x] Drones events syncing @@ -70,6 +70,7 @@ Most of the battle aspect are sync, only few features are still WIP. - [x] Server state persistence - [x] Power network syncing (charger and request power from dyson sphere) - [x] Warning alarm syncing +- [ ] Broadcast notification syncing </details> @@ -109,8 +110,8 @@ Most of the battle aspect are sync, only few features are still WIP. - [x] Show base/hive/relay invasion events in chat - [ ] Sync kill stats - [x] Sync Dark Fog communicator (aggressiveness and truce) -- [ ] Show remote mecha combat drone fleet -- [ ] Show remote mecha space craft fleet +- [ ] Show remote mecha combat drone fleet animation +- [ ] Show remote mecha spacecraft fleet animation - [ ] Show ground-to-space attacks animation on client for remote planets (missile turrets, plasma cannon) - [ ] Show space-to-ground attacks animation for remote planets (lancers invading with sweep laser and bomber) diff --git a/version.json b/version.json index 09aceb2aa..1ee1a87a2 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.9.5", + "version": "0.9.6", "assemblyVersion": { "precision": "build" },