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++; } } }
byte[] Serialize() { serializationBytes.Clear(); VoosNetworkTypes.ShortToBytes stb = new VoosNetworkTypes.ShortToBytes(); VoosNetworkTypes.UintToBytes utb = new VoosNetworkTypes.UintToBytes(); int totalCount = 0; float totalProbability = 0; foreach (KeyValuePair <short, NetworkedActor> na in actors) { VoosActor va = na.Value.actor; if (va != null && va.reliablePhotonView.isMine && !va.IsParented()) { Vector3 p = va.transform.position; Quaternion q = va.transform.rotation; if (na.Value.lastPosition != p || na.Value.lastRotation != q || na.Value.lastColor != va.GetTint()) { na.Value.lastPosition = p; na.Value.lastRotation = q; na.Value.lastColor = va.GetTint(); na.Value.lastTransformUpdateFrame = Time.frameCount; na.Value.messagesSinceLastUpdate = 0; } na.Value.updateProbability = Mathf.Clamp(1.0f / (float)(1 + na.Value.messagesSinceLastUpdate), minimumProbabilityOfUpdate, 1); totalProbability += na.Value.updateProbability; } totalCount++; } float probabilityOfPositionUpdate = 1; if (totalCount > MaxPositionUpdatesPerTick) { probabilityOfPositionUpdate = (float)MaxPositionUpdatesPerTick / totalProbability; } /// Message Header /// Transform count - will modify after the fact. serializationBytes.Add(0); serializationBytes.Add(0); /// Timestamp for prediction. utb.data = VoosNetworkTypes.CompressFloatToUint24(Time.time); serializationBytes.Add(utb.byte0); serializationBytes.Add(utb.byte1); serializationBytes.Add(utb.byte2); int index = 0; foreach (KeyValuePair <short, NetworkedActor> na in actors) { VoosActor va = na.Value.actor; if (va != null && va.reliablePhotonView.isMine && !va.IsParented()) { if (Random.value > (probabilityOfPositionUpdate * na.Value.updateProbability)) { continue; } na.Value.messagesSinceLastUpdate++; /// View ID stb.data = (short)va.reliablePhotonView.viewID; serializationBytes.Add(stb.byte0); serializationBytes.Add(stb.byte1); /// Network data bitmask UnreliableBitmask mask = UnreliableBitmask.None; mask |= va.GetTeleport() ? UnreliableBitmask.Teleport : UnreliableBitmask.None; mask |= va.GetEnablePhysics() ? UnreliableBitmask.EnablePhysics : UnreliableBitmask.None; serializationBytes.Add((byte)mask); // Unset the "teleport" flag, as it's transient and should only be used for one network packet. va.SetTeleport(false); // SUBTLE: If physics is not enabled, then pos/rot is not likely to // change. However, any inaccuracies are also more likely to be noticed // (such as static walls being slightly tilted). So given this, always // send full precision updates for static objects. Sure, this is less // efficient for things like moving platforms which do not have physics // but often change, but that's likely to be a minority. /// Local Position utb.WriteUint24(serializationBytes, VoosNetworkTypes.CompressFloatToUint24(na.Value.lastPosition.x)); utb.WriteUint24(serializationBytes, VoosNetworkTypes.CompressFloatToUint24(na.Value.lastPosition.y)); utb.WriteUint24(serializationBytes, VoosNetworkTypes.CompressFloatToUint24(na.Value.lastPosition.z)); // Static - send full precision rotation on change. TODO I'm sure // there's some way to compress it to 8 bytes without loss. var ftb = new VoosNetworkTypes.FloatToBytes(); Quaternion q = na.Value.lastRotation; ftb.SerializeTo(serializationBytes, q.x); ftb.SerializeTo(serializationBytes, q.y); ftb.SerializeTo(serializationBytes, q.z); ftb.SerializeTo(serializationBytes, q.w); //Color serializationBytes.Add(na.Value.lastColor.r); serializationBytes.Add(na.Value.lastColor.g); serializationBytes.Add(na.Value.lastColor.b); serializationBytes.Add(na.Value.lastColor.a); /// Linear Velocity Rigidbody rb = va.GetComponent <Rigidbody>(); Vector3 lv = Vector3.zero; if (rb != null && !rb.isKinematic) { lv = rb.velocity; } utb.data = VoosNetworkTypes.CompressFloatToUint24(lv.x); serializationBytes.Add(utb.byte0); serializationBytes.Add(utb.byte1); serializationBytes.Add(utb.byte2); utb.data = VoosNetworkTypes.CompressFloatToUint24(lv.y); serializationBytes.Add(utb.byte0); serializationBytes.Add(utb.byte1); serializationBytes.Add(utb.byte2); utb.data = VoosNetworkTypes.CompressFloatToUint24(lv.z); serializationBytes.Add(utb.byte0); serializationBytes.Add(utb.byte1); serializationBytes.Add(utb.byte2); index++; } } stb.data = (short)index; serializationBytes[0] = stb.byte0; serializationBytes[1] = stb.byte1; if (index == 0) { return(null); } return(serializationBytes.ToArray()); }