Merge pull request #443 from vis2k/0gc

0gc
This commit is contained in:
vis2k 2019-02-25 13:57:31 +01:00 committed by GitHub
commit 745519982e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 37 deletions

View File

@ -14,7 +14,7 @@ public ULocalConnectionToClient(LocalClient localClient) : base ("localClient")
this.localClient = localClient;
}
protected override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable)
internal override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable)
{
localClient.InvokeBytesOnClient(bytes);
return true;
@ -29,7 +29,7 @@ public ULocalConnectionToServer() : base("localServer")
{
}
protected override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable)
internal override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable)
{
if (bytes.Length == 0)
{

View File

@ -153,17 +153,14 @@ internal void RemovePlayerController()
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());
byte[] message = Protocol.PackMessage((ushort)msgType, msg);
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, int channelId = Channels.DefaultReliable)
// internal because no one except Mirror should send bytes directly to
// the client. they would be detected as a message. send messages instead.
internal virtual bool SendBytes( byte[] bytes, int channelId = Channels.DefaultReliable)
{
if (logNetworkMessages) { Debug.Log("ConnectionSend con:" + connectionId + " bytes:" + BitConverter.ToString(bytes)); }

View File

@ -447,10 +447,17 @@ internal bool OnSerializeSafely(NetworkBehaviour comp, NetworkWriter writer, boo
return result;
}
// OnSerializeAllSafely is in hot path. caching the writer is really
// worth it to avoid large amounts of allocations.
static NetworkWriter onSerializeWriter = new NetworkWriter();
// serialize all components (or only dirty ones if not initial state)
// -> returns serialized data of everything dirty, null if nothing was dirty
internal byte[] OnSerializeAllSafely(bool initialState)
{
// reset cached writer's position
onSerializeWriter.Position = 0;
if (m_NetworkBehaviours.Length > 64)
{
Debug.LogError("Only 64 NetworkBehaviour components are allowed for NetworkIdentity: " + name + " because of the dirtyComponentMask");
@ -461,8 +468,7 @@ internal byte[] OnSerializeAllSafely(bool initialState)
if (dirtyComponentsMask == 0L)
return null;
NetworkWriter writer = new NetworkWriter();
writer.WritePackedUInt64(dirtyComponentsMask); // WritePacked64 so we don't write full 8 bytes if we don't have to
onSerializeWriter.WritePackedUInt64(dirtyComponentsMask); // WritePacked64 so we don't write full 8 bytes if we don't have to
foreach (NetworkBehaviour comp in m_NetworkBehaviours)
{
@ -473,7 +479,7 @@ internal byte[] OnSerializeAllSafely(bool initialState)
{
// serialize the data
if (LogFilter.Debug) { Debug.Log("OnSerializeAllSafely: " + name + " -> " + comp.GetType() + " initial=" + initialState); }
OnSerializeSafely(comp, writer, initialState);
OnSerializeSafely(comp, onSerializeWriter, initialState);
// Clear dirty bits only if we are synchronizing data and not sending a spawn message.
// This preserves the behavior in HLAPI
@ -484,7 +490,7 @@ internal byte[] OnSerializeAllSafely(bool initialState)
}
}
return writer.ToArray();
return onSerializeWriter.ToArray();
}
private ulong GetDirtyMask(NetworkBehaviour[] components, bool initialState)
@ -903,6 +909,11 @@ internal void Reset()
clientAuthorityOwner = null;
}
// UNetUpdate is in hot path. caching the vars msg is really worth it to
// avoid large amounts of allocations.
static UpdateVarsMessage varsMessage = new UpdateVarsMessage();
// invoked by unity runtime immediately after the regular "Update()" function.
internal void UNetUpdate()
{
@ -915,14 +926,10 @@ internal void UNetUpdate()
byte[] payload = OnSerializeAllSafely(false);
if (payload != null)
{
// construct message and send
UpdateVarsMessage message = new UpdateVarsMessage
{
netId = netId,
payload = payload
};
NetworkServer.SendToReady(this, (short)MsgType.UpdateVars, message);
// populate cached UpdateVarsMessage and send
varsMessage.netId = netId;
varsMessage.payload = payload;
NetworkServer.SendToReady(this, (short)MsgType.UpdateVars, varsMessage);
}
}

View File

@ -186,10 +186,14 @@ static bool SendToObservers(NetworkIdentity identity, short msgType, MessageBase
if (identity != null && identity.observers != null)
{
// pack message into byte[] once
byte[] bytes = Protocol.PackMessage((ushort)msgType, msg);
// send to all observers
bool result = true;
foreach (KeyValuePair<int, NetworkConnection> kvp in identity.observers)
{
result &= kvp.Value.Send(msgType, msg);
result &= kvp.Value.SendBytes(bytes);
}
return result;
}
@ -200,10 +204,14 @@ public static bool SendToAll(short msgType, MessageBase msg, int channelId = Cha
{
if (LogFilter.Debug) { Debug.Log("Server.SendToAll id:" + msgType); }
// pack message into byte[] once
byte[] bytes = Protocol.PackMessage((ushort)msgType, msg);
// send to all
bool result = true;
foreach (KeyValuePair<int, NetworkConnection> kvp in connections)
{
result &= kvp.Value.Send(msgType, msg, channelId);
result &= kvp.Value.SendBytes(bytes, channelId);
}
return result;
}
@ -214,12 +222,16 @@ public static bool SendToReady(NetworkIdentity identity, short msgType, MessageB
if (identity != null && identity.observers != null)
{
// pack message into byte[] once
byte[] bytes = Protocol.PackMessage((ushort)msgType, msg);
// send to all ready observers
bool result = true;
foreach (KeyValuePair<int, NetworkConnection> kvp in identity.observers)
{
if (kvp.Value.isReady)
{
result &= kvp.Value.Send(msgType, msg, channelId);
result &= kvp.Value.SendBytes(bytes, channelId);
}
}
return result;

View File

@ -98,22 +98,25 @@ public static class Channels
// -> this reduces bandwidth by 10% if average message size is 20 bytes (probably even shorter)
public static class Protocol
{
// PackMessage is in hot path. caching the writer is really worth it to
// avoid large amounts of allocations.
static NetworkWriter packWriter = new NetworkWriter();
// pack message before sending
public static byte[] PackMessage(ushort msgType, byte[] content)
// -> pass writer instead of byte[] so we can reuse it
public static byte[] PackMessage(ushort msgType, MessageBase msg)
{
// original HLAPI's 'content' part is never null, so we don't have to handle that case.
// just create an empty array if null.
if (content == null) content = new byte[0];
// reset cached writer's position
packWriter.Position = 0;
NetworkWriter writer = new NetworkWriter();
// write message type
packWriter.WritePackedUInt32(msgType);
// message type (varint)
writer.WritePackedUInt32(msgType);
// serialize message into writer
msg.Serialize(packWriter);
// message content (if any)
writer.Write(content, 0, content.Length);
return writer.ToArray();
// return byte[]
return packWriter.ToArray();
}
// unpack message after receiving

View File

@ -110,8 +110,8 @@ Start reading our code and you'll get the hang of it. We optimize for readabilit
* **KISS / Occam's Razor** - always use the most simple solution.
* **No Premature Optimizations**
MMOs need to run for weeks without issues or exploits.
If you want your code to run 1% faster, spend \$100 on a better CPU.
* **Curly Braces { }**
Only do GC optimizations and caching in hot path. Avoid it everywhere else to keep the code simple.
* **Curly Braces { }**
Always use braces even for one line if's. Unity did this everywhere, and there is value in not accidentally missing a line in an if statement because there were no braces.
* **Variable naming**
\`NetworkIdentity identity\`, not \`NetworkIdentity uv\` or similar. If the variable needs a comment the name needs to be changed. For example, `msg = ... // the message` use `message = ...` without a comment instead