Client-Side NAT punchtrough code
This commit is contained in:
parent
c5d2220b76
commit
2732aa90a1
5 changed files with 841 additions and 22 deletions
|
|
@ -33,6 +33,11 @@ namespace LightReflectiveMirror
|
||||||
firstToSecond.Remove(first);
|
firstToSecond.Remove(first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ICollection<TFirst> GetAllKeys()
|
||||||
|
{
|
||||||
|
return secondToFirst.Values;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetBySecond(TSecond second, out TFirst first)
|
public bool TryGetBySecond(TSecond second, out TFirst first)
|
||||||
{
|
{
|
||||||
return secondToFirst.TryGetValue(second, out first);
|
return secondToFirst.TryGetValue(second, out first);
|
||||||
|
|
|
||||||
230
UnityTransport/HelpAttribute.cs
Normal file
230
UnityTransport/HelpAttribute.cs
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||||
|
/// <copyright file="HelpAttribute.cs">
|
||||||
|
/// <See cref="https://github.com/johnearnshaw/unity-inspector-help"></See>
|
||||||
|
/// Copyright (c) 2017, John Earnshaw, reblGreen Software Limited
|
||||||
|
/// <See cref="https://github.com/johnearnshaw/"></See>
|
||||||
|
/// <See cref="https://bitbucket.com/juanshaf/"></See>
|
||||||
|
/// <See cref="https://reblgreen.com/"></See>
|
||||||
|
/// All rights reserved.
|
||||||
|
/// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
/// permitted provided that the following conditions are met:
|
||||||
|
/// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
/// conditions and the following disclaimer.
|
||||||
|
/// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
/// of conditions and the following disclaimer in the documentation and/or other materials
|
||||||
|
/// provided with the distribution.
|
||||||
|
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
/// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
/// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE
|
||||||
|
/// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
/// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
/// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
/// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
/// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
/// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
/// </copyright>
|
||||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
|
||||||
|
public class HelpAttribute : PropertyAttribute
|
||||||
|
{
|
||||||
|
public readonly string text;
|
||||||
|
|
||||||
|
// MessageType exists in UnityEditor namespace and can throw an exception when used outside the editor.
|
||||||
|
// We spoof MessageType at the bottom of this script to ensure that errors are not thrown when
|
||||||
|
// MessageType is unavailable.
|
||||||
|
public readonly MessageType type;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a HelpBox to the Unity property inspector above this field.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The help text to be displayed in the HelpBox.</param>
|
||||||
|
/// <param name="type">The icon to be displayed in the HelpBox.</param>
|
||||||
|
public HelpAttribute(string text, MessageType type = MessageType.Info)
|
||||||
|
{
|
||||||
|
this.text = text;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[CustomPropertyDrawer(typeof(HelpAttribute))]
|
||||||
|
public class HelpDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
// Used for top and bottom padding between the text and the HelpBox border.
|
||||||
|
const int paddingHeight = 8;
|
||||||
|
|
||||||
|
// Used to add some margin between the the HelpBox and the property.
|
||||||
|
const int marginHeight = 2;
|
||||||
|
|
||||||
|
// Global field to store the original (base) property height.
|
||||||
|
float baseHeight = 0;
|
||||||
|
|
||||||
|
// Custom added height for drawing text area which has the MultilineAttribute.
|
||||||
|
float addedHeight = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A wrapper which returns the PropertyDrawer.attribute field as a HelpAttribute.
|
||||||
|
/// </summary>
|
||||||
|
HelpAttribute helpAttribute { get { return (HelpAttribute)attribute; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A helper property to check for RangeAttribute.
|
||||||
|
/// </summary>
|
||||||
|
RangeAttribute rangeAttribute
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var attributes = fieldInfo.GetCustomAttributes(typeof(RangeAttribute), true);
|
||||||
|
return attributes != null && attributes.Length > 0 ? (RangeAttribute)attributes[0] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A helper property to check for MultiLineAttribute.
|
||||||
|
/// </summary>
|
||||||
|
MultilineAttribute multilineAttribute
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var attributes = fieldInfo.GetCustomAttributes(typeof(MultilineAttribute), true);
|
||||||
|
return attributes != null && attributes.Length > 0 ? (MultilineAttribute)attributes[0] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override float GetPropertyHeight(SerializedProperty prop, GUIContent label)
|
||||||
|
{
|
||||||
|
// We store the original property height for later use...
|
||||||
|
baseHeight = base.GetPropertyHeight(prop, label);
|
||||||
|
|
||||||
|
// This stops icon shrinking if text content doesn't fill out the container enough.
|
||||||
|
float minHeight = paddingHeight * 5;
|
||||||
|
|
||||||
|
// Calculate the height of the HelpBox using the GUIStyle on the current skin and the inspector
|
||||||
|
// window's currentViewWidth.
|
||||||
|
var content = new GUIContent(helpAttribute.text);
|
||||||
|
var style = GUI.skin.GetStyle("helpbox");
|
||||||
|
|
||||||
|
var height = style.CalcHeight(content, EditorGUIUtility.currentViewWidth);
|
||||||
|
|
||||||
|
// We add tiny padding here to make sure the text is not overflowing the HelpBox from the top
|
||||||
|
// and bottom.
|
||||||
|
height += marginHeight * 2;
|
||||||
|
|
||||||
|
// Since we draw a custom text area with the label above if our property contains the
|
||||||
|
// MultilineAttribute, we need to add some extra height to compensate. This is stored in a
|
||||||
|
// seperate global field so we can use it again later.
|
||||||
|
if (multilineAttribute != null && prop.propertyType == SerializedPropertyType.String)
|
||||||
|
{
|
||||||
|
addedHeight = 48f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the calculated HelpBox is less than our minimum height we use this to calculate the returned
|
||||||
|
// height instead.
|
||||||
|
return height > minHeight ? height + baseHeight + addedHeight : minHeight + baseHeight + addedHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
|
||||||
|
{
|
||||||
|
// We get a local reference to the MultilineAttribute as we use it twice in this method and it
|
||||||
|
// saves calling the logic twice for minimal optimization, etc...
|
||||||
|
var multiline = multilineAttribute;
|
||||||
|
|
||||||
|
EditorGUI.BeginProperty(position, label, prop);
|
||||||
|
|
||||||
|
// Copy the position out so we can calculate the position of our HelpBox without affecting the
|
||||||
|
// original position.
|
||||||
|
var helpPos = position;
|
||||||
|
|
||||||
|
helpPos.height -= baseHeight + marginHeight;
|
||||||
|
|
||||||
|
|
||||||
|
if (multiline != null)
|
||||||
|
{
|
||||||
|
helpPos.height -= addedHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renders the HelpBox in the Unity inspector UI.
|
||||||
|
EditorGUI.HelpBox(helpPos, helpAttribute.text, helpAttribute.type);
|
||||||
|
|
||||||
|
position.y += helpPos.height + marginHeight;
|
||||||
|
position.height = baseHeight;
|
||||||
|
|
||||||
|
|
||||||
|
// If we have a RangeAttribute on our field, we need to handle the PropertyDrawer differently to
|
||||||
|
// keep the same style as Unity's default.
|
||||||
|
var range = rangeAttribute;
|
||||||
|
|
||||||
|
if (range != null)
|
||||||
|
{
|
||||||
|
if (prop.propertyType == SerializedPropertyType.Float)
|
||||||
|
{
|
||||||
|
EditorGUI.Slider(position, prop, range.min, range.max, label);
|
||||||
|
}
|
||||||
|
else if (prop.propertyType == SerializedPropertyType.Integer)
|
||||||
|
{
|
||||||
|
EditorGUI.IntSlider(position, prop, (int)range.min, (int)range.max, label);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not numeric so draw standard property field as punishment for adding RangeAttribute to
|
||||||
|
// a property which can not have a range :P
|
||||||
|
EditorGUI.PropertyField(position, prop, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (multiline != null)
|
||||||
|
{
|
||||||
|
// Here's where we handle the PropertyDrawer differently if we have a MultiLineAttribute, to try
|
||||||
|
// and keep some kind of multiline text area. This is not identical to Unity's default but is
|
||||||
|
// better than nothing...
|
||||||
|
if (prop.propertyType == SerializedPropertyType.String)
|
||||||
|
{
|
||||||
|
var style = GUI.skin.label;
|
||||||
|
var size = style.CalcHeight(label, EditorGUIUtility.currentViewWidth);
|
||||||
|
|
||||||
|
EditorGUI.LabelField(position, label);
|
||||||
|
|
||||||
|
position.y += size;
|
||||||
|
position.height += addedHeight - size;
|
||||||
|
|
||||||
|
// Fixed text dissappearing thanks to: http://answers.unity3d.com/questions/244043/textarea-does-not-work-text-dissapears-solution-is.html
|
||||||
|
prop.stringValue = EditorGUI.TextArea(position, prop.stringValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Again with a MultilineAttribute on a non-text field deserves for the standard property field
|
||||||
|
// to be drawn as punishment :P
|
||||||
|
EditorGUI.PropertyField(position, prop, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we get to here it means we're drawing the default property field below the HelpBox. More custom
|
||||||
|
// and built in PropertyDrawers could be implemented to enable HelpBox but it could easily make for
|
||||||
|
// hefty else/if block which would need refactoring!
|
||||||
|
EditorGUI.PropertyField(position, prop, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Replicate MessageType Enum if we are not in editor as this enum exists in UnityEditor namespace.
|
||||||
|
// This should stop errors being logged the same as Shawn Featherly's commit in the Github repo but I
|
||||||
|
// feel is cleaner than having the conditional directive in the middle of the HelpAttribute constructor.
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
#endif
|
||||||
159
UnityTransport/LRMDirectConnectModule.cs
Normal file
159
UnityTransport/LRMDirectConnectModule.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
// This is an optional module for adding direct connect support
|
||||||
|
|
||||||
|
using Mirror;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using LightReflectiveMirror;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(LightReflectiveMirrorTransport))]
|
||||||
|
public class LRMDirectConnectModule : MonoBehaviour
|
||||||
|
{
|
||||||
|
public Transport directConnectTransport;
|
||||||
|
public bool showDebugLogs;
|
||||||
|
private LightReflectiveMirrorTransport lightMirrorTransport;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
lightMirrorTransport = GetComponent<LightReflectiveMirrorTransport>();
|
||||||
|
|
||||||
|
if (directConnectTransport == null)
|
||||||
|
{
|
||||||
|
Debug.Log("Direct Connect Transport is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (directConnectTransport is LightReflectiveMirrorTransport)
|
||||||
|
{
|
||||||
|
Debug.Log("Direct Connect Transport Cannot be the relay, silly. :P");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
directConnectTransport.OnServerConnected = (OnServerConnected);
|
||||||
|
directConnectTransport.OnServerDataReceived = (OnServerDataReceived);
|
||||||
|
directConnectTransport.OnServerDisconnected = (OnServerDisconnected);
|
||||||
|
directConnectTransport.OnServerError = (OnServerError);
|
||||||
|
directConnectTransport.OnClientConnected = (OnClientConnected);
|
||||||
|
directConnectTransport.OnClientDataReceived = (OnClientDataReceived);
|
||||||
|
directConnectTransport.OnClientDisconnected = (OnClientDisconnected);
|
||||||
|
directConnectTransport.OnClientError = (OnClientError);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartServer(int port)
|
||||||
|
{
|
||||||
|
if(port > 0)
|
||||||
|
SetTransportPort(port);
|
||||||
|
|
||||||
|
directConnectTransport.ServerStart();
|
||||||
|
if (showDebugLogs)
|
||||||
|
Debug.Log("Direct Connect Server Created!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopServer()
|
||||||
|
{
|
||||||
|
directConnectTransport.ServerStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JoinServer(string ip, int port)
|
||||||
|
{
|
||||||
|
if(SupportsNATPunch())
|
||||||
|
SetTransportPort(port);
|
||||||
|
directConnectTransport.ClientConnect(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTransportPort(int port)
|
||||||
|
{
|
||||||
|
if (directConnectTransport is kcp2k.KcpTransport kcpTransport)
|
||||||
|
kcpTransport.Port = (ushort)port;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("DIRECT CONNECT MODULE ONLY SUPPORTS KCP AT THE MOMENT.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTransportPort()
|
||||||
|
{
|
||||||
|
if (directConnectTransport is kcp2k.KcpTransport kcpTransport)
|
||||||
|
return kcpTransport.Port;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("DIRECT CONNECT MODULE ONLY SUPPORTS KCP AT THE MOMENT.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsNATPunch()
|
||||||
|
{
|
||||||
|
return directConnectTransport is kcp2k.KcpTransport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool KickClient(int clientID)
|
||||||
|
{
|
||||||
|
if (showDebugLogs)
|
||||||
|
Debug.Log("Kicked direct connect client.");
|
||||||
|
return directConnectTransport.ServerDisconnect(clientID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClientDisconnect()
|
||||||
|
{
|
||||||
|
directConnectTransport.ClientDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ServerSend(int clientID, ArraySegment<byte> data, int channel)
|
||||||
|
{
|
||||||
|
directConnectTransport.ServerSend(clientID, channel, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClientSend(ArraySegment<byte> data, int channel)
|
||||||
|
{
|
||||||
|
directConnectTransport.ClientSend(channel, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Transport Callbacks
|
||||||
|
void OnServerConnected(int clientID)
|
||||||
|
{
|
||||||
|
if (showDebugLogs)
|
||||||
|
Debug.Log("Direct Connect Client Connected");
|
||||||
|
lightMirrorTransport.DirectAddClient(clientID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnServerDataReceived(int clientID, ArraySegment<byte> data, int channel)
|
||||||
|
{
|
||||||
|
lightMirrorTransport.DirectReceiveData(data, channel, clientID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnServerDisconnected(int clientID)
|
||||||
|
{
|
||||||
|
lightMirrorTransport.DirectRemoveClient(clientID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnServerError(int client, Exception error)
|
||||||
|
{
|
||||||
|
if (showDebugLogs)
|
||||||
|
Debug.Log("Direct Server Error: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnClientConnected()
|
||||||
|
{
|
||||||
|
if (showDebugLogs)
|
||||||
|
Debug.Log("Direct Connect Client Joined");
|
||||||
|
|
||||||
|
lightMirrorTransport.DirectClientConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnClientDisconnected()
|
||||||
|
{
|
||||||
|
lightMirrorTransport.DirectDisconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnClientDataReceived(ArraySegment<byte> data, int channel)
|
||||||
|
{
|
||||||
|
lightMirrorTransport.DirectReceiveData(data, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnClientError(Exception error)
|
||||||
|
{
|
||||||
|
if (showDebugLogs)
|
||||||
|
Debug.Log("Direct Client Error: " + error);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,8 @@ using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
using UnityEngine.Networking;
|
using UnityEngine.Networking;
|
||||||
|
|
@ -21,6 +23,10 @@ namespace LightReflectiveMirror
|
||||||
public bool connectOnAwake = true;
|
public bool connectOnAwake = true;
|
||||||
public string authenticationKey = "Secret Auth Key";
|
public string authenticationKey = "Secret Auth Key";
|
||||||
public UnityEvent diconnectedFromRelay;
|
public UnityEvent diconnectedFromRelay;
|
||||||
|
[Header("NAT Punchthrough")]
|
||||||
|
[Help("NAT Punchthrough will require the Direct Connect module attached.")]
|
||||||
|
public bool useNATPunch = true;
|
||||||
|
public ushort NATPunchtroughPort = 7776;
|
||||||
[Header("Server Hosting Data")]
|
[Header("Server Hosting Data")]
|
||||||
public string serverName = "My awesome server!";
|
public string serverName = "My awesome server!";
|
||||||
public string extraServerData = "Map 1";
|
public string extraServerData = "Map 1";
|
||||||
|
|
@ -32,31 +38,137 @@ namespace LightReflectiveMirror
|
||||||
[Header("Server Information")]
|
[Header("Server Information")]
|
||||||
public int serverId = -1;
|
public int serverId = -1;
|
||||||
|
|
||||||
|
private LRMDirectConnectModule _directConnectModule;
|
||||||
|
|
||||||
private byte[] _clientSendBuffer;
|
private byte[] _clientSendBuffer;
|
||||||
private bool _connectedToRelay = false;
|
private bool _connectedToRelay = false;
|
||||||
private bool _isClient = false;
|
private bool _isClient = false;
|
||||||
private bool _isServer = false;
|
private bool _isServer = false;
|
||||||
|
private bool _directConnected = false;
|
||||||
private bool _isAuthenticated = false;
|
private bool _isAuthenticated = false;
|
||||||
private int _currentMemberId;
|
private int _currentMemberId;
|
||||||
private bool _callbacksInitialized = false;
|
private bool _callbacksInitialized = false;
|
||||||
|
private int _cachedHostID;
|
||||||
private BiDictionary<int, int> _connectedRelayClients = new BiDictionary<int, int>();
|
private BiDictionary<int, int> _connectedRelayClients = new BiDictionary<int, int>();
|
||||||
|
private BiDictionary<int, int> _connectedDirectClients = new BiDictionary<int, int>();
|
||||||
|
private UdpClient _NATPuncher;
|
||||||
|
private IPEndPoint _NATIP;
|
||||||
|
private IPEndPoint _relayPuncherIP;
|
||||||
|
private byte[] _punchData = new byte[1] { 1 };
|
||||||
|
private IPEndPoint _directConnectEndpoint;
|
||||||
|
private SocketProxy _clientProxy;
|
||||||
|
private BiDictionary<IPEndPoint, SocketProxy> _serverProxies = new BiDictionary<IPEndPoint, SocketProxy>();
|
||||||
|
|
||||||
public override bool ClientConnected() => _isClient;
|
public override bool ClientConnected() => _isClient;
|
||||||
private void OnConnectedToRelay() => _connectedToRelay = true;
|
private void OnConnectedToRelay() => _connectedToRelay = true;
|
||||||
public bool IsAuthenticated() => _isAuthenticated;
|
public bool IsAuthenticated() => _isAuthenticated;
|
||||||
public override bool ServerActive() => _isServer;
|
public override bool ServerActive() => _isServer;
|
||||||
public override bool Available() => _connectedToRelay;
|
public override bool Available() => _connectedToRelay;
|
||||||
public override void ClientEarlyUpdate() => clientToServerTransport.ClientEarlyUpdate();
|
|
||||||
public override void ClientLateUpdate() => clientToServerTransport.ClientLateUpdate();
|
|
||||||
public override void ClientConnect(Uri uri) => ClientConnect(uri.Host);
|
public override void ClientConnect(Uri uri) => ClientConnect(uri.Host);
|
||||||
public override int GetMaxPacketSize(int channelId = 0) => clientToServerTransport.GetMaxPacketSize(channelId);
|
public override int GetMaxPacketSize(int channelId = 0) => clientToServerTransport.GetMaxPacketSize(channelId);
|
||||||
public override string ServerGetClientAddress(int connectionId) => _connectedRelayClients.GetBySecond(connectionId).ToString();
|
public override string ServerGetClientAddress(int connectionId) {
|
||||||
|
if (_connectedRelayClients.TryGetBySecond(connectionId, out int relayId))
|
||||||
|
return relayId.ToString();
|
||||||
|
|
||||||
|
if (_connectedDirectClients.TryGetBySecond(connectionId, out int directId))
|
||||||
|
return "DIRECT-" + directId;
|
||||||
|
|
||||||
|
// Shouldn't ever get here.
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClientEarlyUpdate()
|
||||||
|
{
|
||||||
|
clientToServerTransport.ClientEarlyUpdate();
|
||||||
|
|
||||||
|
if (_directConnectModule != null)
|
||||||
|
_directConnectModule.directConnectTransport.ClientEarlyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClientLateUpdate()
|
||||||
|
{
|
||||||
|
clientToServerTransport.ClientLateUpdate();
|
||||||
|
|
||||||
|
if (_directConnectModule != null)
|
||||||
|
_directConnectModule.directConnectTransport.ClientLateUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ServerEarlyUpdate()
|
||||||
|
{
|
||||||
|
if (_directConnectModule != null)
|
||||||
|
_directConnectModule.directConnectTransport.ServerEarlyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecvData(IAsyncResult result)
|
||||||
|
{
|
||||||
|
IPEndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
|
||||||
|
var data = _NATPuncher.EndReceive(result, ref newClientEP);
|
||||||
|
|
||||||
|
if (!newClientEP.Address.Equals(_relayPuncherIP.Address))
|
||||||
|
{
|
||||||
|
if (_isServer)
|
||||||
|
{
|
||||||
|
if(_serverProxies.TryGetByFirst(newClientEP, out SocketProxy foundProxy))
|
||||||
|
{
|
||||||
|
if (data.Length > 2)
|
||||||
|
foundProxy.RelayData(data, data.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_serverProxies.Add(newClientEP, new SocketProxy(_NATIP.Port + 1, newClientEP));
|
||||||
|
_serverProxies.GetByFirst(newClientEP).dataReceived += ServerProcessProxyData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isClient)
|
||||||
|
{
|
||||||
|
if(_clientProxy == null)
|
||||||
|
{
|
||||||
|
_clientProxy = new SocketProxy(_NATIP.Port - 1);
|
||||||
|
_clientProxy.dataReceived += ClientProcessProxyData;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_clientProxy.ClientRelayData(data, data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_NATPuncher.BeginReceive(new AsyncCallback(RecvData), _NATPuncher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerProcessProxyData(IPEndPoint remoteEndpoint, byte[] data)
|
||||||
|
{
|
||||||
|
_NATPuncher.Send(data, data.Length, remoteEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientProcessProxyData(IPEndPoint _, byte[] data)
|
||||||
|
{
|
||||||
|
_NATPuncher.Send(data, data.Length, _directConnectEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ServerLateUpdate()
|
||||||
|
{
|
||||||
|
if (_directConnectModule != null)
|
||||||
|
_directConnectModule.directConnectTransport.ServerLateUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
if (clientToServerTransport is LightReflectiveMirrorTransport)
|
if (clientToServerTransport is LightReflectiveMirrorTransport)
|
||||||
throw new Exception("Haha real funny... Use a different transport.");
|
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();
|
SetupCallbacks();
|
||||||
|
|
||||||
if (connectOnAwake)
|
if (connectOnAwake)
|
||||||
|
|
@ -104,6 +216,20 @@ namespace LightReflectiveMirror
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
_clientSendBuffer.WriteByte(ref pos, 200);
|
_clientSendBuffer.WriteByte(ref pos, 200);
|
||||||
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,6 +241,15 @@ namespace LightReflectiveMirror
|
||||||
Debug.Log("You must be connected to Relay to request server list!");
|
Debug.Log("You must be connected to Relay to request server list!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IEnumerator NATPunch(IPEndPoint remoteAddress)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
_NATPuncher.Send(_punchData, 1, remoteAddress);
|
||||||
|
yield return new WaitForSeconds(0.25f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DataReceived(ArraySegment<byte> segmentData, int channel)
|
void DataReceived(ArraySegment<byte> segmentData, int channel)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -136,7 +271,10 @@ namespace LightReflectiveMirror
|
||||||
var recvData = data.ReadBytes(ref pos);
|
var recvData = data.ReadBytes(ref pos);
|
||||||
|
|
||||||
if (_isServer)
|
if (_isServer)
|
||||||
OnServerDataReceived?.Invoke(_connectedRelayClients.GetByFirst(data.ReadInt(ref pos)), new ArraySegment<byte>(recvData), channel);
|
{
|
||||||
|
if(_connectedRelayClients.TryGetByFirst(data.ReadInt(ref pos), out int clientID))
|
||||||
|
OnServerDataReceived?.Invoke(clientID, new ArraySegment<byte>(recvData), channel);
|
||||||
|
}
|
||||||
|
|
||||||
if (_isClient)
|
if (_isClient)
|
||||||
OnClientDataReceived?.Invoke(new ArraySegment<byte>(recvData), channel);
|
OnClientDataReceived?.Invoke(new ArraySegment<byte>(recvData), channel);
|
||||||
|
|
@ -152,9 +290,12 @@ namespace LightReflectiveMirror
|
||||||
if (_isServer)
|
if (_isServer)
|
||||||
{
|
{
|
||||||
int user = data.ReadInt(ref pos);
|
int user = data.ReadInt(ref pos);
|
||||||
OnServerDisconnected?.Invoke(_connectedRelayClients.GetByFirst(user));
|
if (_connectedRelayClients.TryGetByFirst(user, out int clientID))
|
||||||
|
{
|
||||||
|
OnServerDisconnected?.Invoke(_connectedRelayClients.GetByFirst(clientID));
|
||||||
_connectedRelayClients.Remove(user);
|
_connectedRelayClients.Remove(user);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case OpCodes.RoomCreated:
|
case OpCodes.RoomCreated:
|
||||||
serverId = data.ReadInt(ref pos);
|
serverId = data.ReadInt(ref pos);
|
||||||
|
|
@ -172,14 +313,64 @@ namespace LightReflectiveMirror
|
||||||
_currentMemberId++;
|
_currentMemberId++;
|
||||||
}
|
}
|
||||||
break;
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
_NATPuncher = new UdpClient();
|
||||||
|
_NATPuncher.ExclusiveAddressUse = false;
|
||||||
|
_NATIP = new IPEndPoint(IPAddress.Parse(GetLocalIp()), UnityEngine.Random.Range(16000, 17000));
|
||||||
|
_NATPuncher.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||||
|
_NATPuncher.Client.Bind(_NATIP);
|
||||||
|
_relayPuncherIP = new IPEndPoint(IPAddress.Parse(serverIP), NATPunchtroughPort);
|
||||||
|
|
||||||
|
byte[] initalData = new byte[150];
|
||||||
|
int sendPos = 0;
|
||||||
|
|
||||||
|
initalData.WriteBool(ref sendPos, true);
|
||||||
|
initalData.WriteString(ref sendPos, data.ReadString(ref pos));
|
||||||
|
|
||||||
|
// 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 { }
|
catch(Exception e) { print(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator GetServerList()
|
IEnumerator GetServerList()
|
||||||
{
|
{
|
||||||
Uri uri = new Uri($"http://{serverIP}:{endpointServerPort}/api/servers");
|
string uri = $"http://{serverIP}:{endpointServerPort}/api/servers";
|
||||||
|
|
||||||
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
|
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
|
||||||
{
|
{
|
||||||
|
|
@ -187,6 +378,7 @@ namespace LightReflectiveMirror
|
||||||
yield return webRequest.SendWebRequest();
|
yield return webRequest.SendWebRequest();
|
||||||
var result = webRequest.downloadHandler.text;
|
var result = webRequest.downloadHandler.text;
|
||||||
|
|
||||||
|
#if UNITY_2020_1_OR_NEWER
|
||||||
switch (webRequest.result)
|
switch (webRequest.result)
|
||||||
{
|
{
|
||||||
case UnityWebRequest.Result.ConnectionError:
|
case UnityWebRequest.Result.ConnectionError:
|
||||||
|
|
@ -209,6 +401,25 @@ namespace LightReflectiveMirror
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (webRequest.isNetworkError || webRequest.isHttpError)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("LRM | Network Error while retreiving the server list!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (result == "Access Denied")
|
||||||
|
{
|
||||||
|
Debug.LogWarning("LRM | Server list request denied. Make sure you enable 'EndpointServerList' in server config!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
relayServerList?.Clear();
|
||||||
|
relayServerList = JsonConvert.DeserializeObject<List<RelayServerInfo>>(result);
|
||||||
|
serverListUpdated?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,8 +477,7 @@ namespace LightReflectiveMirror
|
||||||
|
|
||||||
public override void ClientConnect(string address)
|
public override void ClientConnect(string address)
|
||||||
{
|
{
|
||||||
int hostId = 0;
|
if (!Available() || !int.TryParse(address, out _cachedHostID))
|
||||||
if (!Available() || !int.TryParse(address, out hostId))
|
|
||||||
{
|
{
|
||||||
Debug.Log("Not connected to relay or invalid server id!");
|
Debug.Log("Not connected to relay or invalid server id!");
|
||||||
OnClientDisconnected?.Invoke();
|
OnClientDisconnected?.Invoke();
|
||||||
|
|
@ -278,8 +488,15 @@ namespace LightReflectiveMirror
|
||||||
throw new Exception("Cannot connect while hosting/already connected!");
|
throw new Exception("Cannot connect while hosting/already connected!");
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
_directConnected = false;
|
||||||
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.JoinServer);
|
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.JoinServer);
|
||||||
_clientSendBuffer.WriteInt(ref pos, hostId);
|
_clientSendBuffer.WriteInt(ref pos, _cachedHostID);
|
||||||
|
_clientSendBuffer.WriteBool(ref pos, _directConnectModule != null);
|
||||||
|
|
||||||
|
if (GetLocalIp() == null)
|
||||||
|
_clientSendBuffer.WriteString(ref pos, "0.0.0.0");
|
||||||
|
else
|
||||||
|
_clientSendBuffer.WriteString(ref pos, GetLocalIp());
|
||||||
|
|
||||||
_isClient = true;
|
_isClient = true;
|
||||||
|
|
||||||
|
|
@ -294,9 +511,18 @@ namespace LightReflectiveMirror
|
||||||
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.LeaveRoom);
|
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.LeaveRoom);
|
||||||
|
|
||||||
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
||||||
|
|
||||||
|
if (_directConnectModule != null)
|
||||||
|
_directConnectModule.ClientDisconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ClientSend(int channelId, ArraySegment<byte> segment)
|
public override void ClientSend(int channelId, ArraySegment<byte> segment)
|
||||||
|
{
|
||||||
|
if (_directConnected)
|
||||||
|
{
|
||||||
|
_directConnectModule.ClientSend(segment, channelId);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.SendData);
|
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.SendData);
|
||||||
|
|
@ -305,6 +531,7 @@ namespace LightReflectiveMirror
|
||||||
|
|
||||||
clientToServerTransport.ClientSend(channelId, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
clientToServerTransport.ClientSend(channelId, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool ServerDisconnect(int connectionId)
|
public override bool ServerDisconnect(int connectionId)
|
||||||
{
|
{
|
||||||
|
|
@ -316,10 +543,19 @@ namespace LightReflectiveMirror
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_connectedDirectClients.TryGetBySecond(connectionId, out int directId))
|
||||||
|
return _directConnectModule.KickClient(directId);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ServerSend(int connectionId, int channelId, ArraySegment<byte> segment)
|
public override void ServerSend(int connectionId, int channelId, ArraySegment<byte> segment)
|
||||||
|
{
|
||||||
|
if (_directConnectModule != null && _connectedDirectClients.TryGetBySecond(connectionId, out int directId))
|
||||||
|
{
|
||||||
|
_directConnectModule.ServerSend(directId, segment, channelId);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.SendData);
|
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.SendData);
|
||||||
|
|
@ -328,6 +564,7 @@ namespace LightReflectiveMirror
|
||||||
|
|
||||||
clientToServerTransport.ClientSend(channelId, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
clientToServerTransport.ClientSend(channelId, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void ServerStart()
|
public override void ServerStart()
|
||||||
{
|
{
|
||||||
|
|
@ -346,6 +583,15 @@ namespace LightReflectiveMirror
|
||||||
_isServer = true;
|
_isServer = true;
|
||||||
_connectedRelayClients = new BiDictionary<int, int>();
|
_connectedRelayClients = new BiDictionary<int, int>();
|
||||||
_currentMemberId = 1;
|
_currentMemberId = 1;
|
||||||
|
_connectedDirectClients = new BiDictionary<int, int>();
|
||||||
|
|
||||||
|
var keys = new List<IPEndPoint>(_serverProxies.GetAllKeys());
|
||||||
|
|
||||||
|
for(int i = 0; i < keys.Count; i++)
|
||||||
|
{
|
||||||
|
_serverProxies.GetByFirst(keys[i]).Dispose();
|
||||||
|
_serverProxies.Remove(keys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.CreateRoom);
|
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.CreateRoom);
|
||||||
|
|
@ -353,6 +599,28 @@ namespace LightReflectiveMirror
|
||||||
_clientSendBuffer.WriteString(ref pos, serverName);
|
_clientSendBuffer.WriteString(ref pos, serverName);
|
||||||
_clientSendBuffer.WriteBool(ref pos, isPublicServer);
|
_clientSendBuffer.WriteBool(ref pos, isPublicServer);
|
||||||
_clientSendBuffer.WriteString(ref pos, extraServerData);
|
_clientSendBuffer.WriteString(ref pos, extraServerData);
|
||||||
|
// If we have direct connect module, and our local IP isnt null, tell server. Only time local IP is null is on cellular networks, such as IOS and Android.
|
||||||
|
_clientSendBuffer.WriteBool(ref pos, _directConnectModule != null ? GetLocalIp() != null ? true : false : false);
|
||||||
|
|
||||||
|
if (_directConnectModule != null && GetLocalIp() != null)
|
||||||
|
{
|
||||||
|
_clientSendBuffer.WriteString(ref pos, GetLocalIp());
|
||||||
|
// Transport port will be NAT port + 1 for the proxy connections.
|
||||||
|
_directConnectModule.StartServer(useNATPunch ? _NATIP.Port + 1 : -1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_clientSendBuffer.WriteString(ref pos, "0.0.0.0");
|
||||||
|
|
||||||
|
if (useNATPunch)
|
||||||
|
{
|
||||||
|
_clientSendBuffer.WriteBool(ref pos, true);
|
||||||
|
_clientSendBuffer.WriteInt(ref pos, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_clientSendBuffer.WriteBool(ref pos, false);
|
||||||
|
_clientSendBuffer.WriteInt(ref pos, _directConnectModule == null ? 1 : _directConnectModule.SupportsNATPunch() ? _directConnectModule.GetTransportPort() : 1);
|
||||||
|
}
|
||||||
|
|
||||||
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
||||||
}
|
}
|
||||||
|
|
@ -366,6 +634,17 @@ namespace LightReflectiveMirror
|
||||||
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.LeaveRoom);
|
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.LeaveRoom);
|
||||||
|
|
||||||
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
clientToServerTransport.ClientSend(0, new ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
||||||
|
|
||||||
|
if (_directConnectModule != null)
|
||||||
|
_directConnectModule.StopServer();
|
||||||
|
|
||||||
|
var keys = new List<IPEndPoint>(_serverProxies.GetAllKeys());
|
||||||
|
|
||||||
|
for (int i = 0; i < keys.Count; i++)
|
||||||
|
{
|
||||||
|
_serverProxies.GetByFirst(keys[i]).Dispose();
|
||||||
|
_serverProxies.Remove(keys[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,8 +671,87 @@ namespace LightReflectiveMirror
|
||||||
public enum OpCodes
|
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,
|
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, RequestServers = 15, ServerListReponse = 16, Authenticated = 17, UpdateRoomData = 18, ServerConnectionData = 19
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Direct Connect Module
|
||||||
|
public void DirectAddClient(int clientID)
|
||||||
|
{
|
||||||
|
if (!_isServer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_connectedDirectClients.Add(clientID, _currentMemberId);
|
||||||
|
OnServerConnected?.Invoke(_currentMemberId);
|
||||||
|
_currentMemberId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DirectRemoveClient(int clientID)
|
||||||
|
{
|
||||||
|
if (!_isServer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnServerDisconnected?.Invoke(_connectedDirectClients.GetByFirst(clientID));
|
||||||
|
_connectedDirectClients.Remove(clientID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DirectReceiveData(ArraySegment<byte> data, int channel, int clientID = -1)
|
||||||
|
{
|
||||||
|
if (_isServer)
|
||||||
|
OnServerDataReceived?.Invoke(_connectedDirectClients.GetByFirst(clientID), data, channel);
|
||||||
|
|
||||||
|
if (_isClient)
|
||||||
|
OnClientDataReceived?.Invoke(data, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DirectClientConnected()
|
||||||
|
{
|
||||||
|
_directConnected = true;
|
||||||
|
OnClientConnected?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DirectDisconnected()
|
||||||
|
{
|
||||||
|
if (_directConnected)
|
||||||
|
{
|
||||||
|
_isClient = false;
|
||||||
|
_directConnected = false;
|
||||||
|
OnClientDisconnected?.Invoke();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
_directConnected = false;
|
||||||
|
_clientSendBuffer.WriteByte(ref pos, (byte)OpCodes.JoinServer);
|
||||||
|
_clientSendBuffer.WriteInt(ref pos, _cachedHostID);
|
||||||
|
_clientSendBuffer.WriteBool(ref pos, false); // Direct failed, use relay
|
||||||
|
|
||||||
|
_isClient = true;
|
||||||
|
|
||||||
|
clientToServerTransport.ClientSend(0, new System.ArraySegment<byte>(_clientSendBuffer, 0, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_clientProxy != null)
|
||||||
|
{
|
||||||
|
_clientProxy.Dispose();
|
||||||
|
_clientProxy = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|
|
||||||
67
UnityTransport/SocketProxy.cs
Normal file
67
UnityTransport/SocketProxy.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace LightReflectiveMirror
|
||||||
|
{
|
||||||
|
|
||||||
|
// This class handles the proxying from punched socket to transport.
|
||||||
|
public class SocketProxy
|
||||||
|
{
|
||||||
|
public DateTime lastInteractionTime;
|
||||||
|
public Action<IPEndPoint, byte[]> dataReceived;
|
||||||
|
UdpClient _udpClient;
|
||||||
|
IPEndPoint _recvEndpoint = new IPEndPoint(IPAddress.Any, 0);
|
||||||
|
IPEndPoint _remoteEndpoint;
|
||||||
|
bool _clientInitialRecv = false;
|
||||||
|
|
||||||
|
public SocketProxy(int port, IPEndPoint remoteEndpoint)
|
||||||
|
{
|
||||||
|
_udpClient = new UdpClient();
|
||||||
|
_udpClient.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||||
|
_udpClient.BeginReceive(new AsyncCallback(RecvData), _udpClient);
|
||||||
|
lastInteractionTime = DateTime.Now;
|
||||||
|
// Clone it so when main socket recvies new data, it wont switcheroo on us.
|
||||||
|
_remoteEndpoint = new IPEndPoint(remoteEndpoint.Address, remoteEndpoint.Port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketProxy(int port)
|
||||||
|
{
|
||||||
|
_udpClient = new UdpClient(port);
|
||||||
|
_udpClient.BeginReceive(new AsyncCallback(RecvData), _udpClient);
|
||||||
|
lastInteractionTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RelayData(byte[] data, int length)
|
||||||
|
{
|
||||||
|
_udpClient.Send(data, length);
|
||||||
|
lastInteractionTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClientRelayData(byte[] data, int length)
|
||||||
|
{
|
||||||
|
if (_clientInitialRecv)
|
||||||
|
{
|
||||||
|
_udpClient.Send(data, length, _recvEndpoint);
|
||||||
|
lastInteractionTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_udpClient.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecvData(IAsyncResult result)
|
||||||
|
{
|
||||||
|
_clientInitialRecv = true;
|
||||||
|
byte[] data = _udpClient.EndReceive(result, ref _recvEndpoint);
|
||||||
|
lastInteractionTime = DateTime.Now;
|
||||||
|
dataReceived?.Invoke(_remoteEndpoint, data);
|
||||||
|
_udpClient.BeginReceive(new AsyncCallback(RecvData), _udpClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue