Skip to content

Commit

Permalink
Merge pull request #76 from project-fika/natpunch-2.0-3.9-dev
Browse files Browse the repository at this point in the history
NAT Punch 2.0
  • Loading branch information
Lacyway authored Jul 5, 2024
2 parents 27222fb + 20623f8 commit ab4ef99
Show file tree
Hide file tree
Showing 44 changed files with 265 additions and 2,237 deletions.
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

0 comments on commit ab4ef99

Please sign in to comment.