public void MapSchemaInt8Test() { var state = new SchemaTest.MapSchemaInt8.MapSchemaInt8(); byte[] bytes = { 128, 171, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 129, 1, 255, 1, 128, 0, 163, 98, 98, 98, 1, 128, 1, 163, 97, 97, 97, 1, 128, 2, 163, 50, 50, 49, 1, 128, 3, 163, 48, 50, 49, 1, 128, 4, 162, 49, 53, 1, 128, 5, 162, 49, 48, 1 }; var refs = new Colyseus.Schema.ColyseusReferenceTracker(); state.Decode(bytes, null, refs); Assert.AreEqual(state.status, "Hello world"); Assert.AreEqual(state.mapOfInt8["bbb"], 1); Assert.AreEqual(state.mapOfInt8["aaa"], 1); Assert.AreEqual(state.mapOfInt8["221"], 1); Assert.AreEqual(state.mapOfInt8["021"], 1); Assert.AreEqual(state.mapOfInt8["15"], 1); Assert.AreEqual(state.mapOfInt8["10"], 1); byte[] addBytes = { 255, 1, 0, 5, 2 }; state.Decode(addBytes, null, refs); Assert.AreEqual(state.mapOfInt8["bbb"], 1); Assert.AreEqual(state.mapOfInt8["aaa"], 1); Assert.AreEqual(state.mapOfInt8["221"], 1); Assert.AreEqual(state.mapOfInt8["021"], 1); Assert.AreEqual(state.mapOfInt8["15"], 1); Assert.AreEqual(state.mapOfInt8["10"], 2); }
public void ArraySchemaTypesTest() { var state = new SchemaTest.ArraySchemaTypes.ArraySchemaTypes(); byte[] bytes = { 128, 1, 129, 2, 130, 3, 131, 4, 255, 1, 128, 0, 5, 128, 1, 6, 255, 2, 128, 0, 0, 128, 1, 10, 128, 2, 20, 128, 3, 205, 192, 13, 255, 3, 128, 0, 163, 111, 110, 101, 128, 1, 163, 116, 119, 111, 128, 2, 165, 116, 104, 114, 101, 101, 255, 4, 128, 0, 232, 3, 0, 0, 128, 1, 192, 13, 0, 0, 128, 2, 72, 244, 255, 255, 255, 5, 128, 100, 129, 208, 156, 255, 6, 128, 100, 129, 208, 156 }; state.arrayOfSchemas.OnAdd += (value, key) => Debug.Log("onAdd, arrayOfSchemas => " + key); state.arrayOfNumbers.OnAdd += (value, key) => Debug.Log("onAdd, arrayOfNumbers => " + key); state.arrayOfStrings.OnAdd += (value, key) => Debug.Log("onAdd, arrayOfStrings => " + key); state.arrayOfInt32.OnAdd += (value, key) => Debug.Log("onAdd, arrayOfInt32 => " + key); var refs = new Colyseus.Schema.ColyseusReferenceTracker(); state.Decode(bytes, null, refs); Debug.Log("Decoded 1st time!"); Assert.AreEqual(2, state.arrayOfSchemas.Count); Assert.AreEqual(100, state.arrayOfSchemas[0].x); Assert.AreEqual(-100, state.arrayOfSchemas[0].y); Assert.AreEqual(100, state.arrayOfSchemas[1].x); Assert.AreEqual(-100, state.arrayOfSchemas[1].y); Assert.AreEqual(4, state.arrayOfNumbers.Count); Assert.AreEqual(0, state.arrayOfNumbers[0]); Assert.AreEqual(10, state.arrayOfNumbers[1]); Assert.AreEqual(20, state.arrayOfNumbers[2]); Assert.AreEqual(3520, state.arrayOfNumbers[3]); Assert.AreEqual(3, state.arrayOfStrings.Count); Assert.AreEqual("one", state.arrayOfStrings[0]); Assert.AreEqual("two", state.arrayOfStrings[1]); Assert.AreEqual("three", state.arrayOfStrings[2]); Assert.AreEqual(3, state.arrayOfInt32.Count); Assert.AreEqual(1000, state.arrayOfInt32[0]); Assert.AreEqual(3520, state.arrayOfInt32[1]); Assert.AreEqual(-3000, state.arrayOfInt32[2]); state.arrayOfSchemas.OnRemove += (value, key) => Debug.Log("onRemove, arrayOfSchemas => " + key); state.arrayOfNumbers.OnRemove += (value, key) => Debug.Log("onRemove, arrayOfNumbers => " + key); state.arrayOfStrings.OnRemove += (value, key) => Debug.Log("onRemove, arrayOfStrings => " + key); state.arrayOfInt32.OnRemove += (value, key) => Debug.Log("onRemove, arrayOfInt32 => " + key); byte[] popBytes = { 255, 1, 64, 1, 255, 2, 64, 3, 64, 2, 64, 1, 255, 4, 64, 2, 64, 1, 255, 3, 64, 2, 64, 1 }; state.Decode(popBytes, null, refs); Debug.Log("Decoded 2nd time!"); Assert.AreEqual(1, state.arrayOfSchemas.Count); Assert.AreEqual(1, state.arrayOfNumbers.Count); Assert.AreEqual(1, state.arrayOfStrings.Count); Assert.AreEqual(1, state.arrayOfInt32.Count); Debug.Log("FINISHED"); }
/// <summary> /// Clear all items and indices /// </summary> /// <param name="refs">Passed in for garbage collection, if needed</param> public void Clear(ColyseusReferenceTracker refs = null) { if (refs != null && HasSchemaChild) { foreach (IRef item in items.Values) { refs.Remove(item.__refId); } } indexes.Clear(); items.Clear(); }
public void MapSchemaTypesTest() { var state = new SchemaTest.MapSchemaTypes.MapSchemaTypes(); byte[] bytes = { 128, 1, 129, 2, 130, 3, 131, 4, 255, 1, 128, 0, 163, 111, 110, 101, 5, 128, 1, 163, 116, 119, 111, 6, 128, 2, 165, 116, 104, 114, 101, 101, 7, 255, 2, 128, 0, 163, 111, 110, 101, 1, 128, 1, 163, 116, 119, 111, 2, 128, 2, 165, 116, 104, 114, 101, 101, 205, 192, 13, 255, 3, 128, 0, 163, 111, 110, 101, 163, 79, 110, 101, 128, 1, 163, 116, 119, 111, 163, 84, 119, 111, 128, 2, 165, 116, 104, 114, 101, 101, 165, 84, 104, 114, 101, 101, 255, 4, 128, 0, 163, 111, 110, 101, 192, 13, 0, 0, 128, 1, 163, 116, 119, 111, 24, 252, 255, 255, 128, 2, 165, 116, 104, 114, 101, 101, 208, 7, 0, 0, 255, 5, 128, 100, 129, 204, 200, 255, 6, 128, 205, 44, 1, 129, 205, 144, 1, 255, 7, 128, 205, 244, 1, 129, 205, 88, 2 }; state.mapOfSchemas.OnAdd += (value, key) => Debug.Log("OnAdd, mapOfSchemas => " + key); state.mapOfNumbers.OnAdd += (value, key) => Debug.Log("OnAdd, mapOfNumbers => " + key); state.mapOfStrings.OnAdd += (value, key) => Debug.Log("OnAdd, mapOfStrings => " + key); state.mapOfInt32.OnAdd += (value, key) => Debug.Log("OnAdd, mapOfInt32 => " + key); state.mapOfSchemas.OnRemove += (value, key) => Debug.Log("OnRemove, mapOfSchemas => " + key); state.mapOfNumbers.OnRemove += (value, key) => Debug.Log("OnRemove, mapOfNumbers => " + key); state.mapOfStrings.OnRemove += (value, key) => Debug.Log("OnRemove, mapOfStrings => " + key); state.mapOfInt32.OnRemove += (value, key) => Debug.Log("OnRemove, mapOfInt32 => " + key); var refs = new Colyseus.Schema.ColyseusReferenceTracker(); state.Decode(bytes, null, refs); Assert.AreEqual(state.mapOfSchemas.Count, 3); Assert.AreEqual(state.mapOfSchemas["one"].x, 100); Assert.AreEqual(state.mapOfSchemas["one"].y, 200); Assert.AreEqual(state.mapOfSchemas["two"].x, 300); Assert.AreEqual(state.mapOfSchemas["two"].y, 400); Assert.AreEqual(state.mapOfSchemas["three"].x, 500); Assert.AreEqual(state.mapOfSchemas["three"].y, 600); Assert.AreEqual(state.mapOfNumbers.Count, 3); Assert.AreEqual(state.mapOfNumbers["one"], 1); Assert.AreEqual(state.mapOfNumbers["two"], 2); Assert.AreEqual(state.mapOfNumbers["three"], 3520); Assert.AreEqual(state.mapOfStrings.Count, 3); Assert.AreEqual(state.mapOfStrings["one"], "One"); Assert.AreEqual(state.mapOfStrings["two"], "Two"); Assert.AreEqual(state.mapOfStrings["three"], "Three"); Assert.AreEqual(state.mapOfInt32.Count, 3); Assert.AreEqual(state.mapOfInt32["one"], 3520); Assert.AreEqual(state.mapOfInt32["two"], -1000); Assert.AreEqual(state.mapOfInt32["three"], 2000); byte[] deleteBytes = { 255, 2, 64, 1, 64, 2, 255, 1, 64, 1, 64, 2, 255, 3, 64, 1, 64, 2, 255, 4, 64, 1, 64, 2 }; state.Decode(deleteBytes, null, refs); Assert.AreEqual(state.mapOfSchemas.Count, 1); Assert.AreEqual(state.mapOfNumbers.Count, 1); Assert.AreEqual(state.mapOfStrings.Count, 1); Assert.AreEqual(state.mapOfInt32.Count, 1); }
public void MapSchemaMoveNullifyTypeTest() { var state = new SchemaTest.MapSchemaMoveNullifyType.State(); byte[] bytes = { 129, 1, 64, 255, 1, 128, 0, 161, 48, 0 }; var refs = new Colyseus.Schema.ColyseusReferenceTracker(); state.Decode(bytes, null, refs); Assert.DoesNotThrow(() => { // FIXME: this test only passes because empty byte[] moveAndNullifyBytes = { 128, 1, 65 }; state.Decode(moveAndNullifyBytes, null, refs); }); }
public void InstanceSharingTypes() { var refs = new Colyseus.Schema.ColyseusReferenceTracker(); var client = new SchemaTest.InstanceSharing.State(); client.Decode(new byte[] { 130, 1, 131, 2, 128, 3, 129, 3, 255, 1, 255, 2, 255, 3, 128, 4, 255, 3, 128, 4, 255, 4, 128, 10, 129, 10, 255, 4, 128, 10, 129, 10 }, null, refs); Assert.AreEqual(client.player1, client.player2); Assert.AreEqual(client.player1.position, client.player2.position); Assert.AreEqual(refs.refCounts[client.player1.__refId], 2); Assert.AreEqual(5, refs.refs.Count); client.Decode(new byte[] { 130, 1, 131, 2, 64, 65 }, null, refs); Assert.AreEqual(null, client.player2); Assert.AreEqual(null, client.player2); Assert.AreEqual(3, refs.refs.Count); client.Decode(new byte[] { 255, 1, 128, 0, 5, 128, 1, 5, 128, 2, 5, 128, 3, 6, 255, 5, 128, 7, 255, 6, 128, 8, 255, 7, 128, 10, 129, 10, 255, 8, 128, 10, 129, 10 }, null, refs); Assert.AreEqual(client.arrayOfPlayers[0], client.arrayOfPlayers[1]); Assert.AreEqual(client.arrayOfPlayers[1], client.arrayOfPlayers[2]); Assert.AreNotEqual(client.arrayOfPlayers[2], client.arrayOfPlayers[3]); Assert.AreEqual(7, refs.refs.Count); client.Decode(new byte[] { 255, 1, 64, 3, 64, 2, 64, 1 }, null, refs); Assert.AreEqual(1, client.arrayOfPlayers.Count); Assert.AreEqual(5, refs.refs.Count); var previousArraySchemaRefId = client.arrayOfPlayers.__refId; // Replacing ArraySchema client.Decode(new byte[] { 130, 9, 255, 9, 128, 0, 10, 255, 10, 128, 11, 255, 11, 128, 10, 129, 20 }, null, refs); Assert.AreEqual(false, refs.refs.ContainsKey(previousArraySchemaRefId)); Assert.AreEqual(1, client.arrayOfPlayers.Count); Assert.AreEqual(5, refs.refs.Count); // Clearing ArraySchema client.Decode(new byte[] { 255, 9, 10 }, null, refs); Assert.AreEqual(0, client.arrayOfPlayers.Count); Assert.AreEqual(3, refs.refs.Count); }
/// <summary> /// Decode incoming data /// </summary> /// <param name="bytes">The incoming data</param> /// <param name="it"><see cref="Iterator" /> used to decode. If null, will create a new one</param> /// <param name="refs"> /// <see cref="ColyseusReferenceTracker" /> for all refs found through the decoding process. If null, will /// create a new one /// </param> /// <exception cref="Exception">If no decoding fails</exception> public void Decode(byte[] bytes, Iterator it = null, ColyseusReferenceTracker refs = null) { ColyseusDecoder decode = ColyseusDecoder.GetInstance(); if (it == null) { it = new Iterator(); } if (refs == null) { refs = new ColyseusReferenceTracker(); } int totalBytes = bytes.Length; int refId = 0; IRef _ref = this; this.refs = refs; refs.Add(refId, _ref); List <DataChange> changes = new List <DataChange>(); OrderedDictionary allChanges = new OrderedDictionary(); // Dictionary<int, List<DataChange>> allChanges.Add(refId, changes); while (it.Offset < totalBytes) { byte _byte = bytes[it.Offset++]; if (_byte == (byte)SPEC.SWITCH_TO_STRUCTURE) { refId = Convert.ToInt32(decode.DecodeNumber(bytes, it)); _ref = refs.Get(refId); // // Trying to access a reference that haven't been decoded yet. // if (_ref == null) { throw new Exception("refId not found: " + refId); } // create empty list of changes for this refId. changes = new List <DataChange>(); allChanges[(object)refId] = changes; continue; } bool isSchema = _ref is Schema; byte operation = (byte)(isSchema ? (_byte >> 6) << 6 // "compressed" index + operation : _byte); // "uncompressed" index + operation (array/map items) if (operation == (byte)OPERATION.CLEAR) { ((ISchemaCollection)_ref).Clear(refs); continue; } int fieldIndex; string fieldName = null; string fieldType = null; System.Type childType = null; if (isSchema) { fieldIndex = _byte % (operation == 0 ? 255 : operation); // FIXME: JS allows (0 || 255) ((Schema)_ref).fieldsByIndex.TryGetValue(fieldIndex, out fieldName); // fieldType = ((Schema)_ref).fieldTypes[fieldName]; ((Schema)_ref).fieldTypes.TryGetValue(fieldName ?? "", out fieldType); ((Schema)_ref).fieldChildTypes.TryGetValue(fieldName ?? "", out childType); } else { fieldName = ""; // FIXME fieldIndex = Convert.ToInt32(decode.DecodeNumber(bytes, it)); if (((ISchemaCollection)_ref).HasSchemaChild) { fieldType = "ref"; childType = ((ISchemaCollection)_ref).GetChildType(); } else { fieldType = ((ISchemaCollection)_ref).ChildPrimitiveType; } } object value = null; object previousValue = null; object dynamicIndex = null; if (!isSchema) { previousValue = _ref.GetByIndex(fieldIndex); if ((operation & (byte)OPERATION.ADD) == (byte)OPERATION.ADD) { // MapSchema dynamic index. dynamicIndex = ((ISchemaCollection)_ref).GetItems() is OrderedDictionary ? (object)decode.DecodeString(bytes, it) : fieldIndex; ((ISchemaCollection)_ref).SetIndex(fieldIndex, dynamicIndex); } else { dynamicIndex = ((ISchemaCollection)_ref).GetIndex(fieldIndex); } } else if (fieldName != null) // FIXME: duplicate check { previousValue = ((Schema)_ref)[fieldName]; } // // Delete operations // if ((operation & (byte)OPERATION.DELETE) == (byte)OPERATION.DELETE) { if (operation != (byte)OPERATION.DELETE_AND_ADD) { _ref.DeleteByIndex(fieldIndex); } // Flag `refId` for garbage collection. if (previousValue != null && previousValue is IRef) { refs.Remove(((IRef)previousValue).__refId); } value = null; } if (fieldName == null) { // // keep skipping next bytes until reaches a known structure // by local decoder. // Iterator nextIterator = new Iterator { Offset = it.Offset }; while (it.Offset < totalBytes) { if (decode.SwitchStructureCheck(bytes, it)) { nextIterator.Offset = it.Offset + 1; if (refs.Has(Convert.ToInt32(decode.DecodeNumber(bytes, nextIterator)))) { break; } } it.Offset++; } continue; } if (operation == (byte)OPERATION.DELETE) { // // FIXME: refactor me. // Don't do anything. // } else if (fieldType == "ref") { refId = Convert.ToInt32(decode.DecodeNumber(bytes, it)); value = refs.Get(refId); if (operation != (byte)OPERATION.REPLACE) { System.Type concreteChildType = GetSchemaType(bytes, it, childType); if (value == null) { value = CreateTypeInstance(concreteChildType); if (previousValue != null) { ((Schema)value).MoveEventHandlers((Schema)previousValue); if ( ((IRef)previousValue).__refId > 0 && refId != ((IRef)previousValue).__refId ) { refs.Remove(((IRef)previousValue).__refId); } } } refs.Add(refId, (IRef)value, value != previousValue); } } else if (childType == null) { // primitive values value = decode.DecodePrimitiveType(fieldType, bytes, it); } else { refId = Convert.ToInt32(decode.DecodeNumber(bytes, it)); value = refs.Get(refId); ISchemaCollection valueRef = refs.Has(refId) ? (ISchemaCollection)previousValue : (ISchemaCollection)Activator.CreateInstance(childType); value = valueRef.Clone(); // keep reference to nested childPrimitiveType. string childPrimitiveType; ((Schema)_ref).fieldChildPrimitiveTypes.TryGetValue(fieldName, out childPrimitiveType); ((ISchemaCollection)value).ChildPrimitiveType = childPrimitiveType; if (previousValue != null) { ((ISchemaCollection)value).MoveEventHandlers( (ISchemaCollection)previousValue); if ( ((IRef)previousValue).__refId > 0 && refId != ((IRef)previousValue).__refId ) { refs.Remove(((IRef)previousValue).__refId); List <DataChange> deletes = new List <DataChange>(); IDictionary items = ((ISchemaCollection)previousValue).GetItems(); foreach (object key in items.Keys) { deletes.Add(new DataChange { DynamicIndex = key, Op = (byte)OPERATION.DELETE, Value = null, PreviousValue = items[key] }); } allChanges[(object)((IRef)previousValue).__refId] = deletes; } } refs.Add(refId, (IRef)value, valueRef != previousValue); } bool hasChange = previousValue != value; if (value != null) { if (value is IRef) { ((IRef)value).__refId = refId; } if (_ref is Schema) { ((Schema)_ref)[fieldName] = value; } else if (_ref is ISchemaCollection) { ((ISchemaCollection)_ref).SetByIndex(fieldIndex, dynamicIndex, value); } } if (hasChange) { changes.Add(new DataChange { Op = operation, Field = fieldName, DynamicIndex = dynamicIndex, Value = value, PreviousValue = previousValue }); } } TriggerChanges(ref allChanges); refs.GarbageCollection(); }