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
|
||||
* kcp2k.KcpTransport
|
||||
* Mirror.SimpleWebTransport
|
||||
* MultiCompiled.KcpWebCombined
|
||||
|
||||
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
|
||||
// spawns at the point before shoulder rotation.
|
||||
#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
|
||||
|
||||
/// <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
|
||||
public bool statisticsLog;
|
||||
|
||||
void Awake()
|
||||
public override void Awake()
|
||||
{
|
||||
|
||||
KCPConfig conf = new KCPConfig();
|
||||
|
|
@ -169,7 +169,7 @@ namespace kcp2k
|
|||
public override string ServerGetClientAddress(int connectionId) => server.GetClientAddress(connectionId);
|
||||
public override void ServerStop() => server.Stop();
|
||||
|
||||
public void Update()
|
||||
public override void Update()
|
||||
{
|
||||
server.TickIncoming();
|
||||
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;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
public override void Awake()
|
||||
{
|
||||
Log.level = _logLevels;
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ namespace Mirror
|
|||
return builder.Uri;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public override void Update()
|
||||
{
|
||||
server?.ProcessMessageQueue();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace Mirror
|
|||
// let's only allocate it once.
|
||||
Func<bool> enabledCheck;
|
||||
|
||||
void Awake()
|
||||
public override void Awake()
|
||||
{
|
||||
TelepathyConfig conf = new TelepathyConfig();
|
||||
if (!File.Exists("TelepathyConfig.json"))
|
||||
|
|
@ -162,7 +162,7 @@ namespace Mirror
|
|||
}
|
||||
public override void ServerStop() => server.Stop();
|
||||
// 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
|
||||
// when LateUpdate already started.
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ namespace LightReflectiveMirror
|
|||
GUI.enabled = false;
|
||||
}
|
||||
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);
|
||||
|
||||
if (lrm.useLoadBalancer)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace LightReflectiveMirror
|
|||
// Connection/auth variables
|
||||
public Transport clientToServerTransport;
|
||||
public string serverIP = null;
|
||||
public ushort serverPort = 7777;
|
||||
public ushort endpointServerPort = 8080;
|
||||
public float heartBeatInterval = 3;
|
||||
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!");
|
||||
#endif
|
||||
|
||||
if (Application.platform == RuntimePlatform.WebGLPlayer)
|
||||
useNATPunch = false;
|
||||
else
|
||||
_directConnectModule = GetComponent<LRMDirectConnectModule>();
|
||||
|
||||
if (clientToServerTransport is LightReflectiveMirrorTransport)
|
||||
throw new Exception("Haha real funny... Use a different transport.");
|
||||
|
||||
_directConnectModule = GetComponent<LRMDirectConnectModule>();
|
||||
|
||||
if (_directConnectModule != null)
|
||||
{
|
||||
if (useNATPunch && !_directConnectModule.SupportsNATPunch())
|
||||
|
|
@ -71,7 +74,7 @@ namespace LightReflectiveMirror
|
|||
{
|
||||
if (!_connectedToRelay)
|
||||
{
|
||||
Connect(serverIP);
|
||||
Connect(serverIP, serverPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -102,7 +105,6 @@ namespace LightReflectiveMirror
|
|||
throw new Exception("LRM | Client to Server Transport cannot be LRM.");
|
||||
|
||||
SetTransportPort(port);
|
||||
|
||||
|
||||
this.serverIP = serverIP;
|
||||
serverStatus = "Connecting to relay...";
|
||||
|
|
|
|||
Loading…
Reference in a new issue