void Deserialize(byte[] bytes, int senderId) { VoosNetworkTypes.ShortToBytes stb = new VoosNetworkTypes.ShortToBytes(); VoosNetworkTypes.UintToBytes utb = new VoosNetworkTypes.UintToBytes(); if (bytes.Length < 6) { return; } stb.byte0 = bytes[0]; stb.byte1 = bytes[1]; short positionCount = stb.data; utb.byte0 = bytes[1]; utb.byte1 = bytes[2]; utb.byte2 = bytes[3]; utb.byte3 = 0; float timestamp = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); if (bytes.Length < 5 + positionCount * size) { return; } for (int i = 0; i < positionCount; i++) { int offset = 5 + i * size; /// View Id stb.byte0 = bytes[offset]; stb.byte1 = bytes[offset + 1]; offset += 2; short viewId = stb.data; if (actors.ContainsKey(viewId)) { NetworkedActor na = actors[viewId]; VoosActor va = na.actor; if (va == null) { diag.numNullActorEntries++; } if (va != null && va.reliablePhotonView.ownerId != senderId) { diag.numSenderOwnerMismatches++; } if (va != null && va.reliablePhotonView.ownerId == senderId) { va.lastUnreliableUpdateTime = Time.realtimeSinceStartup; va.unrel = na; float timeDelta = Mathf.Clamp(timestamp - na.lastRecvTimestamp, 0.01f, maxTimeDelta); na.lastRecvTimestamp = timestamp; UnreliableBitmask mask = (UnreliableBitmask)bytes[offset]; offset += 1; Vector3 oldP = na.lastPosition; /// Local Position utb.byte0 = bytes[offset]; utb.byte1 = bytes[offset + 1]; utb.byte2 = bytes[offset + 2]; utb.byte3 = 0; na.lastPosition.x = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 3]; utb.byte1 = bytes[offset + 4]; utb.byte2 = bytes[offset + 5]; utb.byte3 = 0; na.lastPosition.y = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 6]; utb.byte1 = bytes[offset + 7]; utb.byte2 = bytes[offset + 8]; utb.byte3 = 0; offset += 9; na.lastPosition.z = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); Vector3 predictionError = na.lastPosition - va.transform.position; Quaternion oldQ = na.lastRotation; // Full precision rotation for static var ftb = new VoosNetworkTypes.FloatToBytes(); float x = ftb.DeserializeFrom(bytes, ref offset); float y = ftb.DeserializeFrom(bytes, ref offset); float z = ftb.DeserializeFrom(bytes, ref offset); float w = ftb.DeserializeFrom(bytes, ref offset); na.lastRotation = new Quaternion(x, y, z, w); /// Color Color32 c = new Color32(); c.r = bytes[offset]; c.g = bytes[offset + 1]; c.b = bytes[offset + 2]; c.a = bytes[offset + 3]; offset += 4; va.SetTint(c); /// Linear V utb.byte0 = bytes[offset]; utb.byte1 = bytes[offset + 1]; utb.byte2 = bytes[offset + 2]; utb.byte3 = 0; na.linearVelocity.x = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 3]; utb.byte1 = bytes[offset + 4]; utb.byte2 = bytes[offset + 5]; utb.byte3 = 0; na.linearVelocity.y = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 6]; utb.byte1 = bytes[offset + 7]; utb.byte2 = bytes[offset + 8]; utb.byte3 = 0; na.linearVelocity.z = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); Rigidbody rb = va.GetComponent <Rigidbody>(); bool isDynamic = rb != null && va.GetEnablePhysics(); // Hard Snap if the teleport flag is on. Otherwise we smoothly interpolate. if ((mask & UnreliableBitmask.Teleport) == UnreliableBitmask.Teleport) { predictionError = Vector3.zero; va.transform.position = na.lastPosition; va.transform.rotation = na.lastRotation; if (rb != null) { rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } } if (va.IsParented()) { continue; } if (isDynamic) { if (SnapRigidbodyOnRecv) { rb.velocity = na.linearVelocity; rb.MovePosition(na.lastPosition); rb.MoveRotation(na.lastRotation); } else { const float catchUpEnterThresh = 1f; const float catchUpExitThresh = 0.5f; float errorDist = predictionError.magnitude; if (!va.GetReplicantCatchUpMode()) { if (errorDist > catchUpEnterThresh) { va.SetReplicantCatchUpMode(true); } } else { if (va.debug) { Util.Log($"in catch up mode... err dist: {errorDist}"); } if (errorDist < catchUpExitThresh) { va.SetReplicantCatchUpMode(false); } } if (va.GetReplicantCatchUpMode()) { // Let the fixed update directly lerp this. } else { // Best-effort corrective velocity only. // TODO expose some tunable error -> correction velocity parameter? rb.velocity = na.linearVelocity + predictionError; } } } } } else { diag.numUnknownViewIds++; } } }