public static void CopyFieldsFromBuffer <TOutputStream>(NetworkSchema schema, byte[] inputBuffer, ref TOutputStream output) where TOutputStream : NetworkCompression.IOutputStream { var input = new ByteInputStream(inputBuffer); int fieldIndex = 0; for (; fieldIndex < schema.fields.Count; ++fieldIndex) { var field = schema.fields[fieldIndex]; switch (field.fieldType) { case NetworkSchema.FieldType.Bool: case NetworkSchema.FieldType.UInt: case NetworkSchema.FieldType.Int: case NetworkSchema.FieldType.Float: output.WriteRawBits(input.ReadBits(field.bits), field.bits); break; case NetworkSchema.FieldType.Vector2: output.WriteRawBits(input.ReadUInt32(), field.bits); output.WriteRawBits(input.ReadUInt32(), field.bits); break; case NetworkSchema.FieldType.Vector3: output.WriteRawBits(input.ReadUInt32(), field.bits); output.WriteRawBits(input.ReadUInt32(), field.bits); output.WriteRawBits(input.ReadUInt32(), field.bits); break; case NetworkSchema.FieldType.Quaternion: output.WriteRawBits(input.ReadUInt32(), field.bits); output.WriteRawBits(input.ReadUInt32(), field.bits); output.WriteRawBits(input.ReadUInt32(), field.bits); output.WriteRawBits(input.ReadUInt32(), field.bits); break; case NetworkSchema.FieldType.String: case NetworkSchema.FieldType.ByteArray: { byte[] data; int dataIndex; int dataSize; input.GetByteArray(out data, out dataIndex, out dataSize, field.arraySize); output.WritePackedUInt((uint)dataSize, field.startContext); output.WriteRawBytes(data, dataIndex, dataSize); } break; default: GameDebug.Assert(false); break; } } }
static public unsafe void Write <TOutputStream>(ref TOutputStream output, NetworkSchema schema, byte[] inputData, byte[] baselineData, byte[] fieldsChangedPrediction, byte fieldMask, ref uint entity_hash) where TOutputStream : NetworkCompression.IOutputStream { GameDebug.Assert(baselineData != null); var inputStream = new ByteInputStream(inputData); var baselineStream = new ByteInputStream(baselineData); int numFields = schema.fields.Count; GameDebug.Assert(fieldsChangedPrediction.Length >= numFields / 8, "Not enough bits in fieldsChangedPrediction for all fields"); for (int i = 0, l = fieldsNotPredicted.Length; i < l; ++i) { fieldsNotPredicted[i] = 0; } // calculate bitmask of fields that need to be encoded for (int fieldIndex = 0; fieldIndex < schema.fields.Count; ++fieldIndex) { var field = schema.fields[fieldIndex]; // Skip fields that are masked out bool masked = (field.fieldMask & fieldMask) != 0; byte fieldByteOffset = (byte)((uint)fieldIndex >> 3); byte fieldBitOffset = (byte)((uint)fieldIndex & 0x7); switch (field.fieldType) { case NetworkSchema.FieldType.Bool: { uint value = inputStream.ReadBits(1); uint baseline = baselineStream.ReadUInt8(); if (!masked) { entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, value); if (value != baseline) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } break; } case NetworkSchema.FieldType.Int: { uint value = inputStream.ReadBits(field.bits); uint baseline = (uint)baselineStream.ReadBits(field.bits); if (!masked) { entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, value); if (value != baseline) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } break; } case NetworkSchema.FieldType.UInt: { uint value = inputStream.ReadBits(field.bits); uint baseline = (uint)baselineStream.ReadBits(field.bits); if (!masked) { entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, value); if (value != baseline) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } break; } case NetworkSchema.FieldType.Float: { uint value = inputStream.ReadBits(field.bits); uint baseline = (uint)baselineStream.ReadBits(field.bits); if (!masked) { entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, value); if (value != baseline) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } break; } case NetworkSchema.FieldType.Vector2: { uint vx = inputStream.ReadBits(field.bits); uint vy = inputStream.ReadBits(field.bits); uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); if (!masked) { entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vx); entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vy); if (vx != bx || vy != by) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } break; } case NetworkSchema.FieldType.Vector3: { uint vx = inputStream.ReadBits(field.bits); uint vy = inputStream.ReadBits(field.bits); uint vz = inputStream.ReadBits(field.bits); uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); uint bz = baselineStream.ReadUInt32(); if (!masked) { entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vx); entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vy); entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vz); if (vx != bx || vy != by || vz != bz) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } break; } case NetworkSchema.FieldType.Quaternion: { uint vx = inputStream.ReadBits(field.bits); uint vy = inputStream.ReadBits(field.bits); uint vz = inputStream.ReadBits(field.bits); uint vw = inputStream.ReadBits(field.bits); uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); uint bz = baselineStream.ReadUInt32(); uint bw = baselineStream.ReadUInt32(); if (!masked) { entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vx); entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vy); entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vz); entity_hash = NetworkUtils.SimpleHashStreaming(entity_hash, vw); if (vx != bx || vy != by || vz != bz || vw != bw) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } break; } case NetworkSchema.FieldType.String: case NetworkSchema.FieldType.ByteArray: { // TODO : Do a better job of string and buffer diffs? byte[] valueBuffer; int valueOffset; int valueLength; inputStream.GetByteArray(out valueBuffer, out valueOffset, out valueLength, field.arraySize); byte[] baselineBuffer = null; int baselineOffset = 0; int baselineLength = 0; baselineStream.GetByteArray(out baselineBuffer, out baselineOffset, out baselineLength, field.arraySize); if (!masked) { entity_hash += 0; // TODO client side has no easy way to hash strings. enable this when possible: NetworkUtils.SimpleHash(valueBuffer, valueLength); if (valueLength != baselineLength || NetworkUtils.MemCmp(valueBuffer, valueOffset, baselineBuffer, baselineOffset, valueLength) != 0) { fieldsNotPredicted[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } } break; } } inputStream.Reset(); baselineStream.Reset(); int skipContext = schema.id * NetworkConfig.maxContextsPerSchema + NetworkConfig.firstSchemaContext; // Client needs fieldsNotPredicted. We send the delta between it and fieldsChangedPrediction { for (int i = 0; i * 8 < numFields; i++) { byte deltaFields = (byte)(fieldsNotPredicted[i] ^ fieldsChangedPrediction[i]); output.WritePackedNibble((uint)(deltaFields & 0xF), skipContext + i * 2); output.WritePackedNibble((uint)((deltaFields >> 4) & 0xF), skipContext + i * 2 + 1); } } int startBitPosition = 0; for (int fieldIndex = 0; fieldIndex < numFields; ++fieldIndex) { var field = schema.fields[fieldIndex]; int fieldStartContext = field.startContext; startBitPosition = output.GetBitPosition2(); byte fieldByteOffset = (byte)((uint)fieldIndex >> 3); byte fieldBitOffset = (byte)((uint)fieldIndex & 0x7); var notPredicted = ((fieldsNotPredicted[fieldByteOffset] & (1 << fieldBitOffset)) != 0); switch (field.fieldType) { case NetworkSchema.FieldType.Bool: { uint value = inputStream.ReadBits(1); /*uint unused_baseline = */ baselineStream.ReadUInt8(); if (notPredicted) { output.WriteRawBits(value, 1); NetworkSchema.AddStatsToFieldBool(field, (value != 0), false, output.GetBitPosition2() - startBitPosition); } break; } case NetworkSchema.FieldType.Int: { uint value = inputStream.ReadBits(field.bits); uint baseline = (uint)baselineStream.ReadBits(field.bits); if (notPredicted) { if (field.delta) { output.WritePackedUIntDelta(value, baseline, fieldStartContext); NetworkSchema.AddStatsToFieldInt(field, (int)value, (int)baseline, output.GetBitPosition2() - startBitPosition); } else { output.WriteRawBits(value, field.bits); NetworkSchema.AddStatsToFieldInt(field, (int)value, 0, output.GetBitPosition2() - startBitPosition); } } break; } case NetworkSchema.FieldType.UInt: { uint value = inputStream.ReadBits(field.bits); uint baseline = (uint)baselineStream.ReadBits(field.bits); if (notPredicted) { if (field.delta) { output.WritePackedUIntDelta(value, baseline, fieldStartContext); NetworkSchema.AddStatsToFieldUInt(field, value, baseline, output.GetBitPosition2() - startBitPosition); } else { output.WriteRawBits(value, field.bits); NetworkSchema.AddStatsToFieldUInt(field, value, 0, output.GetBitPosition2() - startBitPosition); } } break; } case NetworkSchema.FieldType.Float: { uint value = inputStream.ReadBits(field.bits); uint baseline = (uint)baselineStream.ReadBits(field.bits); if (notPredicted) { if (field.delta) { output.WritePackedUIntDelta(value, baseline, fieldStartContext); NetworkSchema.AddStatsToFieldFloat(field, value, baseline, output.GetBitPosition2() - startBitPosition); } else { output.WriteRawBits(value, field.bits); NetworkSchema.AddStatsToFieldFloat(field, value, 0, output.GetBitPosition2() - startBitPosition); } } break; } case NetworkSchema.FieldType.Vector2: { uint vx = inputStream.ReadBits(field.bits); uint vy = inputStream.ReadBits(field.bits); uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); if (notPredicted) { if (field.delta) { output.WritePackedUIntDelta(vx, bx, fieldStartContext + 0); output.WritePackedUIntDelta(vy, by, fieldStartContext + 1); NetworkSchema.AddStatsToFieldVector2(field, vx, vy, bx, by, output.GetBitPosition2() - startBitPosition); } else { output.WriteRawBits(vx, field.bits); output.WriteRawBits(vy, field.bits); NetworkSchema.AddStatsToFieldVector2(field, vx, vy, 0, 0, output.GetBitPosition2() - startBitPosition); } } break; } case NetworkSchema.FieldType.Vector3: { uint vx = inputStream.ReadBits(field.bits); uint vy = inputStream.ReadBits(field.bits); uint vz = inputStream.ReadBits(field.bits); uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); uint bz = baselineStream.ReadUInt32(); if (notPredicted) { if (field.delta) { output.WritePackedUIntDelta(vx, bx, fieldStartContext + 0); output.WritePackedUIntDelta(vy, by, fieldStartContext + 1); output.WritePackedUIntDelta(vz, bz, fieldStartContext + 2); NetworkSchema.AddStatsToFieldVector3(field, vx, vy, vz, bx, by, bz, output.GetBitPosition2() - startBitPosition); } else { output.WriteRawBits(vx, field.bits); output.WriteRawBits(vy, field.bits); output.WriteRawBits(vz, field.bits); NetworkSchema.AddStatsToFieldVector3(field, vx, vy, vz, 0, 0, 0, output.GetBitPosition2() - startBitPosition); } } break; } case NetworkSchema.FieldType.Quaternion: { // TODO : Figure out what to do with quaternions uint vx = inputStream.ReadBits(field.bits); uint vy = inputStream.ReadBits(field.bits); uint vz = inputStream.ReadBits(field.bits); uint vw = inputStream.ReadBits(field.bits); uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); uint bz = baselineStream.ReadUInt32(); uint bw = baselineStream.ReadUInt32(); if (notPredicted) { if (field.delta) { output.WritePackedUIntDelta(vx, bx, fieldStartContext + 0); output.WritePackedUIntDelta(vy, by, fieldStartContext + 1); output.WritePackedUIntDelta(vz, bz, fieldStartContext + 2); output.WritePackedUIntDelta(vw, bw, fieldStartContext + 3); NetworkSchema.AddStatsToFieldQuaternion(field, vx, vy, vz, vw, bx, by, bz, bw, output.GetBitPosition2() - startBitPosition); } else { output.WriteRawBits(vx, field.bits); output.WriteRawBits(vy, field.bits); output.WriteRawBits(vz, field.bits); output.WriteRawBits(vw, field.bits); NetworkSchema.AddStatsToFieldQuaternion(field, vx, vy, vz, vw, 0, 0, 0, 0, output.GetBitPosition2() - startBitPosition); } } break; } case NetworkSchema.FieldType.String: case NetworkSchema.FieldType.ByteArray: { // TODO : Do a better job of string and buffer diffs? byte[] valueBuffer; int valueOffset; int valueLength; inputStream.GetByteArray(out valueBuffer, out valueOffset, out valueLength, field.arraySize); byte[] baselineBuffer = null; int baselineOffset = 0; int baselineLength = 0; baselineStream.GetByteArray(out baselineBuffer, out baselineOffset, out baselineLength, field.arraySize); if (notPredicted) { output.WritePackedUInt((uint)valueLength, fieldStartContext); output.WriteRawBytes(valueBuffer, valueOffset, valueLength); if (field.fieldType == NetworkSchema.FieldType.String) { NetworkSchema.AddStatsToFieldString(field, valueBuffer, valueOffset, valueLength, output.GetBitPosition2() - startBitPosition); } else { NetworkSchema.AddStatsToFieldByteArray(field, valueBuffer, valueOffset, valueLength, output.GetBitPosition2() - startBitPosition); } } } break; } } }
// Predict snapshot from baselines. Returns true if prediction is different from baseline 0 (if it need to be automatically predicted next frame). public static void PredictSnapshot(byte[] outputData, byte[] fieldsChangedPrediction, NetworkSchema schema, uint numBaselines, uint time0, byte[] baselineData0, uint time1, byte[] baselineData1, uint time2, byte[] baselineData2, uint time, byte fieldMask) { for (int i = 0, l = fieldsChangedPrediction.Length; i < l; ++i) { fieldsChangedPrediction[i] = 0; } if (numBaselines < 3) { System.Array.Copy(baselineData0, outputData, schema.GetByteSize()); return; } var baselineStream0 = new ByteInputStream(baselineData0); var baselineStream1 = new ByteInputStream(baselineData1); var baselineStream2 = new ByteInputStream(baselineData2); var outputStream = new ByteOutputStream(outputData); for (int i = 0; i < schema.fields.Count; ++i) { GameDebug.Assert(schema.fields[i].byteOffset == baselineStream0.GetBytePosition()); GameDebug.Assert(schema.fields[i].byteOffset == baselineStream1.GetBytePosition()); GameDebug.Assert(schema.fields[i].byteOffset == baselineStream2.GetBytePosition()); var field = schema.fields[i]; byte fieldByteOffset = (byte)((uint)i >> 3); byte fieldBitOffset = (byte)((uint)i & 0x7); bool masked = (field.fieldMask & fieldMask) != 0; switch (field.fieldType) { case NetworkSchema.FieldType.Bool: { uint baseline0 = baselineStream0.ReadUInt8(); uint baseline1 = baselineStream1.ReadUInt8(); uint baseline2 = baselineStream2.ReadUInt8(); uint prediction = baseline0; if (!masked) { if (baseline0 != baseline1 && baseline1 != baseline2) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } outputStream.WriteUInt8((byte)prediction); break; } case NetworkSchema.FieldType.UInt: case NetworkSchema.FieldType.Int: case NetworkSchema.FieldType.Float: { uint baseline0 = (uint)baselineStream0.ReadBits(field.bits); uint baseline1 = (uint)baselineStream1.ReadBits(field.bits); uint baseline2 = (uint)baselineStream2.ReadBits(field.bits); uint prediction = baseline0; if (!masked) { if (field.delta) { bool predictionLikelyWrong; prediction = NetworkPrediction.PredictUint(numBaselines, time0, baseline0, time1, baseline1, time2, baseline2, time, out predictionLikelyWrong); if (predictionLikelyWrong) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } else { if (baseline0 != baseline1 && baseline1 != baseline2) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } } outputStream.WriteBits(prediction, field.bits); //RUTODO: fix this break; } case NetworkSchema.FieldType.Vector2: { uint bx0 = baselineStream0.ReadUInt32(); uint by0 = baselineStream0.ReadUInt32(); uint bx1 = baselineStream1.ReadUInt32(); uint by1 = baselineStream1.ReadUInt32(); uint bx2 = baselineStream2.ReadUInt32(); uint by2 = baselineStream2.ReadUInt32(); uint px = bx0; uint py = by0; if (!masked) { if (field.delta) { bool predictionLikelyWrongX; bool predictionLikelyWrongY; px = NetworkPrediction.PredictUint(numBaselines, time0, bx0, time1, bx1, time2, bx2, time, out predictionLikelyWrongX); py = NetworkPrediction.PredictUint(numBaselines, time0, by0, time1, by1, time2, by2, time, out predictionLikelyWrongY); if (predictionLikelyWrongX || predictionLikelyWrongY) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } else { if ((bx0 != bx1 || by0 != by1) && (bx1 != bx2 || by1 != by2)) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } } outputStream.WriteUInt32(px); outputStream.WriteUInt32(py); break; } case NetworkSchema.FieldType.Vector3: { uint bx0 = baselineStream0.ReadUInt32(); uint by0 = baselineStream0.ReadUInt32(); uint bz0 = baselineStream0.ReadUInt32(); uint bx1 = baselineStream1.ReadUInt32(); uint by1 = baselineStream1.ReadUInt32(); uint bz1 = baselineStream1.ReadUInt32(); uint bx2 = baselineStream2.ReadUInt32(); uint by2 = baselineStream2.ReadUInt32(); uint bz2 = baselineStream2.ReadUInt32(); uint px = bx0; uint py = by0; uint pz = bz0; if (!masked) { if (field.delta) { bool predictionLikelyWrongX; bool predictionLikelyWrongY; bool predictionLikelyWrongZ; px = NetworkPrediction.PredictUint(numBaselines, time0, bx0, time1, bx1, time2, bx2, time, out predictionLikelyWrongX); py = NetworkPrediction.PredictUint(numBaselines, time0, by0, time1, by1, time2, by2, time, out predictionLikelyWrongY); pz = NetworkPrediction.PredictUint(numBaselines, time0, bz0, time1, bz1, time2, bz2, time, out predictionLikelyWrongZ); if (predictionLikelyWrongX || predictionLikelyWrongY || predictionLikelyWrongZ) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } else { if ((bx0 != bx1 || by0 != by1 || bz0 != bz1) && (bx1 != bx2 || by1 != by2 || bz1 != bz2)) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } } outputStream.WriteUInt32(px); outputStream.WriteUInt32(py); outputStream.WriteUInt32(pz); break; } case NetworkSchema.FieldType.Quaternion: { uint bx0 = baselineStream0.ReadUInt32(); uint by0 = baselineStream0.ReadUInt32(); uint bz0 = baselineStream0.ReadUInt32(); uint bw0 = baselineStream0.ReadUInt32(); uint bx1 = baselineStream1.ReadUInt32(); uint by1 = baselineStream1.ReadUInt32(); uint bz1 = baselineStream1.ReadUInt32(); uint bw1 = baselineStream1.ReadUInt32(); uint bx2 = baselineStream2.ReadUInt32(); uint by2 = baselineStream2.ReadUInt32(); uint bz2 = baselineStream2.ReadUInt32(); uint bw2 = baselineStream2.ReadUInt32(); uint px = bx0; uint py = by0; uint pz = bz0; uint pw = bw0; if (!masked) { if (field.delta) { bool predictionLikelyWrongX; bool predictionLikelyWrongY; bool predictionLikelyWrongZ; bool predictionLikelyWrongW; px = NetworkPrediction.PredictUint(numBaselines, time0, bx0, time1, bx1, time2, bx2, time, out predictionLikelyWrongX); py = NetworkPrediction.PredictUint(numBaselines, time0, by0, time1, by1, time2, by2, time, out predictionLikelyWrongY); pz = NetworkPrediction.PredictUint(numBaselines, time0, bz0, time1, bz1, time2, bz2, time, out predictionLikelyWrongZ); pw = NetworkPrediction.PredictUint(numBaselines, time0, bw0, time1, bw1, time2, bw2, time, out predictionLikelyWrongW); if (predictionLikelyWrongX || predictionLikelyWrongY || predictionLikelyWrongZ || predictionLikelyWrongW) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } else { if ((bx0 != bx1 || by0 != by1 || bz0 != bz1 || bw0 != bw1) && (bx1 != bx2 || by1 != by2 || bz1 != bz2 || bw1 != bw2)) { fieldsChangedPrediction[fieldByteOffset] |= (byte)(1 << fieldBitOffset); } } } outputStream.WriteUInt32(px); outputStream.WriteUInt32(py); outputStream.WriteUInt32(pz); outputStream.WriteUInt32(pw); break; } case NetworkSchema.FieldType.String: case NetworkSchema.FieldType.ByteArray: { baselineStream1.SkipByteArray(field.arraySize); baselineStream2.SkipByteArray(field.arraySize); outputStream.CopyByteArray(ref baselineStream0, field.arraySize); //TODO: predict me! } break; } } //fieldsChangedPrediction = 0; outputStream.Flush(); }
public static int Read <TInputStream>(ref TInputStream input, NetworkSchema schema, byte[] outputData, byte[] baselineData, byte[] fieldsChangedPrediction, byte fieldMask, ref uint hash) where TInputStream : NetworkCompression.IInputStream { GameDebug.Assert(baselineData != null); var outputStream = new ByteOutputStream(outputData); var baselineStream = new ByteInputStream(baselineData); int numFields = schema.fields.Count; int skipContext = schema.id * NetworkConfig.maxContextsPerSchema + NetworkConfig.firstSchemaContext; for (int i = 0; i * 8 < numFields; i++) { uint value = input.ReadPackedNibble(skipContext + 2 * i + 0); value |= input.ReadPackedNibble(skipContext + 2 * i + 1) << 4; fieldsNotPredicted[i] = (byte)(value ^ fieldsChangedPrediction[i]); } for (int i = 0; i < numFields; ++i) { GameDebug.Assert(schema.fields[i].byteOffset == baselineStream.GetBytePosition()); int fieldStartContext = schema.fields[i].startContext; var field = schema.fields[i]; byte fieldByteOffset = (byte)((uint)i >> 3); byte fieldBitOffset = (byte)((uint)i & 0x7); bool skip = (fieldsNotPredicted[fieldByteOffset] & (1 << fieldBitOffset)) == 0; bool masked = ((field.fieldMask & fieldMask) != 0); skip = skip || masked; switch (field.fieldType) { case NetworkSchema.FieldType.Bool: { uint value = baselineStream.ReadUInt8(); if (!skip) { value = input.ReadRawBits(1); } if (!masked) { hash = NetworkUtils.SimpleHashStreaming(hash, value); } outputStream.WriteUInt8((byte)value); break; } case NetworkSchema.FieldType.UInt: case NetworkSchema.FieldType.Int: case NetworkSchema.FieldType.Float: { uint baseline = (uint)baselineStream.ReadBits(field.bits); uint value = baseline; if (!skip) { if (field.delta) { value = input.ReadPackedUIntDelta(baseline, fieldStartContext); } else { value = input.ReadRawBits(field.bits); } } if (!masked) { hash = NetworkUtils.SimpleHashStreaming(hash, value); } outputStream.WriteBits(value, field.bits); //RUTODO: fix this break; } case NetworkSchema.FieldType.Vector2: { uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); uint vx = bx; uint vy = by; if (!skip) { if (field.delta) { vx = input.ReadPackedUIntDelta(bx, fieldStartContext + 0); vy = input.ReadPackedUIntDelta(by, fieldStartContext + 1); } else { vx = input.ReadRawBits(field.bits); vy = input.ReadRawBits(field.bits); } } if (!masked) { hash = NetworkUtils.SimpleHashStreaming(hash, vx); hash = NetworkUtils.SimpleHashStreaming(hash, vy); } outputStream.WriteUInt32(vx); outputStream.WriteUInt32(vy); break; } case NetworkSchema.FieldType.Vector3: { uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); uint bz = baselineStream.ReadUInt32(); uint vx = bx; uint vy = by; uint vz = bz; if (!skip) { if (field.delta) { vx = input.ReadPackedUIntDelta(bx, fieldStartContext + 0); vy = input.ReadPackedUIntDelta(by, fieldStartContext + 1); vz = input.ReadPackedUIntDelta(bz, fieldStartContext + 2); } else { vx = input.ReadRawBits(field.bits); vy = input.ReadRawBits(field.bits); vz = input.ReadRawBits(field.bits); } } if (!masked) { hash = NetworkUtils.SimpleHashStreaming(hash, vx); hash = NetworkUtils.SimpleHashStreaming(hash, vy); hash = NetworkUtils.SimpleHashStreaming(hash, vz); } outputStream.WriteUInt32(vx); outputStream.WriteUInt32(vy); outputStream.WriteUInt32(vz); break; } case NetworkSchema.FieldType.Quaternion: { uint bx = baselineStream.ReadUInt32(); uint by = baselineStream.ReadUInt32(); uint bz = baselineStream.ReadUInt32(); uint bw = baselineStream.ReadUInt32(); uint vx = bx; uint vy = by; uint vz = bz; uint vw = bw; if (!skip) { if (field.delta) { vx = input.ReadPackedUIntDelta(bx, fieldStartContext + 0); vy = input.ReadPackedUIntDelta(by, fieldStartContext + 1); vz = input.ReadPackedUIntDelta(bz, fieldStartContext + 2); vw = input.ReadPackedUIntDelta(bw, fieldStartContext + 3); //RUTODO: normalize } else { vx = input.ReadRawBits(field.bits); vy = input.ReadRawBits(field.bits); vz = input.ReadRawBits(field.bits); vw = input.ReadRawBits(field.bits); } } if (!masked) { hash = NetworkUtils.SimpleHashStreaming(hash, vx); hash = NetworkUtils.SimpleHashStreaming(hash, vy); hash = NetworkUtils.SimpleHashStreaming(hash, vz); hash = NetworkUtils.SimpleHashStreaming(hash, vw); } outputStream.WriteUInt32(vx); outputStream.WriteUInt32(vy); outputStream.WriteUInt32(vz); outputStream.WriteUInt32(vw); break; } case NetworkSchema.FieldType.String: case NetworkSchema.FieldType.ByteArray: { // TODO : Do a better job with deltaing strings and buffers if (!skip) { baselineStream.SkipByteArray(field.arraySize); outputStream.CopyByteArray <TInputStream>(ref input, field.arraySize, fieldStartContext); } else { outputStream.CopyByteArray(ref baselineStream, field.arraySize); } if (!masked) { hash += 0; // TODO (hash strings and bytearrays as well) } } break; } } outputStream.Flush(); return(outputStream.GetBytePosition()); }