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

NAT Punch 2.0 #76

Merged
merged 10 commits into from
Jul 5, 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
11 changes: 0 additions & 11 deletions Fika.Core/Coop/GameMode/CoopGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
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 @@ -924,16 +923,6 @@ 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
23 changes: 22 additions & 1 deletion Fika.Core/FikaPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Fika.Core.UI.Patches;
using Fika.Core.UI.Patches.MatchmakerAcceptScreen;
using Fika.Core.Utils;
using SPT.Common.Http;
using SPT.Custom.Airdrops.Patches;
using SPT.Custom.BTR.Patches;
using SPT.Custom.Patches;
Expand Down Expand Up @@ -189,11 +190,19 @@ public class FikaPlugin : BaseUnityPlugin
public bool SharedQuestProgression;
#endregion

#region natpunch config
public bool NatPunchServerEnable;
public string NatPunchServerIP;
public int NatPunchServerPort;
public int NatPunchServerNatIntroduceAmount;
#endregion

protected void Awake()
{
Instance = this;

GetClientConfig();
GetNatPunchServerConfig();
SetupConfig();

new FikaVersionLabel_Patch().Enable();
Expand Down Expand Up @@ -269,6 +278,18 @@ private void GetClientConfig()
clientConfig.LogValues();
}

private void GetNatPunchServerConfig()
{
NatPunchServerConfigModel natPunchServerConfig = FikaRequestHandler.GetNatPunchServerConfig();

NatPunchServerEnable = natPunchServerConfig.Enable;
NatPunchServerIP = RequestHandler.Host.Replace("http://", "").Split(':')[0];
NatPunchServerPort = natPunchServerConfig.Port;
NatPunchServerNatIntroduceAmount = natPunchServerConfig.NatIntroduceAmount;

natPunchServerConfig.LogValues();
}

private void SetupConfig()
{
// Hidden
Expand Down Expand Up @@ -408,7 +429,7 @@ private void SetupConfig()

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 }));

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 }));
UseNatPunching = Config.Bind("Network", "Use NAT Punching", false, new ConfigDescription("Use NAT punching when hosting a raid. Only works with fullcone NAT type routers and requires NatPunchServer to be running on the AKI server. UPnP, Force IP and Force Bind IP are disabled with 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
52 changes: 52 additions & 0 deletions Fika.Core/Models/NatPunchServerConfigModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Reflection;
using System.Runtime.Serialization;

namespace Fika.Core.UI.Models
{
[DataContract]
public struct NatPunchServerConfigModel
{
[DataMember(Name = "enable")]
public bool Enable;

[DataMember(Name = "port")]
public int Port;

[DataMember(Name = "natIntroduceAmount")]
public int NatIntroduceAmount;

public NatPunchServerConfigModel(bool enable, int port, int natIntroduceAmount)
{
Enable = enable;
Port = port;
NatIntroduceAmount = natIntroduceAmount;
}

public void LogValues()
{
FikaPlugin.Instance.FikaLogger.LogInfo("Received NatPunchServer config from server:");
FieldInfo[] fields = typeof(NatPunchServerConfigModel).GetFields();
foreach (FieldInfo field in fields)
{
object value = field.GetValue(this);
if (value is Array valueArray)
{
string values = "";
for (int i = 0; i < valueArray.Length; i++)
{
if (i == 0)
{
values = valueArray.GetValue(i).ToString();
continue;
}
values = values + ", " + valueArray.GetValue(i).ToString();
}
FikaPlugin.Instance.FikaLogger.LogInfo(field.Name + ": " + values);
continue;
}
FikaPlugin.Instance.FikaLogger.LogInfo(field.Name + ": " + value);
}
}
}
}
151 changes: 80 additions & 71 deletions Fika.Core/Networking/FikaPingingClient.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using BepInEx.Logging;
using EFT.UI;
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;
Expand All @@ -14,62 +14,70 @@

namespace Fika.Core.Networking
{
public class FikaPingingClient : MonoBehaviour, INetEventListener
public class FikaPingingClient : MonoBehaviour, INetEventListener, INatPunchListener
{
public NetManager NetClient;
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;
private Coroutine _keepAliveRoutine;

public bool Init(string serverId)
{
NetClient = new(this)
{
UnconnectedMessagesEnabled = true
UnconnectedMessagesEnabled = true,
NatPunchEnabled = true
};

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)
{
localIp = result.Ips[1];
}
int port = result.Port;
NetClient.Start();

if (string.IsNullOrEmpty(ip))
if (FikaBackendUtils.IsHostNatPunch)
{
_logger.LogError("IP was empty when pinging!");
return false;
}
NetClient.NatPunchModule.Init(this);

if (port == default)
{
_logger.LogError("Port was empty when pinging!");
return false;
}
string natPunchServerIP = FikaPlugin.Instance.NatPunchServerIP;
int natPunchServerPort = FikaPlugin.Instance.NatPunchServerPort;
string token = $"client:{serverId}";

remoteEndPoint = new(IPAddress.Parse(ip), port);
if (!string.IsNullOrEmpty(localIp))
{
localEndPoint = new(IPAddress.Parse(localIp), port);
}
NetClient.NatPunchModule.SendNatIntroduceRequest(natPunchServerIP, natPunchServerPort, token);

if (FikaBackendUtils.IsHostNatPunch)
{
natPunchRequestTask = Task.Run(() => NatPunchRequest(serverId));
_logger.LogInfo($"SendNatIntroduceRequest: {natPunchServerIP}:{natPunchServerPort}");
}
else
{
string ip = result.Ips[0];
string localIp = null;
if (result.Ips.Length > 1)
{
localIp = result.Ips[1];
}
int port = result.Port;

NetClient.Start(localPort);
if (string.IsNullOrEmpty(ip))
{
_logger.LogError("IP was empty when pinging!");
return false;
}

if (port == default)
{
_logger.LogError("Port was empty when pinging!");
return false;
}

remoteEndPoint = new(IPAddress.Parse(ip), port);
if (!string.IsNullOrEmpty(localIp))
{
localEndPoint = new(IPAddress.Parse(localIp), port);
}
}

return true;
}
Expand All @@ -79,57 +87,26 @@ public void PingEndPoint(string message)
NetDataWriter writer = new();
writer.Put(message);

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

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

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

if (!fikaNatPunchClient.Connected)
if (localEndPoint != null)
{
_logger.LogError("Unable to connect to NatPunchRelayService.");
return;
NetClient.SendUnconnectedMessage(writer, localEndPoint);
}

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());
_keepAliveRoutine = StartCoroutine(KeepAlive());
}

public void StopKeepAliveRoutine()
{
if(keepAliveRoutine != null)
if(_keepAliveRoutine != null)
{
StopCoroutine(keepAliveRoutine);
StopCoroutine(_keepAliveRoutine);
}
}

Expand All @@ -139,6 +116,7 @@ public IEnumerator KeepAlive()
{
PingEndPoint("fika.keepalive");
NetClient.PollEvents();
NetClient.NatPunchModule.PollEvents();

yield return new WaitForSeconds(1.0f);
}
Expand Down Expand Up @@ -174,7 +152,7 @@ public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketRead
Received = true;
FikaBackendUtils.RemoteIp = remoteEndPoint.Address.ToString();
FikaBackendUtils.RemotePort = remoteEndPoint.Port;
FikaBackendUtils.LocalPort = localPort;
FikaBackendUtils.LocalPort = NetClient.LocalPort;
break;
case "fika.keepalive":
// Do nothing
Expand All @@ -199,5 +177,36 @@ public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
{
// Do nothing
}

public void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token)
{
// Do nothing
}

public void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token)
{
// Do nothing
}

public void OnNatIntroductionResponse(IPEndPoint natLocalEndPoint, IPEndPoint natRemoteEndPoint, string token)
{
_logger.LogInfo($"OnNatIntroductionResponse: {remoteEndPoint}");

localEndPoint = natLocalEndPoint;
remoteEndPoint = natRemoteEndPoint;

Task.Run(async () =>
{
NetDataWriter data = new();
data.Put("fika.hello");

for (int i = 0; i < 20; i++)
{
NetClient.SendUnconnectedMessage(data, localEndPoint);
NetClient.SendUnconnectedMessage(data, remoteEndPoint);
await Task.Delay(250);
}
});
}
}
}
Loading