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

Natpunch 3.9 dev #69

Merged
merged 16 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 11 additions & 1 deletion Fika.Core/Coop/GameMode/CoopGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using EFT.Counters;
using EFT.EnvironmentEffect;
using EFT.Game.Spawning;
using EFT.HealthSystem;
using EFT.Interactive;
using EFT.InventoryLogic;
using EFT.UI;
Expand All @@ -29,6 +28,7 @@
using Fika.Core.Networking;
using Fika.Core.Networking.Http;
using Fika.Core.Networking.Http.Models;
using Fika.Core.Networking.NatPunch;
using Fika.Core.Networking.Packets.GameWorld;
using Fika.Core.UI.Models;
using HarmonyLib;
Expand Down Expand Up @@ -925,6 +925,16 @@ public override async Task<LocalPlayer> vmethod_2(int playerId, Vector3 position

await WaitForPlayers();

if(isServer && FikaPlugin.UseNatPunching.Value)
{
FikaNatPunchServer natPunchServer = Singleton<FikaServer>.Instance.FikaNatPunchServer;

if (natPunchServer != null && natPunchServer.Connected)
{
natPunchServer.Close();
}
}

fikaDebug = gameObject.AddComponent<FikaDebug>();

Destroy(customButton);
Expand Down
2 changes: 2 additions & 0 deletions Fika.Core/Coop/Utils/FikaBackendUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public static class FikaBackendUtils
public static WeatherClass[] Nodes = null;
public static string RemoteIp;
public static int RemotePort;
public static int LocalPort = 0;
public static bool IsHostNatPunch = false;
private static string groupId;

public static MatchmakerTimeHasCome.GClass3187 ScreenController;
Expand Down
34 changes: 31 additions & 3 deletions Fika.Core/Coop/Utils/NetManagerUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ public static class NetManagerUtils
private static ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("NetManagerUtils");
public static GameObject FikaGameObject;

public static void CreateFikaGameObject()
{
FikaGameObject = new GameObject("FikaGameObject");
Object.DontDestroyOnLoad(FikaGameObject);
logger.LogInfo("FikaGameObject has been created!");
}

public static void CreateNetManager(bool isServer)
{
if (FikaGameObject == null)
{
FikaGameObject = new GameObject("FikaGameObject");
Object.DontDestroyOnLoad(FikaGameObject);
logger.LogInfo("FikaGameObject has been created!");
CreateFikaGameObject();
}

if (isServer)
Expand All @@ -36,6 +41,18 @@ public static void CreateNetManager(bool isServer)
}
}

public static void CreatePingingClient()
{
if (FikaGameObject == null)
{
CreateFikaGameObject();
}

FikaPingingClient pingingClient = FikaGameObject.AddComponent<FikaPingingClient>();
Singleton<FikaPingingClient>.Create(pingingClient);
logger.LogInfo("FikaPingingClient has started!");
}

public static void DestroyNetManager(bool isServer)
{
if (FikaGameObject != null)
Expand All @@ -55,6 +72,17 @@ public static void DestroyNetManager(bool isServer)
}
}

public static void DestroyPingingClient()
{
if(FikaGameObject != null)
{
Singleton<FikaPingingClient>.Instance.StopKeepAliveRoutine();
Singleton<FikaPingingClient>.Instance.NetClient.Stop();
Singleton<FikaPingingClient>.TryRelease(Singleton<FikaPingingClient>.Instance);
logger.LogInfo("Destroyed FikaPingingClient");
}
}

public static Task InitNetManager(bool isServer)
{
if (FikaGameObject != null)
Expand Down
3 changes: 3 additions & 0 deletions Fika.Core/Fika.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
<Reference Include="Unity.TextMeshPro" HintPath="..\References\Unity.TextMeshPro.dll" Private="false" />
<Reference Include="UnityEngine" HintPath="..\References\UnityEngine.dll" Private="false" />
<Reference Include="UnityEngine.UI" HintPath="..\References\UnityEngine.UI.dll" Private="false" />
<Reference Include="websocket-sharp">
trippyone marked this conversation as resolved.
Show resolved Hide resolved
<HintPath>..\References\websocket-sharp.dll</HintPath>
trippyone marked this conversation as resolved.
Show resolved Hide resolved
</Reference>
trippyone marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition=" '$(OS)' == 'Windows_NT' ">
Expand Down
17 changes: 10 additions & 7 deletions Fika.Core/FikaPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ public class FikaPlugin : BaseUnityPlugin
public static ConfigEntry<float> AutoRefreshRate { get; set; }
public static ConfigEntry<int> UDPPort { get; set; }
public static ConfigEntry<bool> UseUPnP { get; set; }
public static ConfigEntry<bool> UseNatPunching { get; set; }
public static ConfigEntry<int> ConnectionTimeout { get; set; }

// Gameplay
Expand All @@ -196,6 +197,7 @@ protected void Awake()
Instance = this;
LogDependencyErrors();

GetClientConfig();
SetupConfig();

new FikaVersionLabel_Patch().Enable();
Expand Down Expand Up @@ -229,7 +231,6 @@ protected void Awake()
BundleLoaderPlugin.Create();

FikaAirdropUtil.GetConfigFromServer();
GetClientConfig();
BotSettingsRepoClass.Init();

if (AllowItemSending)
Expand Down Expand Up @@ -386,17 +387,19 @@ private void SetupConfig()

// Network

NativeSockets = Config.Bind(section: "Network", "Native Sockets", false, new ConfigDescription("Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", tags: new ConfigurationManagerAttributes() { Order = 7 }));
NativeSockets = Config.Bind(section: "Network", "Native Sockets", false, new ConfigDescription("Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", tags: new ConfigurationManagerAttributes() { Order = 8 }));

ForceIP = Config.Bind("Network", "Force IP", "", new ConfigDescription("Forces the server when hosting to use this IP when broadcasting to the backend instead of automatically trying to fetch it. Leave empty to disable.", tags: new ConfigurationManagerAttributes() { Order = 7 }));

ForceIP = Config.Bind("Network", "Force IP", "", new ConfigDescription("Forces the server when hosting to use this IP when broadcasting to the backend instead of automatically trying to fetch it. Leave empty to disable.", tags: new ConfigurationManagerAttributes() { Order = 6 }));
ForceBindIP = Config.Bind("Network", "Force Bind IP", "", new ConfigDescription("Forces the server when hosting to use this local IP when starting the server. Useful if you are hosting on a VPN.", new AcceptableValueList<string>(GetLocalAddresses()), new ConfigurationManagerAttributes() { Order = 6 }));

ForceBindIP = Config.Bind("Network", "Force Bind IP", "", new ConfigDescription("Forces the server when hosting to use this local IP when starting the server. Useful if you are hosting on a VPN.", new AcceptableValueList<string>(GetLocalAddresses()), new ConfigurationManagerAttributes() { Order = 5 }));
AutoRefreshRate = Config.Bind("Network", "Auto Server Refresh Rate", 10f, new ConfigDescription("Every X seconds the client will ask the server for the list of matches while at the lobby screen.", new AcceptableValueRange<float>(3f, 60f), new ConfigurationManagerAttributes() { Order = 5 }));

AutoRefreshRate = Config.Bind("Network", "Auto Server Refresh Rate", 10f, new ConfigDescription("Every X seconds the client will ask the server for the list of matches while at the lobby screen.", new AcceptableValueRange<float>(3f, 60f), new ConfigurationManagerAttributes() { Order = 4 }));
UDPPort = Config.Bind("Network", "UDP Port", 25565, new ConfigDescription("Port to use for UDP gameplay packets.", tags: new ConfigurationManagerAttributes() { Order = 4 }));

UDPPort = Config.Bind("Network", "UDP Port", 25565, new ConfigDescription("Port to use for UDP gameplay packets.", tags: new ConfigurationManagerAttributes() { Order = 3 }));
UseUPnP = Config.Bind("Network", "Use UPnP", false, new ConfigDescription("Attempt to open ports using UPnP. Useful if you cannot open ports yourself but the router supports UPnP.", tags: new ConfigurationManagerAttributes() { Order = 3 }));

UseUPnP = Config.Bind("Network", "Use UPnP", false, new ConfigDescription("Attempt to open ports using UPnP. Useful if you cannot open ports yourself but the router supports UPnP.", tags: new ConfigurationManagerAttributes() { Order = 2 }));
UseNatPunching = Config.Bind("Network", "Use NAT Punching", false, new ConfigDescription("Use NAT punching as a NAT traversal method for hosting a raid. Only works with fullcone NAT type routers. UPnP, Force IP and Force Bind IP are disabled in this mode.", tags: new ConfigurationManagerAttributes() { Order = 2 }));

ConnectionTimeout = Config.Bind("Network", "Connection Timeout", 15, new ConfigDescription("How long it takes for a connection to be considered dropped if no packets are received.", new AcceptableValueRange<int>(5, 60), new ConfigurationManagerAttributes() { Order = 1 }));

Expand Down
7 changes: 6 additions & 1 deletion Fika.Core/Networking/FikaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ public void Init()
EnableStatistics = true
};

_netClient.Start();
if (FikaBackendUtils.IsHostNatPunch)
{
NetManagerUtils.DestroyPingingClient();
}

_netClient.Start(FikaBackendUtils.LocalPort);

string ip = FikaBackendUtils.RemoteIp;
int port = FikaBackendUtils.RemotePort;
Expand Down
119 changes: 93 additions & 26 deletions Fika.Core/Networking/FikaPingingClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@
using Fika.Core.Coop.Utils;
using Fika.Core.Networking.Http;
using Fika.Core.Networking.Http.Models;
using Fika.Core.Networking.NatPunch;
using LiteNetLib;
using LiteNetLib.Utils;
using SPT.Common.Http;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using UnityEngine;

namespace Fika.Core.Networking
{
internal class FikaPingingClient(string serverId) : INetEventListener
public class FikaPingingClient : MonoBehaviour, INetEventListener
{
public NetManager NetClient;
private readonly ManualLogSource _logger = Logger.CreateLogSource("Fika.PingingClient");
private readonly string serverId = serverId;
private readonly ManualLogSource _logger = BepInEx.Logging.Logger.CreateLogSource("Fika.PingingClient");
private IPEndPoint remoteEndPoint;
private IPEndPoint localEndPoint;
private IPEndPoint remoteStunEndPoint;
private int localPort = 0;
public bool Received = false;
private Coroutine keepAliveRoutine;
private Task natPunchRequestTask;

public bool Init()
public bool Init(string serverId)
{
NetClient = new(this)
{
Expand All @@ -28,6 +36,8 @@ public bool Init()
GetHostRequest body = new(serverId);
GetHostResponse result = FikaRequestHandler.GetHost(body);

FikaBackendUtils.IsHostNatPunch = result.NatPunch;

string ip = result.Ips[0];
string localIp = null;
if (result.Ips.Length > 1)
Expand All @@ -54,26 +64,84 @@ public bool Init()
localEndPoint = new(IPAddress.Parse(localIp), port);
}

NetClient.Start();
if (FikaBackendUtils.IsHostNatPunch)
{
natPunchRequestTask = Task.Run(() => NatPunchRequest(serverId));
}

NetClient.Start(localPort);

return true;
}

public void PingEndPoint()
public void PingEndPoint(string message)
{
if (Received)
{
return;
}

NetDataWriter writer = new();
writer.Put("fika.hello");
writer.Put(message);

NetClient.SendUnconnectedMessage(writer, remoteEndPoint);
if (localEndPoint != null)
{
NetClient.SendUnconnectedMessage(writer, localEndPoint);
}

if (remoteStunEndPoint != null && natPunchRequestTask.IsCompleted)
{
NetClient.SendUnconnectedMessage(writer, remoteStunEndPoint);
}
}

public async void NatPunchRequest(string serverId)
{
FikaNatPunchClient fikaNatPunchClient = new FikaNatPunchClient();
fikaNatPunchClient.Connect();

if (!fikaNatPunchClient.Connected)
{
_logger.LogError("Unable to connect to NatPunchRelayService.");
return;
}

StunIPEndPoint localStunEndPoint = NatPunchUtils.CreateStunEndPoint();

if (localStunEndPoint == null)
{
_logger.LogError("Nat Punch Request failed: Stun Endpoint is null.");
return;
}

GetHostStunRequest getStunRequest = new GetHostStunRequest(serverId, RequestHandler.SessionId, localStunEndPoint.Remote.Address.ToString(), localStunEndPoint.Remote.Port);
GetHostStunResponse getStunResponse = await fikaNatPunchClient.GetHostStun(getStunRequest);

fikaNatPunchClient.Close();

remoteStunEndPoint = new IPEndPoint(IPAddress.Parse(getStunResponse.StunIp), getStunResponse.StunPort);

localPort = localStunEndPoint.Local.Port;
}

public void StartKeepAliveRoutine()
{
keepAliveRoutine = StartCoroutine(KeepAlive());
}

public void StopKeepAliveRoutine()
{
if(keepAliveRoutine != null)
{
StopCoroutine(keepAliveRoutine);
trippyone marked this conversation as resolved.
Show resolved Hide resolved
}
}

public IEnumerator KeepAlive()
{
while(true)
{
PingEndPoint("fika.keepalive");
NetClient.PollEvents();

yield return new WaitForSeconds(1.0f);
}
}

public void OnConnectionRequest(ConnectionRequest request)
Expand All @@ -98,23 +166,22 @@ public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelN

public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType)
{
if (Received)
{
return;
}
_logger.LogInfo("Received response from server, parsing...");

if (reader.TryGetString(out string result))
{
if (result == "fika.hello")
{
Received = true;
FikaBackendUtils.RemoteIp = remoteEndPoint.Address.ToString();
FikaBackendUtils.RemotePort = remoteEndPoint.Port;
}
else
switch(result)
{
_logger.LogError("Data was not as expected");
case "fika.hello":
Received = true;
FikaBackendUtils.RemoteIp = remoteEndPoint.Address.ToString();
FikaBackendUtils.RemotePort = remoteEndPoint.Port;
FikaBackendUtils.LocalPort = localPort;
break;
case "fika.keepalive":
// Do nothing
break;
default:
_logger.LogError("Data was not as expected");
break;
}
}
else
Expand Down
Loading