fishbait/UnityTransport/LRMTransport/LightReflectiveMirrorTransport.cs
2021-04-06 00:57:45 -05:00

391 lines
14 KiB
C#

using kcp2k;
using Mirror;
using Mirror.SimpleWeb;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
namespace LightReflectiveMirror
{
[DefaultExecutionOrder(1001)]
public partial class LightReflectiveMirrorTransport : Transport
{
private void OnConnectedToRelay() => _connectedToRelay = true;
public bool IsAuthenticated() => _isAuthenticated;
private void Awake()
{
if (clientToServerTransport is LightReflectiveMirrorTransport)
throw new Exception("Haha real funny... Use a different transport.");
_directConnectModule = GetComponent<LRMDirectConnectModule>();
if (_directConnectModule != null)
{
if (useNATPunch && !_directConnectModule.SupportsNATPunch())
{
Debug.LogWarning("LRM | NATPunch is turned on but the transport used does not support it. It will be disabled.");
useNATPunch = false;
}
}
SetupCallbacks();
if (connectOnAwake)
ConnectToRelay();
InvokeRepeating(nameof(SendHeartbeat), heartBeatInterval, heartBeatInterval);
}
private void SetupCallbacks()
{
if (_callbacksInitialized)
return;
_callbacksInitialized = true;
clientToServerTransport.OnClientConnected = OnConnectedToRelay;
clientToServerTransport.OnClientDataReceived = DataReceived;
clientToServerTransport.OnClientDisconnected = Disconnected;
}
void Disconnected()
{
_connectedToRelay = false;
_isAuthenticated = false;
diconnectedFromRelay?.Invoke();
}
public void ConnectToRelay()
{
if (!useLoadBalancer)
{
if (!_connectedToRelay)
{
Connect(serverIP);
}
else
{
Debug.LogWarning("LRM | Already connected to relay!");
}
}
else
{
if (!_connectedToRelay)
{
StartCoroutine(RelayConnect());
}
else
{
Debug.LogWarning("LRM | Already connected to relay!");
}
}
}
/// <summary>
/// Connects to the desired relay
/// </summary>
/// <param name="serverIP"></param>
private void Connect(string serverIP, ushort port = 7777)
{
// need to implement custom port
if (clientToServerTransport is LightReflectiveMirrorTransport)
throw new Exception("LRM | Client to Server Transport cannot be LRM.");
SetTransportPort(port);
this.serverIP = serverIP;
_clientSendBuffer = new byte[clientToServerTransport.GetMaxPacketSize()];
clientToServerTransport.ClientConnect(serverIP);
}
private void DisconnectFromRelay()
{
if (IsAuthenticated())
{
clientToServerTransport.ClientDisconnect();
}
}
void SendHeartbeat()
{
if (_connectedToRelay)
{
int pos = 0;
_clientSendBuffer.WriteByte(ref pos, 200);
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
if (_NATPuncher != null)
_NATPuncher.Send(new byte[] { 0 }, 1, _relayPuncherIP);
var keys = new List<IPEndPoint>(_serverProxies.GetAllKeys());
for (int i = 0; i < keys.Count; i++)
{
if (DateTime.Now.Subtract(_serverProxies.GetByFirst(keys[i]).lastInteractionTime).TotalSeconds > 10)
{
_serverProxies.GetByFirst(keys[i]).Dispose();
_serverProxies.Remove(keys[i]);
}
}
}
}
void DataReceived(ArraySegment<byte> segmentData, int channel)
{
try
{
var data = segmentData.Array;
int pos = segmentData.Offset;
OpCodes opcode = (OpCodes)data.ReadByte(ref pos);
switch (opcode)
{
case OpCodes.Authenticated:
_isAuthenticated = true;
break;
case OpCodes.AuthenticationRequest:
SendAuthKey();
break;
case OpCodes.GetData:
var recvData = data.ReadBytes(ref pos);
if (_isServer)
{
if (_connectedRelayClients.TryGetByFirst(data.ReadInt(ref pos), out int clientID))
OnServerDataReceived?.Invoke(clientID, new ArraySegment<byte>(recvData), channel);
}
if (_isClient)
OnClientDataReceived?.Invoke(new ArraySegment<byte>(recvData), channel);
break;
case OpCodes.ServerLeft:
if (_isClient)
{
_isClient = false;
OnClientDisconnected?.Invoke();
}
break;
case OpCodes.PlayerDisconnected:
if (_isServer)
{
int user = data.ReadInt(ref pos);
if (_connectedRelayClients.TryGetByFirst(user, out int clientID))
{
OnServerDisconnected?.Invoke(clientID);
_connectedRelayClients.Remove(user);
}
}
break;
case OpCodes.RoomCreated:
serverId = data.ReadInt(ref pos);
break;
case OpCodes.ServerJoined:
int clientId = data.ReadInt(ref pos);
if (_isClient)
{
OnClientConnected?.Invoke();
}
if (_isServer)
{
_connectedRelayClients.Add(clientId, _currentMemberId);
OnServerConnected?.Invoke(_currentMemberId);
_currentMemberId++;
}
break;
case OpCodes.DirectConnectIP:
var ip = data.ReadString(ref pos);
int port = data.ReadInt(ref pos);
bool attemptNatPunch = data.ReadBool(ref pos);
_directConnectEndpoint = new IPEndPoint(IPAddress.Parse(ip), port);
if (useNATPunch && attemptNatPunch)
{
StartCoroutine(NATPunch(_directConnectEndpoint));
}
if (!_isServer)
{
if (_clientProxy == null && useNATPunch && attemptNatPunch)
{
_clientProxy = new SocketProxy(_NATIP.Port - 1);
_clientProxy.dataReceived += ClientProcessProxyData;
}
if (useNATPunch && attemptNatPunch)
_directConnectModule.JoinServer("127.0.0.1", _NATIP.Port - 1);
else
_directConnectModule.JoinServer(ip, port);
}
break;
case OpCodes.RequestNATConnection:
if (GetLocalIp() != null && _directConnectModule != null)
{
byte[] initalData = new byte[150];
int sendPos = 0;
initalData.WriteBool(ref sendPos, true);
initalData.WriteString(ref sendPos, data.ReadString(ref pos));
NATPunchtroughPort = data.ReadInt(ref pos);
if (_NATPuncher == null)
{
_NATPuncher = new UdpClient { ExclusiveAddressUse = false };
while (true)
{
try
{
_NATIP = new IPEndPoint(IPAddress.Parse(GetLocalIp()), UnityEngine.Random.Range(16000, 17000));
_NATPuncher.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_NATPuncher.Client.Bind(_NATIP);
break;
}
catch {} // Binding port is in use, keep trying :P
}
}
IPAddress serverAddr;
if (!IPAddress.TryParse(serverIP, out serverAddr))
serverAddr = Dns.GetHostEntry(serverIP).AddressList[0];
_relayPuncherIP = new IPEndPoint(IPAddress.Parse(serverIP), NATPunchtroughPort);
// Send 3 to lower chance of it being dropped or corrupted when received on server.
_NATPuncher.Send(initalData, sendPos, _relayPuncherIP);
_NATPuncher.Send(initalData, sendPos, _relayPuncherIP);
_NATPuncher.Send(initalData, sendPos, _relayPuncherIP);
_NATPuncher.BeginReceive(new AsyncCallback(RecvData), _NATPuncher);
}
break;
}
}
catch (Exception e) { print(e); }
}
public void SetTransportPort(ushort port)
{
if (clientToServerTransport is KcpTransport kcp)
kcp.Port = port;
if (clientToServerTransport is TelepathyTransport telepathy)
telepathy.port = port;
if (clientToServerTransport is SimpleWebTransport swt)
swt.port = port;
}
public void UpdateRoomInfo(string newServerName = null, string newServerData = null, bool? newServerIsPublic = null, int? newPlayerCap = null)
{
if (_isServer)
{
int pos = 0;
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.UpdateRoomData);
if (!string.IsNullOrEmpty(newServerName))
{
_clientSendBuffer.WriteBool(ref pos, true);
_clientSendBuffer.WriteString(ref pos, newServerName);
}
else
_clientSendBuffer.WriteBool(ref pos, false);
if (!string.IsNullOrEmpty(newServerData))
{
_clientSendBuffer.WriteBool(ref pos, true);
_clientSendBuffer.WriteString(ref pos, newServerData);
}
else
_clientSendBuffer.WriteBool(ref pos, false);
if (newServerIsPublic != null)
{
_clientSendBuffer.WriteBool(ref pos, true);
_clientSendBuffer.WriteBool(ref pos, newServerIsPublic.Value);
}
else
_clientSendBuffer.WriteBool(ref pos, false);
if (newPlayerCap != null)
{
_clientSendBuffer.WriteBool(ref pos, true);
_clientSendBuffer.WriteInt(ref pos, newPlayerCap.Value);
}
else
_clientSendBuffer.WriteBool(ref pos, false);
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
}
}
Room GetServerForID(int serverID)
{
for(int i = 0; i < relayServerList.Count; i++)
{
if(relayServerList[i].serverId == serverId)
return relayServerList[i];
}
throw new Exception("LRM | An attempt was made to connect to a server which does not exist!");
}
void SendAuthKey()
{
int pos = 0;
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.AuthenticationResponse);
_clientSendBuffer.WriteString(ref pos, authenticationKey);
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
}
public enum OpCodes
{
Default = 0, RequestID = 1, JoinServer = 2, SendData = 3, GetID = 4, ServerJoined = 5, GetData = 6, CreateRoom = 7, ServerLeft = 8, PlayerDisconnected = 9, RoomCreated = 10,
LeaveRoom = 11, KickPlayer = 12, AuthenticationRequest = 13, AuthenticationResponse = 14, Authenticated = 17, UpdateRoomData = 18, ServerConnectionData = 19, RequestNATConnection = 20,
DirectConnectIP = 21
}
private static string GetLocalIp()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
return null;
}
}
[Serializable]
public struct Room
{
public string serverName;
public int currentPlayers;
public int maxPlayers;
public int serverId;
public string serverData;
public RelayAddress relayInfo;
}
[Serializable]
public struct RelayAddress
{
public ushort Port;
public ushort EndpointPort;
public string Address;
}
}