Added KcpWebCombined, Disabled NAT/Direct on webgl, exposed node port
This commit is contained in:
parent
011f562e63
commit
d7d7b3fe9e
10 changed files with 342 additions and 11 deletions
|
|
@ -93,6 +93,7 @@ To switch between them you have the following options:
|
||||||
* Mirror.TelepathyTransport
|
* Mirror.TelepathyTransport
|
||||||
* kcp2k.KcpTransport
|
* kcp2k.KcpTransport
|
||||||
* Mirror.SimpleWebTransport
|
* Mirror.SimpleWebTransport
|
||||||
|
* MultiCompiled.KcpWebCombined
|
||||||
|
|
||||||
AuthenticationKey - This is the key the clients need to have on their inspector. It cannot be blank.
|
AuthenticationKey - This is the key the clients need to have on their inspector. It cannot be blank.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,9 @@ namespace Mirror
|
||||||
// ShoulderRotation.LateUpdate, resulting in projectile
|
// ShoulderRotation.LateUpdate, resulting in projectile
|
||||||
// spawns at the point before shoulder rotation.
|
// spawns at the point before shoulder rotation.
|
||||||
#pragma warning disable UNT0001 // Empty Unity message
|
#pragma warning disable UNT0001 // Empty Unity message
|
||||||
public void Update() { }
|
public abstract void Update();
|
||||||
|
|
||||||
|
public abstract void Awake();
|
||||||
#pragma warning restore UNT0001 // Empty Unity message
|
#pragma warning restore UNT0001 // Empty Unity message
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// Source: https://stackoverflow.com/questions/255341/getting-multiple-keys-of-specified-value-of-a-generic-dictionary#255630
|
||||||
|
//
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LightReflectiveMirror
|
||||||
|
{
|
||||||
|
class BiDictionary<TFirst, TSecond>
|
||||||
|
{
|
||||||
|
IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
|
||||||
|
IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
|
||||||
|
|
||||||
|
public void Add(TFirst first, TSecond second)
|
||||||
|
{
|
||||||
|
if (firstToSecond.ContainsKey(first) ||
|
||||||
|
secondToFirst.ContainsKey(second))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Duplicate first or second");
|
||||||
|
}
|
||||||
|
firstToSecond.Add(first, second);
|
||||||
|
secondToFirst.Add(second, first);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetByFirst(TFirst first, out TSecond second)
|
||||||
|
{
|
||||||
|
return firstToSecond.TryGetValue(first, out second);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(TFirst first)
|
||||||
|
{
|
||||||
|
secondToFirst.Remove(firstToSecond[first]);
|
||||||
|
firstToSecond.Remove(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<TFirst> GetAllKeys()
|
||||||
|
{
|
||||||
|
return secondToFirst.Values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetBySecond(TSecond second, out TFirst first)
|
||||||
|
{
|
||||||
|
return secondToFirst.TryGetValue(second, out first);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TSecond GetByFirst(TFirst first)
|
||||||
|
{
|
||||||
|
return firstToSecond[first];
|
||||||
|
}
|
||||||
|
|
||||||
|
public TFirst GetBySecond(TSecond second)
|
||||||
|
{
|
||||||
|
return secondToFirst[second];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ namespace kcp2k
|
||||||
// log statistics for headless servers that can't show them in GUI
|
// log statistics for headless servers that can't show them in GUI
|
||||||
public bool statisticsLog;
|
public bool statisticsLog;
|
||||||
|
|
||||||
void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
|
|
||||||
KCPConfig conf = new KCPConfig();
|
KCPConfig conf = new KCPConfig();
|
||||||
|
|
@ -169,7 +169,7 @@ namespace kcp2k
|
||||||
public override string ServerGetClientAddress(int connectionId) => server.GetClientAddress(connectionId);
|
public override string ServerGetClientAddress(int connectionId) => server.GetClientAddress(connectionId);
|
||||||
public override void ServerStop() => server.Stop();
|
public override void ServerStop() => server.Stop();
|
||||||
|
|
||||||
public void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
server.TickIncoming();
|
server.TickIncoming();
|
||||||
server.TickOutgoing();
|
server.TickOutgoing();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
using kcp2k;
|
||||||
|
using Mirror;
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MultiCompiled
|
||||||
|
{
|
||||||
|
|
||||||
|
public class KcpWebCombined : Transport
|
||||||
|
{
|
||||||
|
public Transport[] transports;
|
||||||
|
|
||||||
|
Transport available;
|
||||||
|
|
||||||
|
public override void Awake()
|
||||||
|
{
|
||||||
|
transports = new Transport[2] { new KcpTransport(), new SimpleWebTransport() };
|
||||||
|
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
transport.Awake();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
transport.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Available()
|
||||||
|
{
|
||||||
|
// available if any of the transports is available
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
if (transport.Available())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Client
|
||||||
|
|
||||||
|
public override void ClientConnect(string address)
|
||||||
|
{
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
if (transport.Available())
|
||||||
|
{
|
||||||
|
available = transport;
|
||||||
|
transport.OnClientConnected = OnClientConnected;
|
||||||
|
transport.OnClientDataReceived = OnClientDataReceived;
|
||||||
|
transport.OnClientError = OnClientError;
|
||||||
|
transport.OnClientDisconnected = OnClientDisconnected;
|
||||||
|
transport.ClientConnect(address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ArgumentException("No transport suitable for this platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClientConnect(Uri uri)
|
||||||
|
{
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
if (transport.Available())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
available = transport;
|
||||||
|
transport.OnClientConnected = OnClientConnected;
|
||||||
|
transport.OnClientDataReceived = OnClientDataReceived;
|
||||||
|
transport.OnClientError = OnClientError;
|
||||||
|
transport.OnClientDisconnected = OnClientDisconnected;
|
||||||
|
transport.ClientConnect(uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
// transport does not support the schema, just move on to the next one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ArgumentException("No transport suitable for this platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ClientConnected()
|
||||||
|
{
|
||||||
|
return (object)available != null && available.ClientConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClientDisconnect()
|
||||||
|
{
|
||||||
|
if ((object)available != null)
|
||||||
|
available.ClientDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClientSend(int channelId, ArraySegment<byte> segment)
|
||||||
|
{
|
||||||
|
available.ClientSend(channelId, segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Server
|
||||||
|
// connection ids get mapped to base transports
|
||||||
|
// if we have 3 transports, then
|
||||||
|
// transport 0 will produce connection ids [0, 3, 6, 9, ...]
|
||||||
|
// transport 1 will produce connection ids [1, 4, 7, 10, ...]
|
||||||
|
// transport 2 will produce connection ids [2, 5, 8, 11, ...]
|
||||||
|
int FromBaseId(int transportId, int connectionId)
|
||||||
|
{
|
||||||
|
return connectionId * transports.Length + transportId;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ToBaseId(int connectionId)
|
||||||
|
{
|
||||||
|
return connectionId / transports.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ToTransportId(int connectionId)
|
||||||
|
{
|
||||||
|
return connectionId % transports.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddServerCallbacks()
|
||||||
|
{
|
||||||
|
// wire all the base transports to my events
|
||||||
|
for (int i = 0; i < transports.Length; i++)
|
||||||
|
{
|
||||||
|
// this is required for the handlers, if I use i directly
|
||||||
|
// then all the handlers will use the last i
|
||||||
|
int locali = i;
|
||||||
|
Transport transport = transports[i];
|
||||||
|
|
||||||
|
transport.OnServerConnected = (baseConnectionId =>
|
||||||
|
{
|
||||||
|
OnServerConnected.Invoke(FromBaseId(locali, baseConnectionId));
|
||||||
|
});
|
||||||
|
|
||||||
|
transport.OnServerDataReceived = (baseConnectionId, data, channel) =>
|
||||||
|
{
|
||||||
|
OnServerDataReceived.Invoke(FromBaseId(locali, baseConnectionId), data, channel);
|
||||||
|
};
|
||||||
|
|
||||||
|
transport.OnServerError = (baseConnectionId, error) =>
|
||||||
|
{
|
||||||
|
OnServerError.Invoke(FromBaseId(locali, baseConnectionId), error);
|
||||||
|
};
|
||||||
|
transport.OnServerDisconnected = baseConnectionId =>
|
||||||
|
{
|
||||||
|
OnServerDisconnected.Invoke(FromBaseId(locali, baseConnectionId));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for now returns the first uri,
|
||||||
|
// should we return all available uris?
|
||||||
|
public override Uri ServerUri()
|
||||||
|
{
|
||||||
|
return transports[0].ServerUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool ServerActive()
|
||||||
|
{
|
||||||
|
// avoid Linq.All allocations
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
if (!transport.ServerActive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ServerGetClientAddress(int connectionId)
|
||||||
|
{
|
||||||
|
int baseConnectionId = ToBaseId(connectionId);
|
||||||
|
int transportId = ToTransportId(connectionId);
|
||||||
|
return transports[transportId].ServerGetClientAddress(baseConnectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ServerDisconnect(int connectionId)
|
||||||
|
{
|
||||||
|
int baseConnectionId = ToBaseId(connectionId);
|
||||||
|
int transportId = ToTransportId(connectionId);
|
||||||
|
return transports[transportId].ServerDisconnect(baseConnectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ServerSend(int connectionId, int channelId, ArraySegment<byte> segment)
|
||||||
|
{
|
||||||
|
int baseConnectionId = ToBaseId(connectionId);
|
||||||
|
int transportId = ToTransportId(connectionId);
|
||||||
|
|
||||||
|
for (int i = 0; i < transports.Length; ++i)
|
||||||
|
{
|
||||||
|
if (i == transportId)
|
||||||
|
{
|
||||||
|
transports[i].ServerSend(baseConnectionId, channelId, segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ServerStart(ushort port)
|
||||||
|
{
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
AddServerCallbacks();
|
||||||
|
transport.ServerStart(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ServerStop()
|
||||||
|
{
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
transport.ServerStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override int GetMaxPacketSize(int channelId = 0)
|
||||||
|
{
|
||||||
|
// finding the max packet size in a multiplex environment has to be
|
||||||
|
// done very carefully:
|
||||||
|
// * servers run multiple transports at the same time
|
||||||
|
// * different clients run different transports
|
||||||
|
// * there should only ever be ONE true max packet size for everyone,
|
||||||
|
// otherwise a spawn message might be sent to all tcp sockets, but
|
||||||
|
// be too big for some udp sockets. that would be a debugging
|
||||||
|
// nightmare and allow for possible exploits and players on
|
||||||
|
// different platforms seeing a different game state.
|
||||||
|
// => the safest solution is to use the smallest max size for all
|
||||||
|
// transports. that will never fail.
|
||||||
|
int mininumAllowedSize = int.MaxValue;
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
int size = transport.GetMaxPacketSize(channelId);
|
||||||
|
mininumAllowedSize = Math.Min(size, mininumAllowedSize);
|
||||||
|
}
|
||||||
|
return mininumAllowedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
transport.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
foreach (Transport transport in transports)
|
||||||
|
{
|
||||||
|
builder.AppendLine(transport.ToString());
|
||||||
|
}
|
||||||
|
return builder.ToString().Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -78,7 +78,7 @@ namespace Mirror
|
||||||
return maxMessageSize;
|
return maxMessageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
Log.level = _logLevels;
|
Log.level = _logLevels;
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ namespace Mirror
|
||||||
return builder.Uri;
|
return builder.Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
server?.ProcessMessageQueue();
|
server?.ProcessMessageQueue();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ namespace Mirror
|
||||||
// let's only allocate it once.
|
// let's only allocate it once.
|
||||||
Func<bool> enabledCheck;
|
Func<bool> enabledCheck;
|
||||||
|
|
||||||
void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
TelepathyConfig conf = new TelepathyConfig();
|
TelepathyConfig conf = new TelepathyConfig();
|
||||||
if (!File.Exists("TelepathyConfig.json"))
|
if (!File.Exists("TelepathyConfig.json"))
|
||||||
|
|
@ -162,7 +162,7 @@ namespace Mirror
|
||||||
}
|
}
|
||||||
public override void ServerStop() => server.Stop();
|
public override void ServerStop() => server.Stop();
|
||||||
// messages should always be processed in early update
|
// messages should always be processed in early update
|
||||||
public void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
// note: we need to check enabled in case we set it to false
|
// note: we need to check enabled in case we set it to false
|
||||||
// when LateUpdate already started.
|
// when LateUpdate already started.
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,7 @@ namespace LightReflectiveMirror
|
||||||
GUI.enabled = false;
|
GUI.enabled = false;
|
||||||
}
|
}
|
||||||
lrm.serverIP = EditorGUILayout.TextField("LRM Node IP", lrm.serverIP);
|
lrm.serverIP = EditorGUILayout.TextField("LRM Node IP", lrm.serverIP);
|
||||||
|
lrm.serverPort = (ushort)Mathf.Clamp(EditorGUILayout.IntField("LRM Node Port", lrm.serverPort), ushort.MinValue, ushort.MaxValue);
|
||||||
lrm.endpointServerPort = (ushort)Mathf.Clamp(EditorGUILayout.IntField("Endpoint Port", lrm.endpointServerPort), ushort.MinValue, ushort.MaxValue);
|
lrm.endpointServerPort = (ushort)Mathf.Clamp(EditorGUILayout.IntField("Endpoint Port", lrm.endpointServerPort), ushort.MinValue, ushort.MaxValue);
|
||||||
|
|
||||||
if (lrm.useLoadBalancer)
|
if (lrm.useLoadBalancer)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ namespace LightReflectiveMirror
|
||||||
// Connection/auth variables
|
// Connection/auth variables
|
||||||
public Transport clientToServerTransport;
|
public Transport clientToServerTransport;
|
||||||
public string serverIP = null;
|
public string serverIP = null;
|
||||||
|
public ushort serverPort = 7777;
|
||||||
public ushort endpointServerPort = 8080;
|
public ushort endpointServerPort = 8080;
|
||||||
public float heartBeatInterval = 3;
|
public float heartBeatInterval = 3;
|
||||||
public bool connectOnAwake = true;
|
public bool connectOnAwake = true;
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,14 @@ namespace LightReflectiveMirror
|
||||||
throw new Exception("LRM | Please switch to .NET 4.x for LRM to function properly!");
|
throw new Exception("LRM | Please switch to .NET 4.x for LRM to function properly!");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (Application.platform == RuntimePlatform.WebGLPlayer)
|
||||||
|
useNATPunch = false;
|
||||||
|
else
|
||||||
|
_directConnectModule = GetComponent<LRMDirectConnectModule>();
|
||||||
|
|
||||||
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 (_directConnectModule != null)
|
||||||
{
|
{
|
||||||
if (useNATPunch && !_directConnectModule.SupportsNATPunch())
|
if (useNATPunch && !_directConnectModule.SupportsNATPunch())
|
||||||
|
|
@ -71,7 +74,7 @@ namespace LightReflectiveMirror
|
||||||
{
|
{
|
||||||
if (!_connectedToRelay)
|
if (!_connectedToRelay)
|
||||||
{
|
{
|
||||||
Connect(serverIP);
|
Connect(serverIP, serverPort);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -103,7 +106,6 @@ namespace LightReflectiveMirror
|
||||||
|
|
||||||
SetTransportPort(port);
|
SetTransportPort(port);
|
||||||
|
|
||||||
|
|
||||||
this.serverIP = serverIP;
|
this.serverIP = serverIP;
|
||||||
serverStatus = "Connecting to relay...";
|
serverStatus = "Connecting to relay...";
|
||||||
_clientSendBuffer = new byte[clientToServerTransport.GetMaxPacketSize()];
|
_clientSendBuffer = new byte[clientToServerTransport.GetMaxPacketSize()];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue