Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP FEATURE] Add Reconnecting #39

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
f3688ff
Add Rejoin to Enum
CWXDEV May 10, 2024
738a1fc
add Rejoin option to UIscript (still says join for now, tooltip menti…
CWXDEV May 10, 2024
c04b63b
Add reconnect packets (to make into one packet)
CWXDEV May 10, 2024
1b0d597
add de/serialize for interactiveObjects/windows/lights
CWXDEV May 10, 2024
1723541
add reconnect flag and easy access packet location
CWXDEV May 10, 2024
5f74104
add reconnecting to CoopGame
CWXDEV May 10, 2024
986c5ed
Add gathering of things to sync and apply changes on clientside
CWXDEV May 10, 2024
2c9b474
change to be NetID instead of profileID
CWXDEV May 10, 2024
6a9aa6a
to Fix logic
CWXDEV May 10, 2024
fa54e94
commented out syncing code for now
CWXDEV May 10, 2024
f3b7162
change requestpacket back to profileId, move pose set up to a little …
CWXDEV May 10, 2024
a2eb021
re-enable syncing of windows/lights/doors, stop reassignment of netid…
CWXDEV May 10, 2024
4fcd518
change syncing to coroutine
CWXDEV May 11, 2024
f648b91
add spawning as corpse
CWXDEV May 11, 2024
df71038
make reconnect nullable, change logic to check for null packet, set p…
CWXDEV May 11, 2024
d3d1598
add logic for smoke syncing
CWXDEV May 11, 2024
f871733
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 11, 2024
0978ce5
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 13, 2024
20c12f2
add a flag to know when players have finished spawning
CWXDEV May 13, 2024
914a384
change to pass in smokeGrenade to avoid need to cast in extension
CWXDEV May 13, 2024
e89086c
wait for players to spawn before doing smoke logic to fix bundles iss…
CWXDEV May 13, 2024
0c29204
add reconnecting player inv sync, revert change to when smoke logic w…
CWXDEV May 13, 2024
2b7b123
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 13, 2024
773c759
Fix inventory syncing on reconnect, fixes bundles not being loaded
CWXDEV May 13, 2024
422b3a1
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 13, 2024
d113744
Add static/container loot syncing
CWXDEV May 16, 2024
c3c11fb
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 16, 2024
3b36eaf
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 16, 2024
f16353b
add workaround to collect corpses from Host, and just "kill the playe…
CWXDEV May 16, 2024
53902b0
Fix name formatting for bots
CWXDEV May 16, 2024
264f368
Fix pose level on spawning
CWXDEV May 16, 2024
a873a5a
add poseLevel
CWXDEV May 16, 2024
af7958b
add a basic retry mechanism, failing NEEDS to be reworked
CWXDEV May 16, 2024
027368d
Add Visual feedback on reconnecting, added Fail message and return to…
CWXDEV May 17, 2024
b2d87b0
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 18, 2024
5ccd9c6
fix spawning of players, fix race condition, leaving temp synthetic d…
CWXDEV May 19, 2024
511c51a
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 19, 2024
df4d27e
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 20, 2024
655ddcc
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 20, 2024
e84a5e6
sync profile instead of just equipment, add some notes
CWXDEV May 20, 2024
92752e3
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 21, 2024
567a6e6
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 21, 2024
bed5d8e
attempt to sync quests
CWXDEV May 21, 2024
c525133
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 22, 2024
107b632
add quest and achievement syncing
CWXDEV May 22, 2024
e87ef14
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 22, 2024
9321aeb
clean up
CWXDEV May 22, 2024
bd9c1d3
Merge branch 'dev' of https://github.com/project-fika/Fika-Plugin int…
CWXDEV May 23, 2024
6c44ec3
clean up with fixes
CWXDEV May 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Fika.Core/Coop/ClientClasses/CoopClientAchievementController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Comfort.Common;
using EFT;
using EFT.Quests;
using Fika.Core.Coop.Matchmaker;
using Fika.Core.Networking;

namespace Fika.Core.Coop.ClientClasses
{
public class CoopClientAchievementController : AchievementControllerClass
{
private readonly FikaClient _fikaClient = Singleton<FikaClient>.Instance;

public CoopClientAchievementController(Profile profile, InventoryControllerClass inventoryController, ISession session, bool fromServer) : base(profile, inventoryController, session, fromServer)
{
}

public override void OnConditionValueChanged(IConditionCounter conditional, EQuestStatus status, Condition condition, bool notify)
{
if (MatchmakerAcceptPatches.IsClient)
{
ConditionChangePacket packet = new(_fikaClient.MyPlayer.NetId, condition.id, condition.value);
_fikaClient.SendData(_fikaClient.DataWriter, ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered);
}

base.OnConditionValueChanged(conditional, status, condition, notify);
}
}
}
28 changes: 28 additions & 0 deletions Fika.Core/Coop/ClientClasses/CoopClientQuestController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Comfort.Common;
using EFT;
using EFT.Quests;
using Fika.Core.Coop.Matchmaker;
using Fika.Core.Networking;

namespace Fika.Core.Coop.ClientClasses
{
public class CoopClientQuestController : GClass3206
{
private FikaClient fikaClient = Singleton<FikaClient>.Instance;

public CoopClientQuestController(Profile profile, InventoryControllerClass inventoryController, ISession session, bool fromServer) : base(profile, inventoryController, session, fromServer)
{
}

public override void OnConditionValueChanged(IConditionCounter conditional, EQuestStatus status, Condition condition, bool notify)
{
if (MatchmakerAcceptPatches.IsClient)
{
ConditionChangePacket packet = new(fikaClient.MyPlayer.NetId, condition.id, condition.value);
fikaClient.SendData(fikaClient.DataWriter, ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered);
}

base.OnConditionValueChanged(conditional, status, condition, notify);
}
}
}
16 changes: 13 additions & 3 deletions Fika.Core/Coop/Components/CoopHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool i
internal static GameObject CoopHandlerParent;

private Coroutine PingRoutine;
public bool StartSpawning = false;

#endregion

Expand Down Expand Up @@ -283,6 +284,11 @@ private async Task ReadFromServerCharactersLoop()
{
while (RunAsyncTasks)
{
while (!StartSpawning)
{
await Task.Delay(1000);
}

CoopGame coopGame = (CoopGame)Singleton<IFikaGame>.Instance;
int waitTime = 2500;
if (coopGame.Status == GameStatus.Started)
Expand All @@ -294,7 +300,6 @@ private async Task ReadFromServerCharactersLoop()
if (Players == null)
{
continue;

}

ReadFromServerCharacters();
Expand Down Expand Up @@ -369,7 +374,7 @@ await Singleton<PoolManager>.Instance.LoadBundlesAndCreatePools(PoolManager.Pool

if (!spawnObject.IsAlive)
{
// TODO: Spawn them as corpses?
otherPlayer.OnDead(EDamageType.Undefined);
}

if (MatchmakerAcceptPatches.IsServer)
Expand Down Expand Up @@ -402,6 +407,11 @@ private IEnumerator ProcessSpawnQueue()
{
while (true)
{
if (!StartSpawning)
{
yield return new WaitUntil(() => StartSpawning);
}

yield return new WaitForSeconds(1f);

if (Singleton<AbstractGame>.Instantiated)
Expand Down Expand Up @@ -456,7 +466,7 @@ public WorldInteractiveObject GetInteractiveObject(string objectId, out WorldInt
private ObservedCoopPlayer SpawnObservedPlayer(Profile profile, Vector3 position, int playerId, bool isAI, int netId)
{
ObservedCoopPlayer otherPlayer = ObservedCoopPlayer.CreateObservedPlayer(playerId, position, Quaternion.identity,
"Player", isAI == true ? "Bot_" : $"Player_{profile.Nickname}_", EPointOfView.ThirdPerson, profile, isAI,
"Player", isAI == true ? $"Bot_{netId}_" : $"Player_{profile.Nickname}_", EPointOfView.ThirdPerson, profile, isAI,
EUpdateQueue.Update, Player.EUpdateMode.Manual, Player.EUpdateMode.Auto,
GClass549.Config.CharacterController.ObservedPlayerMode,
() => Singleton<SharedGameSettingsClass>.Instance.Control.Settings.MouseSensitivity,
Expand Down
86 changes: 73 additions & 13 deletions Fika.Core/Coop/GameMode/CoopGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
using Fika.Core.UI.Models;
using HarmonyLib;
using JsonType;
using LiteNetLib;
using LiteNetLib.Utils;
using Newtonsoft.Json;
using System;
Expand Down Expand Up @@ -486,7 +487,7 @@ private bool TryDespawnFurthest(Profile profile, Vector3 position, CoopHandler c
if (botKey == string.Empty)
{
#if DEBUG
Logger.LogWarning("TryDespawnFurthest: botKey was empty");
Logger.LogWarning("TryDespawnFurthest: botKey was empty");
#endif
return false;
}
Expand Down Expand Up @@ -677,7 +678,7 @@ private async Task SendOrReceiveSpawnPoint()

if (!string.IsNullOrEmpty(name))
{
Logger.LogInfo($"Retrieved Spawn Point '{name}' from server");
Logger.LogInfo($"Retrieved Spawn Point {name} from server");

Dictionary<ISpawnPoint, SpawnPointMarker> allSpawnPoints = Traverse.Create(spawnPoints).Field("dictionary_0").GetValue<Dictionary<ISpawnPoint, SpawnPointMarker>>();
foreach (ISpawnPoint spawnPointObject in allSpawnPoints.Keys)
Expand Down Expand Up @@ -727,7 +728,37 @@ public override async Task<LocalPlayer> vmethod_2(int playerId, Vector3 position
await CreateCoopHandler();
CoopHandler.GetCoopHandler().LocalGameInstance = this;

LocalPlayer myPlayer = await CoopPlayer.Create(playerId, spawnPoint.Position, spawnPoint.Rotation, "Player", "Main_", EPointOfView.FirstPerson, profile,
Vector3 PosToSpawn = spawnPoint.Position;
Quaternion RotToSpawn = spawnPoint.Rotation;
if (MatchmakerAcceptPatches.IsClient && MatchmakerAcceptPatches.IsReconnect)
{
ReconnectRequestPacket reconnectPacket = new(ProfileId);
MatchmakerAcceptPatches.GClass3163?.ChangeStatus($"Sending Reconnect Request...");

int retryCount = 0;
while (MatchmakerAcceptPatches.ReconnectPacket == null && retryCount < 5)
{
Singleton<FikaClient>.Instance?.SendData(new NetDataWriter(), ref reconnectPacket, DeliveryMethod.ReliableUnordered);
MatchmakerAcceptPatches.GClass3163?.ChangeStatus($"Requests Sent for reconnect... {retryCount + 1}");
await Task.Delay(3000);
retryCount++;
}

if (MatchmakerAcceptPatches.ReconnectPacket == null && retryCount == 5)
{
MatchmakerAcceptPatches.GClass3163?.ChangeStatus($"Failed to Reconnect...");
Singleton<PreloaderUI>.Instance.ShowCriticalErrorScreen("Network Error", "[EXPERIMENTAL] Unable to reconnect to the host. Please try again after returning to main menu.",
ErrorScreen.EButtonType.OkButton, 10f, ReconnectFailed, ReconnectFailed);
}

MatchmakerAcceptPatches.GClass3163?.ChangeStatus($"Reconnecting to host...");

PosToSpawn = MatchmakerAcceptPatches.ReconnectPacket.Value.Position;
RotToSpawn = MatchmakerAcceptPatches.ReconnectPacket.Value.Rotation;
profile = MatchmakerAcceptPatches.ReconnectPacket.Value.Profile.Profile;
}

LocalPlayer myPlayer = await CoopPlayer.Create(playerId, PosToSpawn, RotToSpawn, "Player", "Main_", EPointOfView.FirstPerson, profile,
false, UpdateQueue, armsUpdateMode, bodyUpdateMode,
GClass549.Config.CharacterController.ClientPlayerMode, getSensitivity,
getAimingSensitivity, new GClass1445(), MatchmakerAcceptPatches.IsServer ? 0 : 1000, statisticsManager);
Expand All @@ -740,13 +771,21 @@ public override async Task<LocalPlayer> vmethod_2(int playerId, Vector3 position
throw new MissingComponentException("CoopHandler was missing during CoopGame init");
}

CoopPlayer coopPlayer = (CoopPlayer)myPlayer;

if (MatchmakerAcceptPatches.IsClient && MatchmakerAcceptPatches.IsReconnect)
{
coopPlayer.NetId = MatchmakerAcceptPatches.ReconnectPacket.Value.NetId;
myPlayer.MovementContext.SetPoseLevel(MatchmakerAcceptPatches.ReconnectPacket.Value.PoseLevel, true);
myPlayer.MovementContext.IsInPronePose = MatchmakerAcceptPatches.ReconnectPacket.Value.IsProne;
}

if (RaidSettings.MetabolismDisabled)
{
myPlayer.HealthController.DisableMetabolism();
NotificationManagerClass.DisplayMessageNotification("Metabolism disabled", iconType: EFT.Communications.ENotificationIconType.Alert);
}

CoopPlayer coopPlayer = (CoopPlayer)myPlayer;
coopHandler.Players.Add(coopPlayer.NetId, coopPlayer);

PlayerSpawnRequest body = new(myPlayer.ProfileId, MatchmakerAcceptPatches.GetGroupId());
Expand Down Expand Up @@ -822,15 +861,18 @@ public override async Task<LocalPlayer> vmethod_2(int playerId, Vector3 position
}
}

SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket() { Profile = myPlayer.Profile }, myPlayer.HealthController.IsAlive, false, myPlayer.Transform.position, (myPlayer as CoopPlayer).NetId);

if (MatchmakerAcceptPatches.IsServer)
if (!MatchmakerAcceptPatches.IsReconnect)
{
await SetStatus(myPlayer, LobbyEntry.ELobbyStatus.COMPLETE);
}
else
{
Singleton<FikaClient>.Instance.SendData(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered);
SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket() { Profile = myPlayer.Profile }, myPlayer.HealthController.IsAlive, false, myPlayer.Transform.position, (myPlayer as CoopPlayer).NetId);

if (MatchmakerAcceptPatches.IsServer)
{
await SetStatus(myPlayer, LobbyEntry.ELobbyStatus.COMPLETE);
}
else
{
Singleton<FikaClient>.Instance.SendData(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered);
}
}

if (MatchmakerAcceptPatches.IsServer)
Expand All @@ -848,6 +890,7 @@ public override async Task<LocalPlayer> vmethod_2(int playerId, Vector3 position
}
}

coopHandler.StartSpawning = true;
await WaitForPlayers();

Destroy(customButton);
Expand All @@ -861,6 +904,12 @@ public override async Task<LocalPlayer> vmethod_2(int playerId, Vector3 position
return myPlayer;
}

private void ReconnectFailed()
{
ClientAppUtils.GetMainApp().method_48().HandleExceptions();
Logger.LogError($"Failed to reconnect to the host. Returning to main menu...");
}

private void MainPlayerDied(EDamageType obj)
{
EndByTimerScenario endByTimerScenario = GetComponent<EndByTimerScenario>();
Expand Down Expand Up @@ -960,6 +1009,8 @@ private async Task<Player> CreateLocalPlayer()
{
spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side);
await SendOrReceiveSpawnPoint();
MatchmakerAcceptPatches.IsReconnect = false;
MatchmakerAcceptPatches.ReconnectPacket = null;
}

if (MatchmakerAcceptPatches.IsClient)
Expand Down Expand Up @@ -1086,6 +1137,8 @@ private async Task WaitForPlayers()
client.SendData(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered);
await Task.Delay(1000);
} while (numbersOfPlayersToWaitFor > 0 && !forceStart);

MatchmakerAcceptPatches.SpawnedPlayersComplete = true;
}
}

Expand Down Expand Up @@ -1166,7 +1219,7 @@ public override IEnumerator vmethod_4(float startDelay, BotControllerSettings co
if (limits > 0)
{
botsController_0.BotSpawner.SetMaxBots(limits);
}
}
}

DynamicAI = gameObject.AddComponent<FikaDynamicAI>();
Expand Down Expand Up @@ -1605,6 +1658,13 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa
{
Logger.LogInfo("CoopGame::Stop");

if (MatchmakerAcceptPatches.IsReconnect)
{
MatchmakerAcceptPatches.IsReconnect = false;
MatchmakerAcceptPatches.ReconnectPacket = null;
MatchmakerAcceptPatches.SpawnedPlayersComplete = false;
}

CoopPlayer myPlayer = (CoopPlayer)Singleton<GameWorld>.Instance.MainPlayer;
myPlayer.PacketSender.DestroyThis();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using EFT;
using EFT.UI.Matchmaker;
using Fika.Core.Networking;
using Fika.Core.Networking.Http;
using Fika.Core.Networking.Http.Models;
using System;
Expand Down Expand Up @@ -32,6 +33,10 @@ public static class MatchmakerAcceptPatches
public static WeatherClass[] Nodes { get; set; } = null;
private static string groupId;
private static long timestamp;
public static bool IsReconnect = false;
public static ReconnectResponsePacket? ReconnectPacket;
public static bool SpawnedPlayersComplete = false;

#endregion

#region Static Fields
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Reflection;
using Aki.Reflection.Patching;
using EFT;
using Fika.Core.Coop.Matchmaker;

namespace Fika.Core.Coop.Patches
{
internal class BaseLocalGame_method_6_Patch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return typeof(BaseLocalGame<GamePlayerOwner>).GetMethod(nameof(BaseLocalGame<GamePlayerOwner>.method_11));
}

[PatchPrefix]
public static bool PatchPrefix(ref LocationSettingsClass.Location location)
{
if (MatchmakerAcceptPatches.IsClient && MatchmakerAcceptPatches.IsReconnect)
{
location.Loot = MatchmakerAcceptPatches.ReconnectPacket.Value.Items;
}

return true;
}
}
}
5 changes: 2 additions & 3 deletions Fika.Core/Coop/Players/CoopPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ public static async Task<LocalPlayer> Create(int playerId, Vector3 position, Qua

ISession session = Singleton<ClientApplication<ISession>>.Instance.GetClientBackEndSession();

GClass3206 questController = new(profile, inventoryController, session, true);
CoopClientQuestController questController = new(profile, inventoryController, session, true);
questController.Init();
questController.Run();

AchievementControllerClass achievementsController = new(profile, inventoryController, session, true);
CoopClientAchievementController achievementsController = new(profile, inventoryController, session, true);
achievementsController.Init();
achievementsController.Run();

Expand Down Expand Up @@ -844,7 +844,6 @@ public override void TryInteractionCallback(LootableContainer container)
protected virtual void Start()
{
Profile.Info.GroupId = "Fika";

if (Side != EPlayerSide.Savage)
{
if (Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null)
Expand Down
6 changes: 5 additions & 1 deletion Fika.Core/Coop/Players/ObservedCoopPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public static async Task<ObservedCoopPlayer> CreateObservedPlayer(int playerId,

CoopObservedStatisticsManager statisticsManager = new();


await player.Init(rotation, layerName, pointOfView, profile, inventoryController, healthController,
statisticsManager, null, null, filter, EVoipState.NotAvailable, aiControl, false);

Expand Down Expand Up @@ -746,7 +747,10 @@ public override void OnDead(EDamageType damageType)
}
else
{
NotificationManagerClass.DisplayWarningNotification($"Group member '{nickname}' has died");
if (!MatchmakerAcceptPatches.IsReconnect)
{
NotificationManagerClass.DisplayWarningNotification($"Group member '{Profile.Nickname}' has died");
}
}
}
if (IsBoss(Profile.Info.Settings.Role, out string name) && IsObservedAI && LastAggressor != null)
Expand Down
1 change: 1 addition & 0 deletions Fika.Core/FikaPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ protected void Awake()
new BotCacher_Patch().Enable();
new InventoryScroll_Patch().Enable();
new AbstractGame_InRaid_Patch().Enable();
new BaseLocalGame_method_6_Patch().Enable();
#if GOLDMASTER
new TOS_Patch().Enable();
#endif
Expand Down
Loading