Allow for sending message on unreliable channels (#98)

* Allow for sending message on unreliable channels

* Add channels to commands and rpcs

* Revert file formatting

* make channel id default parameter
This commit is contained in:
Paul Pacheco 2018-11-01 13:07:57 -05:00 committed by vis2k
parent 0a16b189c6
commit 724ebc00d8
11 changed files with 82 additions and 55 deletions

View File

@ -18,21 +18,25 @@ public class SyncVarAttribute : Attribute
[AttributeUsage(AttributeTargets.Method)]
public class CommandAttribute : Attribute
{
public int channel = Channels.DefaultReliable; // this is zero
}
[AttributeUsage(AttributeTargets.Method)]
public class ClientRpcAttribute : Attribute
{
public int channel = Channels.DefaultReliable; // this is zero
}
[AttributeUsage(AttributeTargets.Method)]
public class TargetRpcAttribute : Attribute
{
public int channel = Channels.DefaultReliable; // this is zero
}
[AttributeUsage(AttributeTargets.Event)]
public class SyncEventAttribute : Attribute
{
public int channel = Channels.DefaultReliable; // this is zero
}
[AttributeUsage(AttributeTargets.Method)]

View File

@ -17,7 +17,7 @@ public ULocalConnectionToClient(LocalClient localClient)
m_LocalClient = localClient;
}
protected override bool SendBytes(byte[] bytes)
protected override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable)
{
m_LocalClient.InvokeBytesOnClient(bytes);
return true;
@ -33,7 +33,7 @@ public ULocalConnectionToServer()
address = "localServer";
}
protected override bool SendBytes(byte[] bytes)
protected override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable)
{
if (bytes.Length == 0)
{

View File

@ -74,7 +74,7 @@ protected void InitSyncObject(SyncObject syncObject)
// ----------------------------- Commands --------------------------------
[EditorBrowsable(EditorBrowsableState.Never)]
protected void SendCommandInternal(int cmdHash, NetworkWriter writer, string cmdName)
protected void SendCommandInternal(int cmdHash, NetworkWriter writer, int channelId, string cmdName)
{
// local players can always send commands, regardless of authority, other objects must have authority.
if (!(isLocalPlayer || hasAuthority))
@ -96,7 +96,7 @@ protected void SendCommandInternal(int cmdHash, NetworkWriter writer, string cmd
message.cmdHash = cmdHash;
message.payload = writer.ToArray();
ClientScene.readyConnection.Send((short)MsgType.Command, message);
ClientScene.readyConnection.Send((short)MsgType.Command, message, channelId);
}
[EditorBrowsable(EditorBrowsableState.Never)]
@ -108,7 +108,7 @@ public virtual bool InvokeCommand(int cmdHash, NetworkReader reader)
// ----------------------------- Client RPCs --------------------------------
[EditorBrowsable(EditorBrowsableState.Never)]
protected void SendRPCInternal(int rpcHash, NetworkWriter writer, string rpcName)
protected void SendRPCInternal(int rpcHash, NetworkWriter writer, int channelId, string rpcName)
{
// This cannot use NetworkServer.active, as that is not specific to this object.
if (!isServer)
@ -124,11 +124,11 @@ protected void SendRPCInternal(int rpcHash, NetworkWriter writer, string rpcName
message.rpcHash = rpcHash;
message.payload = writer.ToArray();
NetworkServer.SendToReady(gameObject, (short)MsgType.Rpc, message);
NetworkServer.SendToReady(gameObject, (short)MsgType.Rpc, message, channelId);
}
[EditorBrowsable(EditorBrowsableState.Never)]
protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, NetworkWriter writer, string rpcName)
protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, NetworkWriter writer, int channelId, string rpcName)
{
// This cannot use NetworkServer.active, as that is not specific to this object.
if (!isServer)
@ -144,7 +144,7 @@ protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, Networ
message.rpcHash = rpcHash;
message.payload = writer.ToArray();
conn.Send((short)MsgType.Rpc, message);
conn.Send((short)MsgType.Rpc, message, channelId);
}
[EditorBrowsable(EditorBrowsableState.Never)]
@ -156,7 +156,7 @@ public virtual bool InvokeRPC(int cmdHash, NetworkReader reader)
// ----------------------------- Sync Events --------------------------------
[EditorBrowsable(EditorBrowsableState.Never)]
protected void SendEventInternal(int eventHash, NetworkWriter writer, string eventName)
protected void SendEventInternal(int eventHash, NetworkWriter writer, int channelId, string eventName)
{
if (!NetworkServer.active)
{
@ -171,7 +171,7 @@ protected void SendEventInternal(int eventHash, NetworkWriter writer, string eve
message.eventHash = eventHash;
message.payload = writer.ToArray();
NetworkServer.SendToReady(gameObject, (short)MsgType.SyncEvent, message);
NetworkServer.SendToReady(gameObject, (short)MsgType.SyncEvent, message, channelId);
}
[EditorBrowsable(EditorBrowsableState.Never)]

View File

@ -158,19 +158,19 @@ internal void RemovePlayerController()
m_PlayerController = null;
}
public virtual bool Send(short msgType, MessageBase msg)
public virtual bool Send(short msgType, MessageBase msg, int channelId = Channels.DefaultReliable)
{
NetworkWriter writer = new NetworkWriter();
msg.Serialize(writer);
// pack message and send
byte[] message = Protocol.PackMessage((ushort)msgType, writer.ToArray());
return SendBytes(message);
return SendBytes(message, channelId);
}
// protected because no one except NetworkConnection should ever send bytes directly to the client, as they
// would be detected as some kind of message. send messages instead.
protected virtual bool SendBytes(byte[] bytes)
protected virtual bool SendBytes( byte[] bytes, int channelId = Channels.DefaultReliable)
{
if (logNetworkMessages) { Debug.Log("ConnectionSend con:" + connectionId + " bytes:" + BitConverter.ToString(bytes)); }
@ -188,7 +188,7 @@ protected virtual bool SendBytes(byte[] bytes)
}
byte error;
return TransportSend(bytes, out error);
return TransportSend(channelId, bytes, out error);
}
// handle this message
@ -269,17 +269,17 @@ public virtual void TransportReceive(byte[] bytes)
HandleBytes(bytes);
}
public virtual bool TransportSend(byte[] bytes, out byte error)
public virtual bool TransportSend(int channelId, byte[] bytes, out byte error)
{
error = 0;
if (Transport.layer.ClientConnected())
{
Transport.layer.ClientSend(bytes);
Transport.layer.ClientSend(channelId, bytes);
return true;
}
else if (Transport.layer.ServerActive())
{
Transport.layer.ServerSend(connectionId, bytes);
Transport.layer.ServerSend(connectionId, channelId, bytes);
return true;
}
return false;

View File

@ -235,7 +235,7 @@ static bool SendToObservers(GameObject contextObj, short msgType, MessageBase ms
return false;
}
public static bool SendToAll(short msgType, MessageBase msg)
public static bool SendToAll(short msgType, MessageBase msg, int channelId = Channels.DefaultReliable)
{
if (LogFilter.Debug) { Debug.Log("Server.SendToAll id:" + msgType); }
@ -243,12 +243,12 @@ public static bool SendToAll(short msgType, MessageBase msg)
foreach (KeyValuePair<int, NetworkConnection> kvp in connections)
{
NetworkConnection conn = kvp.Value;
result &= conn.Send(msgType, msg);
result &= conn.Send(msgType, msg, channelId);
}
return result;
}
public static bool SendToReady(GameObject contextObj, short msgType, MessageBase msg)
public static bool SendToReady(GameObject contextObj, short msgType, MessageBase msg, int channelId = Channels.DefaultReliable)
{
if (LogFilter.Debug) { Debug.Log("Server.SendToReady msgType:" + msgType); }
@ -260,7 +260,7 @@ public static bool SendToReady(GameObject contextObj, short msgType, MessageBase
NetworkConnection conn = kvp.Value;
if (conn.isReady)
{
conn.Send(msgType, msg);
conn.Send(msgType, msg, channelId);
}
}
return true;
@ -274,7 +274,7 @@ public static bool SendToReady(GameObject contextObj, short msgType, MessageBase
{
if (kvp.Value.isReady)
{
result &= kvp.Value.Send(msgType, msg);
result &= kvp.Value.Send(msgType, msg, channelId);
}
}
return result;

View File

@ -65,7 +65,10 @@ public LLAPITransport(GlobalConfig globalConfig = null, ConnectionConfig connect
connectionConfig.BandwidthPeakFactor = 2;
connectionConfig.WebSocketReceiveBufferMaxSize = 0;
connectionConfig.UdpSocketReceiveBufferMaxSize = 0;
channelId = connectionConfig.AddChannel(QosType.ReliableFragmentedSequenced);
// channel 0 is reliable fragmented sequenced
connectionConfig.AddChannel(QosType.ReliableFragmentedSequenced);
// channel 1 is unreliable
connectionConfig.AddChannel(QosType.Unreliable);
}
this.connectionConfig = connectionConfig;
@ -96,7 +99,7 @@ public void ClientConnect(string address, int port)
}
}
public bool ClientSend(byte[] data)
public bool ClientSend(int channelId, byte[] data)
{
return NetworkTransport.Send(clientId, clientConnectionId, channelId, data, data.Length, out error);
}
@ -124,23 +127,21 @@ public bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] d
switch (networkEvent)
{
case NetworkEventType.Nothing:
return false;
case NetworkEventType.ConnectEvent:
transportEvent = TransportEvent.Connected;
break;
case NetworkEventType.DataEvent:
transportEvent = TransportEvent.Data;
data = new byte[receivedSize];
Array.Copy(clientReceiveBuffer, data, receivedSize);
break;
case NetworkEventType.DisconnectEvent:
transportEvent = TransportEvent.Disconnected;
break;
default:
return false;
}
// assign rest of the values and return true
data = new byte[receivedSize];
Array.Copy(clientReceiveBuffer, data, receivedSize);
//Debug.Log("LLAPITransport.ClientGetNextMessage: clientid=" + clientId + " connid=" + connectionId + " event=" + networkEvent + " data=" + BitConverter.ToString(data) + " error=" + error);
return true;
}
@ -173,7 +174,7 @@ public void ServerStartWebsockets(string address, int port, int maxConnections)
//Debug.Log("LLAPITransport.ServerStartWebsockets port=" + port + " max=" + maxConnections + " hostid=" + serverHostId);
}
public bool ServerSend(int connectionId, byte[] data)
public bool ServerSend(int connectionId, int channelId, byte[] data)
{
return NetworkTransport.Send(serverHostId, connectionId, channelId, data, data.Length, out error);
}
@ -201,30 +202,29 @@ public bool ServerGetNextMessage(out int connectionId, out TransportEvent transp
// LLAPI client sends keep alive messages (75-6C-6C) on channel=110.
// ignore all messages that aren't for our selected channel.
if (channel != channelId)
/*if (channel != channelId)
{
return false;
}
}*/
switch (networkEvent)
{
case NetworkEventType.Nothing:
return false;
case NetworkEventType.ConnectEvent:
transportEvent = TransportEvent.Connected;
break;
case NetworkEventType.DataEvent:
transportEvent = TransportEvent.Data;
data = new byte[receivedSize];
Array.Copy(serverReceiveBuffer, data, receivedSize);
break;
case NetworkEventType.DisconnectEvent:
transportEvent = TransportEvent.Disconnected;
break;
default:
// nothing or a message we don't recognize
return false;
}
// assign rest of the values and return true
data = new byte[receivedSize];
Array.Copy(serverReceiveBuffer, data, receivedSize);
//Debug.Log("LLAPITransport.ServerGetNextMessage: hostId=" + serverHostId + " event=" + networkEvent + "connid=" + connectionId + " channel=" + channel + " data=" + BitConverter.ToString(data) + " error=" + error);
return true;
}

View File

@ -25,7 +25,7 @@ public TelepathyTransport()
// client
public bool ClientConnected() { return client.Connected; }
public void ClientConnect(string address, int port) { client.Connect(address, port); }
public bool ClientSend(byte[] data) { return client.Send(data); }
public bool ClientSend(int channelId, byte[] data) { return client.Send(data); }
public bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] data)
{
Telepathy.Message message;
@ -59,7 +59,7 @@ public void ServerStartWebsockets(string address, int port, int maxConnections)
{
Debug.LogWarning("TelepathyTransport.ServerStartWebsockets not implemented yet!");
}
public bool ServerSend(int connectionId, byte[] data) { return server.Send(connectionId, data); }
public bool ServerSend(int connectionId, int channelId, byte[] data) { return server.Send(connectionId, data); }
public bool ServerGetNextMessage(out int connectionId, out TransportEvent transportEvent, out byte[] data)
{
Telepathy.Message message;

View File

@ -36,9 +36,9 @@ public void ClientConnect(string address, int port)
{
client.ClientConnect(address, port);
}
public bool ClientSend(byte[] data)
public bool ClientSend(int channelId, byte[] data)
{
return client.ClientSend(data);
return client.ClientSend(channelId, data);
}
public bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] data)
{
@ -81,9 +81,9 @@ public void ServerStartWebsockets(string address, int port, int maxConnections)
else Debug.LogWarning("ServerStartWebsockets can't be called in WebGL.");
}
public bool ServerSend(int connectionId, byte[] data)
public bool ServerSend(int connectionId, int channelId, byte[] data)
{
return server != null && server.ServerSend(connectionId, data);
return server != null && server.ServerSend(connectionId, channelId, data);
}
public bool ServerGetNextMessage(out int connectionId, out TransportEvent transportEvent, out byte[] data)

View File

@ -23,7 +23,7 @@ public interface TransportLayer
// client
bool ClientConnected();
void ClientConnect(string address, int port);
bool ClientSend(byte[] data);
bool ClientSend(int channelId, byte[] data);
bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] data);
void ClientDisconnect();
@ -31,7 +31,7 @@ public interface TransportLayer
bool ServerActive();
void ServerStart(string address, int port, int maxConnections);
void ServerStartWebsockets(string address, int port, int maxConnections);
bool ServerSend(int connectionId, byte[] data);
bool ServerSend(int connectionId, int channelId, byte[] data);
bool ServerGetNextMessage(out int connectionId, out TransportEvent transportEvent, out byte[] data);
bool ServerDisconnect(int connectionId);
bool GetConnectionInfo(int connectionId, out string address);

View File

@ -77,6 +77,12 @@ public enum Version
Current = 1
}
public class Channels
{
public const int DefaultReliable = 0;
public const int DefaultUnreliable = 1;
}
// network protocol all in one place, instead of constructing headers in all kinds of different places
//
// MsgType (1-n bytes)

View File

@ -501,6 +501,19 @@ void GenerateSerialization()
m_td.Methods.Add(serialize);
}
static int GetChannelId(CustomAttribute ca)
{
foreach (CustomAttributeNamedArgument customField in ca.Fields)
{
if (customField.Name == "channel")
{
return (int)customField.Argument.Value;
}
}
return 0;
}
// returns false for error, not for no-hook-exists
bool CheckForHookFunction(FieldDefinition syncVar, out MethodDefinition foundMethod)
{
@ -900,7 +913,7 @@ public void CallCmdThrust(float thrusting, int spin)
base.SendCommandInternal(ShipControl.kCmdCmdThrust, networkWriter, cmdName);
}
*/
MethodDefinition ProcessCommandCall(MethodDefinition md)
MethodDefinition ProcessCommandCall(MethodDefinition md, CustomAttribute ca)
{
MethodDefinition cmd = new MethodDefinition("Call" + md.Name, MethodAttributes.Public |
MethodAttributes.HideBySig,
@ -965,6 +978,7 @@ MethodDefinition ProcessCommandCall(MethodDefinition md)
cmdWorker.Append(cmdWorker.Create(OpCodes.Ldarg_0)); // load 'base.' to call the SendCommand function with
cmdWorker.Append(cmdWorker.Create(OpCodes.Ldsfld, cmdConstant)); // cmdHash
cmdWorker.Append(cmdWorker.Create(OpCodes.Ldloc_0)); // writer
cmdWorker.Append(cmdWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca)));
cmdWorker.Append(cmdWorker.Create(OpCodes.Ldstr, cmdName));
cmdWorker.Append(cmdWorker.Create(OpCodes.Call, Weaver.sendCommandInternal));
@ -1046,7 +1060,7 @@ public void CallTargetTest (NetworkConnection conn, int param)
}
}
*/
MethodDefinition ProcessTargetRpcCall(MethodDefinition md)
MethodDefinition ProcessTargetRpcCall(MethodDefinition md, CustomAttribute ca)
{
MethodDefinition rpc = new MethodDefinition("Call" + md.Name, MethodAttributes.Public |
MethodAttributes.HideBySig,
@ -1101,6 +1115,7 @@ MethodDefinition ProcessTargetRpcCall(MethodDefinition md)
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_1)); // connection
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldsfld, rpcConstant)); // rpcHash
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldloc_0)); // writer
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca)));
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldstr, rpcName));
rpcWorker.Append(rpcWorker.Create(OpCodes.Callvirt, Weaver.sendTargetRpcInternal));
@ -1121,7 +1136,7 @@ public void CallRpcTest (int param)
}
}
*/
MethodDefinition ProcessRpcCall(MethodDefinition md)
MethodDefinition ProcessRpcCall(MethodDefinition md, CustomAttribute ca)
{
MethodDefinition rpc = new MethodDefinition("Call" + md.Name, MethodAttributes.Public |
MethodAttributes.HideBySig,
@ -1163,6 +1178,7 @@ MethodDefinition ProcessRpcCall(MethodDefinition md)
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_0)); // this
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldsfld, rpcConstant)); // rpcHash
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldloc_0)); // writer
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca)));
rpcWorker.Append(rpcWorker.Create(OpCodes.Ldstr, rpcName));
rpcWorker.Append(rpcWorker.Create(OpCodes.Callvirt, Weaver.sendRpcInternal));
@ -1379,7 +1395,7 @@ void ProcessMethods()
m_CmdInvocationFuncs.Add(cmdFunc);
}
MethodDefinition cmdCallFunc = ProcessCommandCall(md);
MethodDefinition cmdCallFunc = ProcessCommandCall(md, ca);
if (cmdCallFunc != null)
{
m_CmdCallFuncs.Add(cmdCallFunc);
@ -1409,7 +1425,7 @@ void ProcessMethods()
m_TargetRpcInvocationFuncs.Add(rpcFunc);
}
MethodDefinition rpcCallFunc = ProcessTargetRpcCall(md);
MethodDefinition rpcCallFunc = ProcessTargetRpcCall(md, ca);
if (rpcCallFunc != null)
{
m_TargetRpcCallFuncs.Add(rpcCallFunc);
@ -1439,7 +1455,7 @@ void ProcessMethods()
m_RpcInvocationFuncs.Add(rpcFunc);
}
MethodDefinition rpcCallFunc = ProcessRpcCall(md);
MethodDefinition rpcCallFunc = ProcessRpcCall(md, ca);
if (rpcCallFunc != null)
{
m_RpcCallFuncs.Add(rpcCallFunc);
@ -1537,7 +1553,7 @@ MethodDefinition ProcessEventInvoke(EventDefinition ed)
return cmd;
}
MethodDefinition ProcessEventCall(EventDefinition ed)
MethodDefinition ProcessEventCall(EventDefinition ed, CustomAttribute ca)
{
MethodReference invoke = Weaver.ResolveMethod(ed.EventType, "Invoke");
MethodDefinition evt = new MethodDefinition("Call" + ed.Name, MethodAttributes.Public |
@ -1572,6 +1588,7 @@ MethodDefinition ProcessEventCall(EventDefinition ed)
evtWorker.Append(evtWorker.Create(OpCodes.Ldarg_0)); // this
evtWorker.Append(evtWorker.Create(OpCodes.Ldsfld, evtConstant)); // eventHash
evtWorker.Append(evtWorker.Create(OpCodes.Ldloc_0)); // writer
evtWorker.Append(evtWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca)));
evtWorker.Append(evtWorker.Create(OpCodes.Ldstr, ed.Name));
evtWorker.Append(evtWorker.Create(OpCodes.Call, Weaver.sendEventInternal));
@ -1615,7 +1632,7 @@ void ProcessEvents()
Weaver.DLog(m_td, "ProcessEvent " + ed);
MethodDefinition eventCallFunc = ProcessEventCall(ed);
MethodDefinition eventCallFunc = ProcessEventCall(ed, ca);
m_td.Methods.Add(eventCallFunc);
Weaver.lists.replacedEvents.Add(ed);