Desync Check Code
Niki Walker's Desync Check Code:
C#
// NWalker - FGOL - Desync dump code:
public void InsideYourRPCEventHandler(...)
{
case PhotonEventDesync:
{
if (! m_desyncMessageBytes.ContainsKey(senderId))
{
m_desyncMessageBytes[senderId] = new List<byte>(10000);
}
var byteContent = (byte[])content;
if (byteContent.Length != 0)
{
m_desyncMessageBytes[senderId].AddRange(byteContent);
}
else
{
string dump = Encoding.UTF8.GetString(m_desyncMessageBytes[senderId].ToArray());
Debug.LogError("Desync from sender: " + sender.NickName + "\n" + dump);
#if UNITY_EDITOR // Log it to desktop when in the editor.
var time = DateTime.UtcNow;
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string path = desktop + "\\Desync_" + time.Hour + "_" + time.Minute + "_" + time.Second + "__" + sender.ID + "_" + sender.NickName + ".txt";
File.WriteAllText(path, dump);
Debug.Log("Desync info outputted to file: " + path);
#endif
data.DesyncText = null;
}
}
break;
}
// Inside your QuantumCallbacks implementation:
/// <inheritdoc />
public override void OnChecksumError(DeterministicTickChecksumError error, Frame[] frames)
{
base.OnChecksumError(error, frames);
// Display to player.
// ... code here that draws a massive desync error message on screen (for devices without consoles)...
// Log.
string desyncMessage = PhotonNetwork.playerName + " DESYNC: " + error.Tick + ": " + error.Checksums.Aggregate("\n", (c, n) => c + "[" + n.Player + ", " + n.Checksum + "]") + "\n\n";
// Send it to all users (including self).
Action<byte[]> raiseEvent = message =>
{
PhotonNetwork.RaiseEvent(HssBootstrapper.PhotonEventDesync, message, true, new RaiseEventOptions
{
CachingOption = EventCaching.DoNotCache,
Encrypt = true,
ForwardToWebhook = false,
Receivers = ReceiverGroup.All
});
};
// NW: Due to the char limit when sending strings, only send the one frame, but send the full frame dump:
for (int index = 0; index < frames.Length; index++)
{
var frame = frames[index];
if (frame.Number == error.Tick || frame.Number == error.Tick - 1)
{
string frameLog = "F[" + frame.DumpFrame() + "]\n\n";
desyncMessage += frameLog;
}
}
var bytes = Encoding.UTF8.GetBytes(desyncMessage);
foreach (var chunk in bytes.Slices(1024, false))
{
raiseEvent(chunk);
}
// Zero byte denotes completed.
raiseEvent(new byte[] { });
Debug.LogError(desyncMessage);
}
public static class Util
{
public static T[] CopySlice<T>(this T[] source, int index, int length, bool padToLength)
{
int n = length;
T[] slice = null;
if (source.Length < index + length)
{
n = source.Length - index;
if (padToLength)
{
slice = new T[length];
}
}
if (slice == null) slice = new T[n];
Array.Copy(source, index, slice, 0, n);
return slice;
}
/// <summary>
/// Slices the array into chunks of size <see cref="chunkSize"/>.
/// </summary>
public static IEnumerable<T[]> Slices<T>(this T[] source, int chunkSize, bool padToLength)
{
for (int i = 0; i < source.Length; i += chunkSize)
{
yield return source.CopySlice(i, chunkSize, padToLength);
}
}
}
Back to top