Merged master

This commit is contained in:
MrGadget1024 2023-03-14 07:28:23 -04:00
commit 801aca4889
5 changed files with 160 additions and 98 deletions

View File

@ -109,7 +109,6 @@ protected virtual void UpdateClient()
// only while we have snapshots // only while we have snapshots
if (clientSnapshots.Count > 0) if (clientSnapshots.Count > 0)
{ {
// step the interpolation without touching time. // step the interpolation without touching time.
// NetworkClient is responsible for time globally. // NetworkClient is responsible for time globally.
SnapshotInterpolation.StepInterpolation( SnapshotInterpolation.StepInterpolation(
@ -122,7 +121,6 @@ protected virtual void UpdateClient()
// interpolate & apply // interpolate & apply
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t); TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t);
Apply(computed, to); Apply(computed, to);
} }
} }
} }

View File

@ -46,7 +46,7 @@ void Update()
else if (isClient) UpdateClient(); else if (isClient) UpdateClient();
} }
void UpdateServer() void UpdateServerBroadcast()
{ {
// broadcast to all clients each 'sendInterval' // broadcast to all clients each 'sendInterval'
// (client with authority will drop the rpc) // (client with authority will drop the rpc)
@ -118,7 +118,10 @@ void UpdateServer()
} }
#endif #endif
} }
}
void UpdateServerInterpolation()
{
// apply buffered snapshots IF client authority // apply buffered snapshots IF client authority
// -> in server authority, server moves the object // -> in server authority, server moves the object
// so no need to apply any snapshots there. // so no need to apply any snapshots there.
@ -131,112 +134,129 @@ void UpdateServer()
connectionToClient != null && connectionToClient != null &&
!isOwned) !isOwned)
{ {
if (serverSnapshots.Count > 0) if (serverSnapshots.Count == 0) return;
{
// step the transform interpolation without touching time.
// NetworkClient is responsible for time globally.
SnapshotInterpolation.StepInterpolation(
serverSnapshots,
connectionToClient.remoteTimeline,
out TransformSnapshot from,
out TransformSnapshot to,
out double t);
// interpolate & apply // step the transform interpolation without touching time.
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t); // NetworkClient is responsible for time globally.
Apply(computed, to); SnapshotInterpolation.StepInterpolation(
} serverSnapshots,
connectionToClient.remoteTimeline,
out TransformSnapshot from,
out TransformSnapshot to,
out double t);
// interpolate & apply
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t);
Apply(computed, to);
} }
} }
void UpdateServer()
{
// broadcast to all clients each 'sendInterval'
UpdateServerBroadcast();
// apply buffered snapshots IF client authority
UpdateServerInterpolation();
}
void UpdateClientBroadcast()
{
// https://github.com/vis2k/Mirror/pull/2992/
if (!NetworkClient.ready) return;
// send to server each 'sendInterval'
// NetworkTime.localTime for double precision until Unity has it too
//
// IMPORTANT:
// snapshot interpolation requires constant sending.
// DO NOT only send if position changed. for example:
// ---
// * client sends first position at t=0
// * ... 10s later ...
// * client moves again, sends second position at t=10
// ---
// * server gets first position at t=0
// * server gets second position at t=10
// * server moves from first to second within a time of 10s
// => would be a super slow move, instead of a wait & move.
//
// IMPORTANT:
// DO NOT send nulls if not changed 'since last send' either. we
// send unreliable and don't know which 'last send' the other end
// received successfully.
if (NetworkTime.localTime >= lastClientSendTime + NetworkClient.sendInterval) // same interval as time interpolation!
{
// send snapshot without timestamp.
// receiver gets it from batch timestamp to save bandwidth.
TransformSnapshot snapshot = Construct();
#if onlySyncOnChange_BANDWIDTH_SAVING
cachedSnapshotComparison = CompareSnapshots(snapshot);
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
#endif
#if onlySyncOnChange_BANDWIDTH_SAVING
CmdClientToServerSync(
// only sync what the user wants to sync
syncPosition && positionChanged ? snapshot.position : default(Vector3?),
syncRotation && rotationChanged ? snapshot.rotation : default(Quaternion?),
syncScale && scaleChanged ? snapshot.scale : default(Vector3?)
);
#else
CmdClientToServerSync(
// only sync what the user wants to sync
syncPosition ? snapshot.position : default(Vector3?),
syncRotation ? snapshot.rotation : default(Quaternion?),
syncScale ? snapshot.scale : default(Vector3?)
);
#endif
lastClientSendTime = NetworkTime.localTime;
#if onlySyncOnChange_BANDWIDTH_SAVING
if (cachedSnapshotComparison)
{
hasSentUnchangedPosition = true;
}
else
{
hasSentUnchangedPosition = false;
lastSnapshot = snapshot;
}
#endif
}
}
void UpdateClientInterpolation()
{
// only while we have snapshots
if (clientSnapshots.Count == 0) return;
// step the interpolation without touching time.
// NetworkClient is responsible for time globally.
SnapshotInterpolation.StepInterpolation(
clientSnapshots,
NetworkTime.time, // == NetworkClient.localTimeline from snapshot interpolation
out TransformSnapshot from,
out TransformSnapshot to,
out double t);
// interpolate & apply
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t);
Apply(computed, to);
}
void UpdateClient() void UpdateClient()
{ {
// client authority, and local player (= allowed to move myself)? // client authority, and local player (= allowed to move myself)?
if (IsClientWithAuthority) if (IsClientWithAuthority)
{ {
// https://github.com/vis2k/Mirror/pull/2992/ UpdateClientBroadcast();
if (!NetworkClient.ready) return;
// send to server each 'sendInterval'
// NetworkTime.localTime for double precision until Unity has it too
//
// IMPORTANT:
// snapshot interpolation requires constant sending.
// DO NOT only send if position changed. for example:
// ---
// * client sends first position at t=0
// * ... 10s later ...
// * client moves again, sends second position at t=10
// ---
// * server gets first position at t=0
// * server gets second position at t=10
// * server moves from first to second within a time of 10s
// => would be a super slow move, instead of a wait & move.
//
// IMPORTANT:
// DO NOT send nulls if not changed 'since last send' either. we
// send unreliable and don't know which 'last send' the other end
// received successfully.
if (NetworkTime.localTime >= lastClientSendTime + NetworkClient.sendInterval) // same interval as time interpolation!
{
// send snapshot without timestamp.
// receiver gets it from batch timestamp to save bandwidth.
TransformSnapshot snapshot = Construct();
#if onlySyncOnChange_BANDWIDTH_SAVING
cachedSnapshotComparison = CompareSnapshots(snapshot);
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
#endif
#if onlySyncOnChange_BANDWIDTH_SAVING
CmdClientToServerSync(
// only sync what the user wants to sync
syncPosition && positionChanged ? snapshot.position : default(Vector3?),
syncRotation && rotationChanged ? snapshot.rotation : default(Quaternion?),
syncScale && scaleChanged ? snapshot.scale : default(Vector3?)
);
#else
CmdClientToServerSync(
// only sync what the user wants to sync
syncPosition ? snapshot.position : default(Vector3?),
syncRotation ? snapshot.rotation : default(Quaternion?),
syncScale ? snapshot.scale : default(Vector3?)
);
#endif
lastClientSendTime = NetworkTime.localTime;
#if onlySyncOnChange_BANDWIDTH_SAVING
if (cachedSnapshotComparison)
{
hasSentUnchangedPosition = true;
}
else
{
hasSentUnchangedPosition = false;
lastSnapshot = snapshot;
}
#endif
}
} }
// for all other clients (and for local player if !authority), // for all other clients (and for local player if !authority),
// we need to apply snapshots from the buffer // we need to apply snapshots from the buffer
else else
{ {
// only while we have snapshots UpdateClientInterpolation();
if (clientSnapshots.Count > 0)
{
// step the interpolation without touching time.
// NetworkClient is responsible for time globally.
SnapshotInterpolation.StepInterpolation(
clientSnapshots,
NetworkTime.time, // == NetworkClient.localTimeline from snapshot interpolation
out TransformSnapshot from,
out TransformSnapshot to,
out double t);
// interpolate & apply
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t);
Apply(computed, to);
}
} }
} }

View File

@ -1,3 +1,11 @@
V1.33 [2023-03-14]
- perf: KcpServer/Client RawReceive now call socket.Poll to avoid non-blocking
socket's allocating a new SocketException in case they WouldBlock.
fixes https://github.com/MirrorNetworking/Mirror/issues/3413
- perf: KcpServer/Client RawSend now call socket.Poll to avoid non-blocking
socket's allocating a new SocketException in case they WouldBlock.
fixes https://github.com/MirrorNetworking/Mirror/issues/3413
V1.32 [2023-03-12] V1.32 [2023-03-12]
- fix: KcpPeer RawInput now doesn't disconnect in case of random internet noise - fix: KcpPeer RawInput now doesn't disconnect in case of random internet noise

View File

@ -131,6 +131,15 @@ protected virtual bool RawReceive(out ArraySegment<byte> segment)
try try
{ {
// when using non-blocking sockets, ReceiveFrom may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectRead)) return false;
// ReceiveFrom allocates. we used bound Receive. // ReceiveFrom allocates. we used bound Receive.
// returns amount of bytes written into buffer. // returns amount of bytes written into buffer.
// throws SocketException if datagram was larger than buffer. // throws SocketException if datagram was larger than buffer.
@ -166,6 +175,15 @@ protected virtual void RawSend(ArraySegment<byte> data)
{ {
try try
{ {
// when using non-blocking sockets, SendTo may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectWrite)) return;
socket.Send(data.Array, data.Offset, data.Count, SocketFlags.None); socket.Send(data.Array, data.Offset, data.Count, SocketFlags.None);
} }
// for non-blocking sockets, SendTo may throw WouldBlock. // for non-blocking sockets, SendTo may throw WouldBlock.

View File

@ -154,6 +154,15 @@ protected virtual bool RawReceiveFrom(out ArraySegment<byte> segment, out int co
try try
{ {
// when using non-blocking sockets, ReceiveFrom may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectRead)) return false;
// NOTE: ReceiveFrom allocates. // NOTE: ReceiveFrom allocates.
// we pass our IPEndPoint to ReceiveFrom. // we pass our IPEndPoint to ReceiveFrom.
// receive from calls newClientEP.Create(socketAddr). // receive from calls newClientEP.Create(socketAddr).
@ -206,11 +215,20 @@ protected virtual void RawSend(int connectionId, ArraySegment<byte> data)
return; return;
} }
// send to the the endpoint.
// do not send to 'newClientEP', as that's always reused.
// fixes https://github.com/MirrorNetworking/Mirror/issues/3296
try try
{ {
// when using non-blocking sockets, SendTo may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectWrite)) return;
// send to the the endpoint.
// do not send to 'newClientEP', as that's always reused.
// fixes https://github.com/MirrorNetworking/Mirror/issues/3296
socket.SendTo(data.Array, data.Offset, data.Count, SocketFlags.None, connection.remoteEndPoint); socket.SendTo(data.Array, data.Offset, data.Count, SocketFlags.None, connection.remoteEndPoint);
} }
// for non-blocking sockets, SendTo may throw WouldBlock. // for non-blocking sockets, SendTo may throw WouldBlock.