diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index 8ee13c34..fe3013ef 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -65,6 +65,7 @@ public sealed class CoopGame : BaseLocalGame, IBotGame, IFik public bool HasReceivedLoot { get; set; } = false; public List ThrownGrenades; public bool WeatherReady; + public bool RaidStarted { get; set; } private readonly Dictionary botQueue = []; private Coroutine extractRoutine; @@ -578,9 +579,9 @@ public override IEnumerator vmethod_2() server.GameStartTime = startTime; sessionTime = GameTimer.SessionTime; - InformationPacket packet = new(false) + InformationPacket packet = new() { - NumberOfPlayers = server.NetServer.ConnectedPeersCount, + RaidStarted = RaidStarted, ReadyPlayers = server.ReadyClients, HostReady = true, GameTime = gameTime.Value, @@ -684,9 +685,9 @@ private async Task WaitForOtherPlayers() SetMatchmakerStatus(LocaleUtils.UI_WAIT_FOR_OTHER_PLAYERS.Localized(), (float)server.ReadyClients / expectedPlayers); } while (coopHandler.AmountOfHumans < expectedPlayers); - InformationPacket packet = new(false) + InformationPacket packet = new() { - NumberOfPlayers = server.NetServer.ConnectedPeersCount, + RaidStarted = RaidStarted, ReadyPlayers = server.ReadyClients }; @@ -712,9 +713,9 @@ private async Task WaitForOtherPlayers() DynamicAI.AddHumans(); } - InformationPacket finalPacket = new(false) + InformationPacket finalPacket = new() { - NumberOfPlayers = server.NetServer.ConnectedPeersCount, + RaidStarted = RaidStarted, ReadyPlayers = server.ReadyClients }; @@ -733,7 +734,7 @@ private async Task WaitForOtherPlayers() } while (coopHandler.AmountOfHumans < expectedPlayers); - InformationPacket packet = new(true) + InformationPacket packet = new() { ReadyPlayers = 1 }; @@ -928,6 +929,51 @@ public override async Task vmethod_3(GameWorld gameWorld, int playe return coopPlayer; } + /// + /// This creates a "custom" Back button so that we can back out if we get stuck + /// + /// + /// + /// + /// + private GameObject CreateStartButton() + { + if (MenuUI.Instantiated) + { + MenuUI menuUI = MenuUI.Instance; + DefaultUIButton backButton = Traverse.Create(menuUI.MatchmakerTimeHasCome).Field("_cancelButton").Value; + GameObject customButton = Instantiate(backButton.gameObject, backButton.gameObject.transform.parent); + customButton.gameObject.name = "FikaStartButton"; + //customButton.gameObject.transform.position = new(customButton.transform.position.x, customButton.transform.position.y - 20, customButton.transform.position.z); + customButton.gameObject.SetActive(true); + DefaultUIButton backButtonComponent = customButton.GetComponent(); + backButtonComponent.SetHeaderText(LocaleUtils.UI_START_RAID.Localized(), 32); + backButtonComponent.SetEnabledTooltip(LocaleUtils.UI_START_RAID_DESCRIPTION.Localized()); + UnityEngine.Events.UnityEvent newEvent = new(); + newEvent.AddListener(() => + { + if (isServer) + { + RaidStarted = true; + FikaBackendUtils.HostExpectedNumberOfPlayers = Singleton.Instance.NetServer.ConnectedPeersCount; + return; + } + + FikaClient fikaClient = Singleton.Instance ?? throw new NullReferenceException("CreateStartButton::FikaClient was null!"); + InformationPacket packet = new() + { + RequestStart = true + }; + fikaClient.SendData(ref packet, DeliveryMethod.ReliableOrdered); + }); + Traverse.Create(backButtonComponent).Field("OnClick").SetValue(newEvent); + + return customButton; + } + + return null; + } + /// /// This creates a "custom" Back button so that we can back out if we get stuck /// @@ -1101,7 +1147,7 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU } } - await WaitForPlayersToConnect(); + await WaitForHostToStart(); LocationSettingsClass.Location location = localRaidSettings_0.selectedLocation; if (isServer) @@ -1163,7 +1209,7 @@ private async Task WaitForHostToLoad() { FikaClient client = Singleton.Instance; - InformationPacket packet = new(true); + InformationPacket packet = new(); do { SetMatchmakerStatus(LocaleUtils.UI_WAIT_FOR_HOST_INIT.Localized()); @@ -1338,88 +1384,53 @@ private async Task InitInteractables() /// - /// used to wait for all other players to join the game + /// used to wait for host to start the raid /// /// - private async Task WaitForPlayersToConnect() + private async Task WaitForHostToStart() { - Logger.LogInfo("Starting task to wait for other players."); - - SetMatchmakerStatus(LocaleUtils.UI_INIT_COOP_GAME.Localized()); - int numbersOfPlayersToWaitFor = 0; + Logger.LogInfo("Starting task to wait for host to start the raid."); - string localizedPlayer = LocaleUtils.UI_WAIT_FOR_PLAYER.Localized(); - string localizedPlayers = LocaleUtils.UI_WAIT_FOR_PLAYERS.Localized(); + SetMatchmakerStatus("Waiting for host to start the raid..."); + GameObject startButton = null; if (isServer) { + startButton = CreateStartButton() ?? throw new NullReferenceException("Start button could not be created!"); FikaServer server = Singleton.Instance; server.RaidInitialized = true; - do + while (!RaidStarted) { - numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (server.NetServer.ConnectedPeersCount + 1); - if (numbersOfPlayersToWaitFor > 0) - { - bool multiple = numbersOfPlayersToWaitFor > 1; - SetMatchmakerStatus(string.Format(multiple ? localizedPlayers : localizedPlayer, - numbersOfPlayersToWaitFor)); - } - else - { - SetMatchmakerStatus(LocaleUtils.UI_ALL_PLAYERS_JOINED.Localized()); - } - await Task.Delay(100); - } while (numbersOfPlayersToWaitFor > 0); - } - else - { - FikaClient client = Singleton.Instance; - - while (client.NetClient == null) - { - await Task.Delay(500); + await Task.Yield(); } - int connectionAttempts = 0; - - while (client.ServerConnection == null && connectionAttempts < 5) + if (startButton != null) { - // Server retries 10 times with a 500ms interval, we give it 5 seconds to try - SetMatchmakerStatus(LocaleUtils.UI_WAITING_FOR_CONNECT.Localized()); - connectionAttempts++; - await Task.Delay(1000); - - if (client.ServerConnection == null && connectionAttempts == 5) - { - Singleton.Instance.ShowErrorScreen(LocaleUtils.UI_ERROR_CONNECTING.Localized(), - LocaleUtils.UI_ERROR_CONNECTING_TO_RAID.Localized()); - } + Destroy(startButton); } - while (client == null) - { - await Task.Delay(500); - } + SetStatusModel status = new(FikaBackendUtils.GroupId, LobbyEntry.ELobbyStatus.IN_GAME); + await FikaRequestHandler.UpdateSetStatus(status); + return; + } - InformationPacket packet = new(true); + if (FikaBackendUtils.IsDedicatedRequester) + { + startButton = CreateStartButton() ?? throw new NullReferenceException("Start button could not be created!"); + } + FikaClient client = Singleton.Instance; + InformationPacket packet = new(); + client.SendData(ref packet, DeliveryMethod.ReliableUnordered); + while (!RaidStarted) + { + await Task.Delay(250); client.SendData(ref packet, DeliveryMethod.ReliableUnordered); - do - { - numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (client.ConnectedClients + 1); - if (numbersOfPlayersToWaitFor > 0) - { - bool multiple = numbersOfPlayersToWaitFor > 1; - SetMatchmakerStatus(string.Format(multiple ? localizedPlayers : localizedPlayer, - numbersOfPlayersToWaitFor)); - } - else - { - SetMatchmakerStatus(LocaleUtils.UI_ALL_PLAYERS_JOINED.Localized()); - } - client.SendData(ref packet, DeliveryMethod.ReliableUnordered); - await Task.Delay(1000); - } while (numbersOfPlayersToWaitFor > 0); + } + + if (startButton != null) + { + Destroy(startButton); } } @@ -1545,11 +1556,7 @@ public override async Task vmethod_1(BotControllerSettings controllerSettings, I botsController_0.EventsController.SpawnAction(); FikaPlugin.DynamicAI.SettingChanged += DynamicAI_SettingChanged; - FikaPlugin.DynamicAIRate.SettingChanged += DynamicAIRate_SettingChanged; - - SetStatusModel status = new(FikaBackendUtils.GroupId, LobbyEntry.ELobbyStatus.IN_GAME); - - await FikaRequestHandler.UpdateSetStatus(status); + FikaPlugin.DynamicAIRate.SettingChanged += DynamicAIRate_SettingChanged; } // Add FreeCamController to GameWorld GameObject @@ -2454,11 +2461,12 @@ private void CleanUpFikaBackendUtils() { FikaBackendUtils.HostExpectedNumberOfPlayers = 1; FikaBackendUtils.IsSpectator = false; + FikaBackendUtils.IsDedicatedRequester = false; } FikaBackendUtils.RequestFikaWorld = false; FikaBackendUtils.IsReconnect = false; - FikaBackendUtils.ReconnectPosition = Vector3.zero; + FikaBackendUtils.ReconnectPosition = Vector3.zero; } private class ExitManager : Class1491 diff --git a/Fika.Core/Coop/Utils/FikaBackendUtils.cs b/Fika.Core/Coop/Utils/FikaBackendUtils.cs index e156929a..10e65d30 100644 --- a/Fika.Core/Coop/Utils/FikaBackendUtils.cs +++ b/Fika.Core/Coop/Utils/FikaBackendUtils.cs @@ -34,11 +34,12 @@ public static class FikaBackendUtils public static string PMCName { get; internal set; } public static EMatchmakerType MatchingType { get; internal set; } = EMatchmakerType.Single; public static bool IsDedicated = false; - public static bool IsReconnect { get; internal set; } = false; + public static bool IsReconnect { get; internal set; } public static bool IsDedicatedGame { get; set; } = false; - public static bool IsTransit { get; internal set; } = false; - public static bool IsSpectator { get; internal set; } = false; - public static bool IsHostNatPunch { get; internal set; } = false; + public static bool IsDedicatedRequester { get; set; } + public static bool IsTransit { get; internal set; } + public static bool IsSpectator { get; internal set; } + public static bool IsHostNatPunch { get; internal set; } public static int HostExpectedNumberOfPlayers { get; set; } = 1; public static string RemoteIp { get; internal set; } public static int RemotePort { get; internal set; } diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index 60cfd044..d7f0e014 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -22,9 +22,7 @@ using Fika.Core.Coop.Utils; using Fika.Core.Modding; using Fika.Core.Modding.Events; -using Fika.Core.Networking.Packets; using Fika.Core.Utils; -using FlyingWormConsole3; using HarmonyLib; using LiteNetLib; using LiteNetLib.Utils; @@ -46,7 +44,6 @@ public class FikaClient : MonoBehaviour, INetEventListener, IFikaNetworkManager public CoopPlayer MyPlayer; public int Ping = 0; public int ServerFPS = 0; - public int ConnectedClients = 0; public int ReadyClients = 0; public bool HostReady = false; public bool HostLoaded = false; @@ -1060,18 +1057,19 @@ private void OnHealthSyncPacketReceived(HealthSyncPacket packet) private void OnInformationPacketReceived(InformationPacket packet) { - if (!packet.IsRequest) + CoopGame coopGame = CoopHandler.LocalGameInstance; + if (coopGame != null) { - ConnectedClients = packet.NumberOfPlayers; - ReadyClients = packet.ReadyPlayers; - HostReady = packet.HostReady; - HostLoaded = packet.HostLoaded; + coopGame.RaidStarted = packet.RaidStarted; + FikaBackendUtils.HostExpectedNumberOfPlayers = packet.ReadyPlayers; + } + ReadyClients = packet.ReadyPlayers; + HostReady = packet.HostReady; + HostLoaded = packet.HostLoaded; - if (packet.HostReady) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - coopGame.SetClientTime(packet.GameTime, packet.SessionTime); - } + if (packet.HostReady) + { + coopGame.SetClientTime(packet.GameTime, packet.SessionTime); } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 0a047d33..7190118b 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -985,16 +985,22 @@ private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) { ReadyClients += packet.ReadyPlayers; - bool hostReady = coopHandler != null && coopHandler.LocalGameInstance != null && coopHandler.LocalGameInstance.Status == GameStatus.Started; + bool gameExists = coopHandler != null && coopHandler.LocalGameInstance != null; + bool hostReady = gameExists && coopHandler.LocalGameInstance.Status == GameStatus.Started; - InformationPacket respondPackage = new(false) + InformationPacket respondPackage = new() { - NumberOfPlayers = netServer.ConnectedPeersCount, + RaidStarted = gameExists && coopHandler.LocalGameInstance.RaidStarted, ReadyPlayers = ReadyClients, HostReady = hostReady, HostLoaded = RaidInitialized }; + if (gameExists && packet.RequestStart) + { + coopHandler.LocalGameInstance.RaidStarted = true; + } + if (hostReady) { respondPackage.GameTime = gameStartTime.Value; diff --git a/Fika.Core/Networking/Packets/Backend/InformationPacket.cs b/Fika.Core/Networking/Packets/Backend/InformationPacket.cs index ebaf4ab1..41ad5932 100644 --- a/Fika.Core/Networking/Packets/Backend/InformationPacket.cs +++ b/Fika.Core/Networking/Packets/Backend/InformationPacket.cs @@ -5,20 +5,20 @@ namespace Fika.Core.Networking { - public struct InformationPacket(bool isRequest) : INetSerializable + public struct InformationPacket : INetSerializable { - public bool IsRequest = isRequest; - public int NumberOfPlayers = 0; - public int ReadyPlayers = 0; - public bool HostReady = false; - public bool HostLoaded = false; + public bool RaidStarted; + public bool RequestStart; + public int ReadyPlayers; + public bool HostReady; + public bool HostLoaded; public DateTime GameTime; public TimeSpan SessionTime; public void Deserialize(NetDataReader reader) { - IsRequest = reader.GetBool(); - NumberOfPlayers = reader.GetInt(); + RaidStarted = reader.GetBool(); + RequestStart = reader.GetBool(); ReadyPlayers = reader.GetInt(); HostReady = reader.GetBool(); if (HostReady) @@ -31,8 +31,8 @@ public void Deserialize(NetDataReader reader) public void Serialize(NetDataWriter writer) { - writer.Put(IsRequest); - writer.Put(NumberOfPlayers); + writer.Put(RaidStarted); + writer.Put(RequestStart); writer.Put(ReadyPlayers); writer.Put(HostReady); if (HostReady) diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index 984f767d..4b4cb246 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -136,6 +136,8 @@ protected void OnDestroy() private void CreateMatchMakerUI() { + FikaBackendUtils.IsDedicatedRequester = false; + GetDedicatedStatusResponse response = FikaRequestHandler.GetDedicatedStatus(); GameObject matchMakerUiPrefab = InternalBundleLoader.Instance.GetAssetBundle("newmatchmakerui").LoadAsset("NewMatchMakerUI"); @@ -300,12 +302,13 @@ private void CreateMatchMakerUI() }; StartDedicatedResponse response = await FikaRequestHandler.StartDedicated(request); + FikaBackendUtils.IsDedicatedRequester = true; if (!string.IsNullOrEmpty(response.Error)) { PreloaderUI.Instance.ShowErrorScreen(LocaleUtils.UI_DEDICATED_ERROR.Localized(), response.Error); - ToggleLoading(false); + FikaBackendUtils.IsDedicatedRequester = false; } else { diff --git a/Fika.Core/UI/Patches/MenuTaskBar_Patch.cs b/Fika.Core/UI/Patches/MenuTaskBar_Patch.cs index ff95e2b1..ffeda5dc 100644 --- a/Fika.Core/UI/Patches/MenuTaskBar_Patch.cs +++ b/Fika.Core/UI/Patches/MenuTaskBar_Patch.cs @@ -6,7 +6,9 @@ using Newtonsoft.Json.Linq; using SPT.Reflection.Patching; using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using UnityEngine; using UnityEngine.UI; @@ -22,7 +24,8 @@ protected override MethodBase GetTargetMethod() } [PatchPostfix] - public static void Postfix(MenuTaskBar __instance) + public static void Postfix(Dictionary ____toggleButtons, Dictionary ____hoverTooltipAreas, ref GameObject[] ____newInformation) { GameObject watchlistGameobject = GameObject.Find("Preloader UI/Preloader UI/BottomPanel/Content/TaskBar/Tabs/Watchlist"); if (watchlistGameobject != null) @@ -42,7 +45,7 @@ public static void Postfix(MenuTaskBar __instance) LocalizedText text = downloadProfileGameObject.GetComponentInChildren(); if (text != null) { - text.method_2("DOWNLOAD PROFILE"); + text.method_2(LocaleUtils.UI_DOWNLOAD_PROFILE.Localized()); text.LocalizationKey = ""; } @@ -71,7 +74,7 @@ public static void Postfix(MenuTaskBar __instance) { Singleton.Instance.PlayUISound(EUISoundType.ButtonBottomBarClick); string installDir = Environment.CurrentDirectory; - string fikaDir = installDir + @"\user\fika"; + string fikaDir = Path.Combine(installDir, @"\user\fika"); if (!string.IsNullOrEmpty(installDir)) { @@ -91,6 +94,8 @@ public static void Postfix(MenuTaskBar __instance) NotificationManagerClass.DisplayMessageNotification(string.Format(LocaleUtils.SAVED_PROFILE.Localized(), [ColorizeText(EColor.BLUE, profileId), fikaDir])); + ____toggleButtons.Remove(EMenuType.NewsHub); + ____hoverTooltipAreas.Remove(EMenuType.NewsHub); GameObject.Destroy(downloadProfileGameObject); } } @@ -105,6 +110,18 @@ public static void Postfix(MenuTaskBar __instance) FikaPlugin.Instance.FikaLogger.LogError(ex.Message); } }); + + HoverTooltipArea surveyButton = ____hoverTooltipAreas[EMenuType.NewsHub]; + + ____toggleButtons.Remove(EMenuType.NewsHub); + ____hoverTooltipAreas.Remove(EMenuType.NewsHub); + GameObject.Destroy(surveyButton.gameObject); + List newList = new(____newInformation); + newList.Remove(newList.Last()); + ____newInformation = [.. newList]; + + ____toggleButtons.Add(EMenuType.NewsHub, animatedToggle); + ____hoverTooltipAreas.Add(EMenuType.NewsHub, downloadProfileGameObject.GetComponent()); } } } diff --git a/Fika.Core/Utils/LocaleUtils.cs b/Fika.Core/Utils/LocaleUtils.cs index 3a95e70f..079b355f 100644 --- a/Fika.Core/Utils/LocaleUtils.cs +++ b/Fika.Core/Utils/LocaleUtils.cs @@ -155,6 +155,8 @@ public static bool IsBoss(WildSpawnType wildSpawnType, out string name) public const string UI_MM_LOADING_HEADER = "F_MMUI_LoadingScreenHeader"; public const string UI_MM_LOADING_DESCRIPTION = "F_MMUI_LoadingScreenDescription"; public const string UI_MM_JOIN_AS_SPECTATOR = "F_MMUI_JoinAsSpectator"; + public const string UI_START_RAID = "F_UI_StartRaid"; + public const string UI_START_RAID_DESCRIPTION = "F_UI_StartRaidDescription"; public const string UI_CANNOT_JOIN_RAID_OTHER_MAP = "F_UI_CannotJoinRaidOtherMap"; public const string UI_CANNOT_JOIN_RAID_OTHER_TIME = "F_UI_CannotJoinRaidOtherTime"; public const string UI_CANNOT_JOIN_RAID_SCAV_AS_PMC = "F_UI_CannotJoinRaidScavAsPMC"; @@ -207,6 +209,7 @@ public static bool IsBoss(WildSpawnType wildSpawnType, out string name) public const string UI_NOTIFICATION_RECEIVED_ITEM = "F_Notification_ItemReceived"; public const string UI_NOTIFICATION_RAIDSETTINGS_DISABLED = "F_Notification_RaidSettingsDisabled"; public const string UI_EXTRACT_MESSAGE = "F_UI_ExtractMessage"; + public const string UI_DOWNLOAD_PROFILE = "F_UI_DownloadProfile"; // Main Menu UI public const string UI_MMUI_ONLINE_PLAYERS = "F_MMUI_OnlinePlayers";