Repository: colyseus/colyseus-unity3d Branch: master Commit: 08ba8e2303cf Files: 449 Total size: 807.8 KB Directory structure: gitextract_6zht0_qm/ ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE.md │ └── workflows/ │ ├── nuget-publish.yml │ ├── tests.yml │ └── upmsemver.yml ├── .gitignore ├── Assets/ │ ├── Colyseus/ │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── Documentation~/ │ │ │ ├── .gitkeep │ │ │ └── GettingStarted.md │ │ ├── Editor/ │ │ │ ├── Colyseus.Editor.asmdef │ │ │ ├── Colyseus.Editor.asmdef.meta │ │ │ ├── README.md │ │ │ ├── README.md.meta │ │ │ ├── RoomInspector.cs │ │ │ └── RoomInspector.cs.meta │ │ ├── Editor.meta │ │ ├── LICENSE │ │ ├── LICENSE.meta │ │ ├── Runtime/ │ │ │ ├── Colyseus/ │ │ │ │ ├── Auth.cs │ │ │ │ ├── Auth.cs.meta │ │ │ │ ├── Client.cs │ │ │ │ ├── Client.cs.meta │ │ │ │ ├── Connection.cs │ │ │ │ ├── Connection.cs.meta │ │ │ │ ├── HTTP.cs │ │ │ │ ├── HTTP.cs.meta │ │ │ │ ├── Manager.cs │ │ │ │ ├── Manager.cs.meta │ │ │ │ ├── Platform/ │ │ │ │ │ ├── ColyseusContext.cs │ │ │ │ │ ├── ColyseusContext.cs.meta │ │ │ │ │ ├── Defaults/ │ │ │ │ │ │ ├── ConsoleLogger.cs │ │ │ │ │ │ ├── ConsoleLogger.cs.meta │ │ │ │ │ │ ├── DefaultHttpClient.cs │ │ │ │ │ │ ├── DefaultHttpClient.cs.meta │ │ │ │ │ │ ├── InMemoryTokenStorage.cs │ │ │ │ │ │ └── InMemoryTokenStorage.cs.meta │ │ │ │ │ ├── Defaults.meta │ │ │ │ │ ├── IHttpClient.cs │ │ │ │ │ ├── IHttpClient.cs.meta │ │ │ │ │ ├── ILogger.cs │ │ │ │ │ ├── ILogger.cs.meta │ │ │ │ │ ├── ITokenStorage.cs │ │ │ │ │ ├── ITokenStorage.cs.meta │ │ │ │ │ ├── IWebSocket.cs │ │ │ │ │ ├── IWebSocket.cs.meta │ │ │ │ │ ├── PreserveAttribute.cs │ │ │ │ │ ├── PreserveAttribute.cs.meta │ │ │ │ │ ├── Unity/ │ │ │ │ │ │ ├── UnityHttpClient.cs │ │ │ │ │ │ ├── UnityHttpClient.cs.meta │ │ │ │ │ │ ├── UnityLogger.cs │ │ │ │ │ │ ├── UnityLogger.cs.meta │ │ │ │ │ │ ├── UnityPlatform.cs │ │ │ │ │ │ ├── UnityPlatform.cs.meta │ │ │ │ │ │ ├── UnitySettings.cs │ │ │ │ │ │ ├── UnitySettings.cs.meta │ │ │ │ │ │ ├── UnityTokenStorage.cs │ │ │ │ │ │ └── UnityTokenStorage.cs.meta │ │ │ │ │ └── Unity.meta │ │ │ │ ├── Platform.meta │ │ │ │ ├── Protocol/ │ │ │ │ │ ├── ErrorCode.cs │ │ │ │ │ ├── ErrorCode.cs.meta │ │ │ │ │ ├── MatchMakeResponse.cs │ │ │ │ │ ├── MatchMakeResponse.cs.meta │ │ │ │ │ ├── MessageHandler.cs │ │ │ │ │ ├── MessageHandler.cs.meta │ │ │ │ │ ├── Protocol.cs │ │ │ │ │ ├── Protocol.cs.meta │ │ │ │ │ ├── RoomAvailable.cs │ │ │ │ │ └── RoomAvailable.cs.meta │ │ │ │ ├── Protocol.meta │ │ │ │ ├── Room.cs │ │ │ │ ├── Room.cs.meta │ │ │ │ ├── Serializer/ │ │ │ │ │ ├── Conversion/ │ │ │ │ │ │ ├── BigEndianBitConverter.cs │ │ │ │ │ │ ├── BigEndianBitConverter.cs.meta │ │ │ │ │ │ ├── DoubleConverter.cs │ │ │ │ │ │ ├── DoubleConverter.cs.meta │ │ │ │ │ │ ├── EndianBitConverter.cs │ │ │ │ │ │ ├── EndianBitConverter.cs.meta │ │ │ │ │ │ ├── Endianness.cs │ │ │ │ │ │ ├── Endianness.cs.meta │ │ │ │ │ │ ├── LittleEndianBitConverter.cs │ │ │ │ │ │ └── LittleEndianBitConverter.cs.meta │ │ │ │ │ ├── Conversion.meta │ │ │ │ │ ├── NoneSerializer.cs │ │ │ │ │ ├── NoneSerializer.cs.meta │ │ │ │ │ ├── Schema/ │ │ │ │ │ │ ├── Callbacks/ │ │ │ │ │ │ │ ├── Callbacks.cs │ │ │ │ │ │ │ └── Callbacks.cs.meta │ │ │ │ │ │ ├── Callbacks.meta │ │ │ │ │ │ ├── Decoder.cs │ │ │ │ │ │ ├── Decoder.cs.meta │ │ │ │ │ │ ├── DynamicSchema.cs │ │ │ │ │ │ ├── DynamicSchema.cs.meta │ │ │ │ │ │ ├── ReferenceTracker.cs │ │ │ │ │ │ ├── ReferenceTracker.cs.meta │ │ │ │ │ │ ├── Schema.cs │ │ │ │ │ │ ├── Schema.cs.meta │ │ │ │ │ │ ├── TypeContext.cs │ │ │ │ │ │ ├── TypeContext.cs.meta │ │ │ │ │ │ ├── Types/ │ │ │ │ │ │ │ ├── ArraySchema.cs │ │ │ │ │ │ │ ├── ArraySchema.cs.meta │ │ │ │ │ │ │ ├── CustomType.cs │ │ │ │ │ │ │ ├── CustomType.cs.meta │ │ │ │ │ │ │ ├── MapSchema.cs │ │ │ │ │ │ │ ├── MapSchema.cs.meta │ │ │ │ │ │ │ ├── Reflection.cs │ │ │ │ │ │ │ └── Reflection.cs.meta │ │ │ │ │ │ ├── Types.meta │ │ │ │ │ │ ├── Utils/ │ │ │ │ │ │ │ ├── Decode.cs │ │ │ │ │ │ │ ├── Decode.cs.meta │ │ │ │ │ │ │ ├── Encode.cs │ │ │ │ │ │ │ └── Encode.cs.meta │ │ │ │ │ │ └── Utils.meta │ │ │ │ │ ├── Schema.meta │ │ │ │ │ ├── SchemaSerializer.cs │ │ │ │ │ ├── SchemaSerializer.cs.meta │ │ │ │ │ ├── Serializer.cs │ │ │ │ │ └── Serializer.cs.meta │ │ │ │ ├── Serializer.meta │ │ │ │ ├── Settings/ │ │ │ │ │ ├── Settings.cs │ │ │ │ │ └── Settings.cs.meta │ │ │ │ ├── Settings.meta │ │ │ │ ├── Transport/ │ │ │ │ │ ├── WebSocket.cs │ │ │ │ │ ├── WebSocket.cs.meta │ │ │ │ │ ├── WebSocketDispatchLoop.cs │ │ │ │ │ └── WebSocketDispatchLoop.cs.meta │ │ │ │ ├── Transport.meta │ │ │ │ ├── Utils/ │ │ │ │ │ ├── Exceptions.cs │ │ │ │ │ ├── Exceptions.cs.meta │ │ │ │ │ ├── ExtensionMethods.cs │ │ │ │ │ ├── ExtensionMethods.cs.meta │ │ │ │ │ ├── ObjectExtensions.cs │ │ │ │ │ ├── ObjectExtensions.cs.meta │ │ │ │ │ ├── UnityWebRequestAwaiter.cs │ │ │ │ │ └── UnityWebRequestAwaiter.cs.meta │ │ │ │ └── Utils.meta │ │ │ ├── Colyseus.meta │ │ │ ├── ColyseusSDK.asmdef │ │ │ ├── ColyseusSDK.asmdef.meta │ │ │ ├── Editor Default Resources/ │ │ │ │ ├── .gitkeep │ │ │ │ ├── Icons/ │ │ │ │ │ ├── ColyseusSettings.png.import │ │ │ │ │ ├── ColyseusSettings.png.import.meta │ │ │ │ │ └── ColyseusSettings.png.meta │ │ │ │ └── Icons.meta │ │ │ ├── Editor Default Resources.meta │ │ │ ├── Example.meta │ │ │ ├── Example~/ │ │ │ │ ├── ColyseusNetworkManager.cs │ │ │ │ ├── ColyseusNetworkManager.cs.meta │ │ │ │ ├── ConnectionManager.cs │ │ │ │ ├── ConnectionManager.cs.meta │ │ │ │ ├── ExampleScene.unity │ │ │ │ ├── ExampleScene.unity.meta │ │ │ │ ├── MyServerSettings.asset │ │ │ │ ├── MyServerSettings.asset.meta │ │ │ │ ├── Schema/ │ │ │ │ │ ├── Item.cs │ │ │ │ │ ├── Item.cs.meta │ │ │ │ │ ├── MyRoomState.cs │ │ │ │ │ ├── MyRoomState.cs.meta │ │ │ │ │ ├── Player.cs │ │ │ │ │ └── Player.cs.meta │ │ │ │ └── Schema.meta │ │ │ ├── GameDevWare.Serialization/ │ │ │ │ ├── ArrayExtensions.cs │ │ │ │ ├── ArrayExtensions.cs.meta │ │ │ │ ├── GenerateTypeSerializerAttribute.cs │ │ │ │ ├── GenerateTypeSerializerAttribute.cs.meta │ │ │ │ ├── IJsonReader.cs │ │ │ │ ├── IJsonReader.cs.meta │ │ │ │ ├── IJsonWriter.cs │ │ │ │ ├── IJsonWriter.cs.meta │ │ │ │ ├── IValueInfo.cs │ │ │ │ ├── IValueInfo.cs.meta │ │ │ │ ├── IndexedDictionary.cs │ │ │ │ ├── IndexedDictionary.cs.meta │ │ │ │ ├── Json.cs │ │ │ │ ├── Json.cs.meta │ │ │ │ ├── JsonMember.cs │ │ │ │ ├── JsonMember.cs.meta │ │ │ │ ├── JsonReader.cs │ │ │ │ ├── JsonReader.cs.meta │ │ │ │ ├── JsonReaderExtentions.cs │ │ │ │ ├── JsonReaderExtentions.cs.meta │ │ │ │ ├── JsonSerializationException.cs │ │ │ │ ├── JsonSerializationException.cs.meta │ │ │ │ ├── JsonStreamReader.cs │ │ │ │ ├── JsonStreamReader.cs.meta │ │ │ │ ├── JsonStreamWriter.cs │ │ │ │ ├── JsonStreamWriter.cs.meta │ │ │ │ ├── JsonStringBuilderReader.cs │ │ │ │ ├── JsonStringBuilderReader.cs.meta │ │ │ │ ├── JsonStringBuilderWriter.cs │ │ │ │ ├── JsonStringBuilderWriter.cs.meta │ │ │ │ ├── JsonStringReader.cs │ │ │ │ ├── JsonStringReader.cs.meta │ │ │ │ ├── JsonTextReader.cs │ │ │ │ ├── JsonTextReader.cs.meta │ │ │ │ ├── JsonTextWriter.cs │ │ │ │ ├── JsonTextWriter.cs.meta │ │ │ │ ├── JsonToken.cs │ │ │ │ ├── JsonToken.cs.meta │ │ │ │ ├── JsonUtils.cs │ │ │ │ ├── JsonUtils.cs.meta │ │ │ │ ├── JsonWriter.cs │ │ │ │ ├── JsonWriter.cs.meta │ │ │ │ ├── JsonWriterExtentions.cs │ │ │ │ ├── JsonWriterExtentions.cs.meta │ │ │ │ ├── MessagePack/ │ │ │ │ │ ├── BigEndianBitConverter.cs │ │ │ │ │ ├── BigEndianBitConverter.cs.meta │ │ │ │ │ ├── DefaultMsgPackExtensionTypeHandler.cs │ │ │ │ │ ├── DefaultMsgPackExtensionTypeHandler.cs.meta │ │ │ │ │ ├── EndianBitConverter.cs │ │ │ │ │ ├── EndianBitConverter.cs.meta │ │ │ │ │ ├── Endianness.cs │ │ │ │ │ ├── Endianness.cs.meta │ │ │ │ │ ├── LittleEndianBitConverter.cs │ │ │ │ │ ├── LittleEndianBitConverter.cs.meta │ │ │ │ │ ├── MsgPackExtensionType.cs │ │ │ │ │ ├── MsgPackExtensionType.cs.meta │ │ │ │ │ ├── MsgPackExtensionTypeHandler.cs │ │ │ │ │ ├── MsgPackExtensionTypeHandler.cs.meta │ │ │ │ │ ├── MsgPackReader.cs │ │ │ │ │ ├── MsgPackReader.cs.meta │ │ │ │ │ ├── MsgPackTimestamp.cs │ │ │ │ │ ├── MsgPackTimestamp.cs.meta │ │ │ │ │ ├── MsgPackType.cs │ │ │ │ │ ├── MsgPackType.cs.meta │ │ │ │ │ ├── MsgPackWriter.cs │ │ │ │ │ ├── MsgPackWriter.cs.meta │ │ │ │ │ ├── UnknownMsgPackExtentionTypeException.cs │ │ │ │ │ ├── UnknownMsgPackExtentionTypeException.cs.meta │ │ │ │ │ ├── UnknownMsgPackFormatException.cs │ │ │ │ │ └── UnknownMsgPackFormatException.cs.meta │ │ │ │ ├── MessagePack.meta │ │ │ │ ├── Metadata/ │ │ │ │ │ ├── DataMemberDescription.cs │ │ │ │ │ ├── DataMemberDescription.cs.meta │ │ │ │ │ ├── FieldDescription.cs │ │ │ │ │ ├── FieldDescription.cs.meta │ │ │ │ │ ├── MemberDescription.cs │ │ │ │ │ ├── MemberDescription.cs.meta │ │ │ │ │ ├── MetadataReflection.cs │ │ │ │ │ ├── MetadataReflection.cs.meta │ │ │ │ │ ├── PropertyDescription.cs │ │ │ │ │ ├── PropertyDescription.cs.meta │ │ │ │ │ ├── TypeDescription.cs │ │ │ │ │ └── TypeDescription.cs.meta │ │ │ │ ├── Metadata.meta │ │ │ │ ├── MsgPack.cs │ │ │ │ ├── MsgPack.cs.meta │ │ │ │ ├── PathSegment.cs │ │ │ │ ├── PathSegment.cs.meta │ │ │ │ ├── ReflectionExtentions.cs │ │ │ │ ├── ReflectionExtentions.cs.meta │ │ │ │ ├── SerializationContext.cs │ │ │ │ ├── SerializationContext.cs.meta │ │ │ │ ├── SerializationOptions.cs │ │ │ │ ├── SerializationOptions.cs.meta │ │ │ │ ├── Serializers/ │ │ │ │ │ ├── ArraySerializer.cs │ │ │ │ │ ├── ArraySerializer.cs.meta │ │ │ │ │ ├── BinarySerializer.cs │ │ │ │ │ ├── BinarySerializer.cs.meta │ │ │ │ │ ├── BoundsSerializer.cs │ │ │ │ │ ├── BoundsSerializer.cs.meta │ │ │ │ │ ├── DateTimeOffsetSerializer.cs │ │ │ │ │ ├── DateTimeOffsetSerializer.cs.meta │ │ │ │ │ ├── DateTimeSerializer.cs │ │ │ │ │ ├── DateTimeSerializer.cs.meta │ │ │ │ │ ├── DictionaryEntrySerializer.cs │ │ │ │ │ ├── DictionaryEntrySerializer.cs.meta │ │ │ │ │ ├── DictionarySerializer.cs │ │ │ │ │ ├── DictionarySerializer.cs.meta │ │ │ │ │ ├── EnumNumberSerializer.cs │ │ │ │ │ ├── EnumNumberSerializer.cs.meta │ │ │ │ │ ├── EnumSerializer.cs │ │ │ │ │ ├── EnumSerializer.cs.meta │ │ │ │ │ ├── GuidSerializer.cs │ │ │ │ │ ├── GuidSerializer.cs.meta │ │ │ │ │ ├── Matrix4x4Serializer.cs │ │ │ │ │ ├── Matrix4x4Serializer.cs.meta │ │ │ │ │ ├── MsgPackExtensionTypeSerializer.cs │ │ │ │ │ ├── MsgPackExtensionTypeSerializer.cs.meta │ │ │ │ │ ├── MsgPackTimestampSerializer.cs │ │ │ │ │ ├── MsgPackTimestampSerializer.cs.meta │ │ │ │ │ ├── ObjectSerializer.cs │ │ │ │ │ ├── ObjectSerializer.cs.meta │ │ │ │ │ ├── PrimitiveTypeSerializer.cs │ │ │ │ │ ├── PrimitiveTypeSerializer.cs.meta │ │ │ │ │ ├── QuaternionSerializer.cs │ │ │ │ │ ├── QuaternionSerializer.cs.meta │ │ │ │ │ ├── RectSerializer.cs │ │ │ │ │ ├── RectSerializer.cs.meta │ │ │ │ │ ├── StreamSerializer.cs │ │ │ │ │ ├── StreamSerializer.cs.meta │ │ │ │ │ ├── TimeSpanSerializer.cs │ │ │ │ │ ├── TimeSpanSerializer.cs.meta │ │ │ │ │ ├── UriSerializer.cs │ │ │ │ │ ├── UriSerializer.cs.meta │ │ │ │ │ ├── Vector2Serializer.cs │ │ │ │ │ ├── Vector2Serializer.cs.meta │ │ │ │ │ ├── Vector3Serializer.cs │ │ │ │ │ ├── Vector3Serializer.cs.meta │ │ │ │ │ ├── Vector4Serializer.cs │ │ │ │ │ ├── Vector4Serializer.cs.meta │ │ │ │ │ ├── VersionSerializer.cs │ │ │ │ │ └── VersionSerializer.cs.meta │ │ │ │ ├── Serializers.meta │ │ │ │ ├── TypeSerializer.cs │ │ │ │ ├── TypeSerializer.cs.meta │ │ │ │ ├── TypeSerializerAttribute.cs │ │ │ │ └── TypeSerializerAttribute.cs.meta │ │ │ ├── GameDevWare.Serialization.meta │ │ │ └── WebSocket.meta │ │ ├── Runtime.meta │ │ ├── Tests/ │ │ │ ├── .gitkeep │ │ │ ├── Colyseus.Editor.Tests.asmdef │ │ │ ├── Colyseus.Editor.Tests.asmdef.meta │ │ │ ├── Editor/ │ │ │ │ ├── ColyseusTests.meta │ │ │ │ ├── ServerSettingsEditor.cs │ │ │ │ └── ServerSettingsEditor.cs.meta │ │ │ ├── Editor.meta │ │ │ ├── Runtime/ │ │ │ │ └── .gitkeep │ │ │ └── Runtime.meta │ │ ├── Tests.meta │ │ ├── package.json │ │ └── package.json.meta │ └── Colyseus.meta ├── CLAUDE.md ├── LICENSE ├── Packages/ │ ├── manifest.json │ └── packages-lock.json ├── ProjectSettings/ │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── NetworkManager.asset │ ├── PackageManagerSettings.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityConnectSettings.asset │ ├── VFXManager.asset │ ├── VersionControlSettings.asset │ └── XRSettings.asset ├── README.md ├── UserSettings/ │ └── EditorUserSettings.asset ├── nuget/ │ ├── Colyseus.csproj │ ├── README.md │ └── tests/ │ ├── HTTPTest.cs │ ├── HTTPTest.cs.meta │ ├── IntegrationTest.cs │ ├── IntegrationTest.cs.meta │ ├── Schema/ │ │ ├── ArraySchemaClear/ │ │ │ ├── ArraySchemaClear.cs │ │ │ └── ArraySchemaClear.cs.meta │ │ ├── ArraySchemaClear.meta │ │ ├── ArraySchemaMultipleSplice/ │ │ │ ├── Item.cs │ │ │ ├── Item.cs.meta │ │ │ ├── MultipleArraySpliceState.cs │ │ │ ├── MultipleArraySpliceState.cs.meta │ │ │ ├── Player.cs │ │ │ └── Player.cs.meta │ │ ├── ArraySchemaMultipleSplice.meta │ │ ├── ArraySchemaTypes/ │ │ │ ├── ArraySchemaTypes.cs │ │ │ ├── ArraySchemaTypes.cs.meta │ │ │ ├── IAmAChild.cs │ │ │ └── IAmAChild.cs.meta │ │ ├── ArraySchemaTypes.meta │ │ ├── BackwardsForwards/ │ │ │ ├── PlayerV1.cs │ │ │ ├── PlayerV1.cs.meta │ │ │ ├── PlayerV2.cs │ │ │ ├── PlayerV2.cs.meta │ │ │ ├── StateV1.cs │ │ │ ├── StateV1.cs.meta │ │ │ ├── StateV2.cs │ │ │ └── StateV2.cs.meta │ │ ├── BackwardsForwards.meta │ │ ├── Callbacks/ │ │ │ ├── CallbacksState.cs │ │ │ ├── CallbacksState.cs.meta │ │ │ ├── Container.cs │ │ │ ├── Container.cs.meta │ │ │ ├── Item.cs │ │ │ ├── Item.cs.meta │ │ │ ├── Player.cs │ │ │ ├── Player.cs.meta │ │ │ ├── Vec3.cs │ │ │ └── Vec3.cs.meta │ │ ├── Callbacks.meta │ │ ├── ChildSchemaTypes/ │ │ │ ├── ChildSchemaTypes.cs │ │ │ ├── ChildSchemaTypes.cs.meta │ │ │ ├── IAmAChild.cs │ │ │ └── IAmAChild.cs.meta │ │ ├── ChildSchemaTypes.meta │ │ ├── FilteredTypes/ │ │ │ ├── Player.cs │ │ │ ├── Player.cs.meta │ │ │ ├── State.cs │ │ │ └── State.cs.meta │ │ ├── FilteredTypes.meta │ │ ├── InheritedTypes/ │ │ │ ├── Bot.cs │ │ │ ├── Bot.cs.meta │ │ │ ├── Entity.cs │ │ │ ├── Entity.cs.meta │ │ │ ├── InheritedTypes.cs │ │ │ ├── InheritedTypes.cs.meta │ │ │ ├── Player.cs │ │ │ └── Player.cs.meta │ │ ├── InheritedTypes.meta │ │ ├── InstanceSharingTypes/ │ │ │ ├── Player.cs │ │ │ ├── Player.cs.meta │ │ │ ├── Position.cs │ │ │ ├── Position.cs.meta │ │ │ ├── State.cs │ │ │ └── State.cs.meta │ │ ├── InstanceSharingTypes.meta │ │ ├── Item.cs │ │ ├── Item.cs.meta │ │ ├── MapSchemaInt8/ │ │ │ ├── MapSchemaInt8.cs │ │ │ └── MapSchemaInt8.cs.meta │ │ ├── MapSchemaInt8.meta │ │ ├── MapSchemaMoveNullifyType/ │ │ │ ├── State.cs │ │ │ └── State.cs.meta │ │ ├── MapSchemaMoveNullifyType.meta │ │ ├── MapSchemaTypes/ │ │ │ ├── IAmAChild.cs │ │ │ ├── IAmAChild.cs.meta │ │ │ ├── MapSchemaTypes.cs │ │ │ └── MapSchemaTypes.cs.meta │ │ ├── MapSchemaTypes.meta │ │ ├── MyRoomState.cs │ │ ├── MyRoomState.cs.meta │ │ ├── Player.cs │ │ ├── Player.cs.meta │ │ ├── PrimitiveTypes/ │ │ │ ├── PrimitiveTypes.cs │ │ │ └── PrimitiveTypes.cs.meta │ │ └── PrimitiveTypes.meta │ ├── Schema.meta │ ├── SchemaDeserializerTest.cs │ ├── SchemaDeserializerTest.cs.meta │ ├── SerializationTest.cs │ ├── SerializationTest.cs.meta │ └── WebSocketTransportTest.cs ├── nuget-monogame/ │ ├── Colyseus.MonoGame.csproj │ ├── ColyseusGameComponent.cs │ └── README.md └── unity-setup.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true indent_style = space indent_size = 2 [*.{js,ts}] indent_style = space indent_size = 2 [*.{cs}] indent_style = tab indent_size = 2 ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: endel # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel custom: https://www.paypal.me/endeld # Replace with a single custom sponsorship URL ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ================================================ FILE: .github/workflows/nuget-publish.yml ================================================ name: NuGet Publish on: push: branches: - master pull_request: branches: - master jobs: build: name: Build & Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 10.0.x - name: Test run: dotnet test nuget/Colyseus.csproj --filter "FullyQualifiedName!~IntegrationTest" --verbosity normal publish: name: Publish to NuGet runs-on: ubuntu-latest needs: [build] if: github.event_name == 'push' && github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v4 with: fetch-depth: 2 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 10.0.x - name: Check for version change id: version run: | CURRENT=$(python3 -c "import json; print(json.load(open('Assets/Colyseus/package.json'))['version'])") PREVIOUS=$(git show HEAD~1:Assets/Colyseus/package.json 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['version'])" 2>/dev/null || echo "") echo "current=$CURRENT" >> $GITHUB_OUTPUT echo "previous=$PREVIOUS" >> $GITHUB_OUTPUT if [ "$CURRENT" != "$PREVIOUS" ]; then echo "changed=true" >> $GITHUB_OUTPUT echo "Version changed: $PREVIOUS -> $CURRENT" else echo "changed=false" >> $GITHUB_OUTPUT echo "Version unchanged: $CURRENT" fi - name: Pack if: steps.version.outputs.changed == 'true' run: | dotnet pack nuget/Colyseus.csproj -c Release -o $GITHUB_WORKSPACE/packages/ dotnet nuget add source $GITHUB_WORKSPACE/packages/ --name local dotnet pack nuget-monogame/Colyseus.MonoGame.csproj -c Release -o $GITHUB_WORKSPACE/packages/ - name: Publish to NuGet if: steps.version.outputs.changed == 'true' env: NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} run: dotnet nuget push $GITHUB_WORKSPACE/packages/*.nupkg --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json --skip-duplicate ================================================ FILE: .github/workflows/tests.yml ================================================ name: Tests on: push: branches: [master, universal] pull_request: branches: [master, universal] jobs: unit-tests: name: Unit Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 10.0.x - name: Run unit tests run: dotnet test nuget/Colyseus.csproj --filter "FullyQualifiedName!~IntegrationTest" --verbosity normal integration-tests: name: Integration Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 10.0.x - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' - name: Clone test server run: git clone https://github.com/colyseus/sdks-test-server.git sdks-test-server - name: Install test server dependencies working-directory: sdks-test-server run: npm install - name: Start test server working-directory: sdks-test-server run: npx tsx src/index.ts > ../server.log 2>&1 & - name: Wait for server to be ready run: | for i in $(seq 1 30); do if curl -s http://localhost:2567 > /dev/null 2>&1; then echo "Server is ready!" exit 0 fi echo "Waiting for server... ($i/30)" sleep 1 done echo "Server failed to start" cat server.log exit 1 - name: Run integration tests run: dotnet test nuget/Colyseus.csproj --filter "FullyQualifiedName~IntegrationTest" --verbosity normal - name: Print server logs if: always() run: cat server.log || true ================================================ FILE: .github/workflows/upmsemver.yml ================================================ name: Update Unity UPM semantic versioning on: push: branches: - master - dev jobs: checkSemver: name: Check for Semver Change runs-on: ubuntu-latest outputs: semver-updated: ${{ steps.check.outputs.changed }} steps: - name: Checkout Code uses: actions/checkout@v2 - uses: EndBug/version-check@v2 id: check with: file-name: Assets/Colyseus/package.json diff-search: true updateUPM: name: Update UPM branch runs-on: ubuntu-latest needs: [checkSemver] if: needs.checkSemver.outputs.semver-updated == 'true' steps: - uses: actions/checkout@v2 - name: Fetch NativeWebSocket run: bash unity-setup.sh - name: Remove test symlink run: rm -rf Assets/Colyseus/Tests - name: Push package directory to subtree uses: s0/git-publish-subdir-action@develop env: REPO: self BRANCH: upm FOLDER: Assets/Colyseus/ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} createPackage: name: Create Unity Package and Release runs-on: ubuntu-latest needs: [checkSemver] if: needs.checkSemver.outputs.semver-updated == 'true' steps: - name: Checkout Code uses: actions/checkout@v2 - name: Fetch NativeWebSocket run: bash unity-setup.sh - name: Remove test symlink run: rm -rf Assets/Colyseus/Tests - name: Gather files run: | echo "Assets/Colyseus.meta" > metaList find Assets/Colyseus/ -name \*.meta >> metaList - name: Extract Version id: version uses: notiz-dev/github-action-json-property@release with: path: 'Assets/Colyseus/package.json' prop_path: 'version' - run: echo ${{steps.version.outputs.prop}} - name: Create Directory run: mkdir Release - name: Generate Unity Package id: build-package uses: pCYSl5EDgo/create-unitypackage@v1.2.3 with: package-path: 'Colyseus_Plugin.unitypackage' include-files: metaList - name: Upload Package uses: actions/upload-artifact@master with: path: ./Colyseus_Plugin.unitypackage name: Colyseus_Plugin - name: Changelog uses: scottbrenner/generate-changelog-action@master id: Changelog - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ steps.version.outputs.prop }} release_name: ${{ steps.version.outputs.prop }} body: | ${{ steps.Changelog.outputs.changelog }} draft: false prerelease: false - name: Upload Release Asset id: upload-release-asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps asset_path: ./Colyseus_Plugin.unitypackage asset_name: Colyseus_Plugin.unitypackage asset_content_type: application/x-gzip ================================================ FILE: .gitignore ================================================ # Server build files Server/build # NuGet Dependencies /Assets/Colyseus/Runtime/WebSocket #User Specific *.userprefs *.usertasks .DS_Store .vs/ .vsconfig .idea/ #Mono Project Files *.pidb *.resources test-results/ bin obj #NuGet packages Colyseus/packages !packages/repositories.config # Node.js node_modules npm-debug.log package-lock.json # Unity /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ /[Bb]uild/ /[Bb]uilds/ /Assets/AssetStoreTools* /Assets/Colyseus/Runtime/Example/ # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ *.csproj !nuget/**/*.csproj !nuget-monogame/**/*.csproj *.unityproj *.sln *.suo *.tmp *.user *.userprefs *.pidb *.booproj *.svd # Godot *.uid # Unity3D Generated File On Crash Reports sysinfo.txt # Builds *.apk *.unitypackage # Logs /Logs/ ================================================ FILE: Assets/Colyseus/.editorconfig ================================================ [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true indent_style = tab indent_size = 4 [*.{js,ts}] indent_style = space indent_size = 2 [*.{cs}] indent_style = tab indent_size = 4 ================================================ FILE: Assets/Colyseus/.gitignore ================================================ #User Specific *.userprefs *.usertasks .DS_Store .vs/ #Mono Project Files *.pidb *.resources test-results/ bin obj #NuGet packages Colyseus/packages !packages/repositories.config # Node.js node_modules npm-debug.log package-lock.json # Unity /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ /[Bb]uild/ /[Bb]uilds/ /Assets/AssetStoreTools* # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ *.csproj *.unityproj *.sln *.suo *.tmp *.user *.userprefs *.pidb *.booproj *.svd # Unity3D Generated File On Crash Reports sysinfo.txt # Builds *.apk *.unitypackage # Logs /Logs/ ================================================ FILE: Assets/Colyseus/Documentation~/.gitkeep ================================================ ================================================ FILE: Assets/Colyseus/Documentation~/GettingStarted.md ================================================  # Unity SDK For more information, please visit our [documentation site](https://docs.colyseus.io/) # Setup Here we'll be going over the steps to get your Unity client up and running and connected to a Colyseus server. Topics covered include: - Running the server locally - Server settings - Connecting to a server - Connecting to a room - Communicating with a room, and the room's state. The topics should be enough for you to set up a basic client on your own, however, you are welcome to use and modify the included example code to suit your needs. ## Running the server locally To run the demonstration server locally, run the following commands in your terminal: ``` cd Server npm install npm start ``` The built-in demonstration comes with a single [room handler](https://github.com/colyseus/colyseus-unity3d/blob/master/Server/src/rooms/MyRoom.ts), containing a suggested way of handling entities and players. Feel free to change all of it to fit your needs! ## Creating a Colyseus Settings Object: - Right-click anywhere in the Project folder, select "Create", select "Colyseus", and click "Generate ColyseusSettings Scriptable Object" - Fill in the fields as necessary. - **Server Address** - The address to your Colyseus server. - **Server Port** - The port to your Colyseus server. - **Use secure protocol** - Check this if requests and messages to your server should use the "https" and "wss" protocols. - **Default headers** - You can add an unlimited number of default headers for non web socket requests to your server. - The default headers are used by the `ColyseusRequest` class. - An example header could have a `"Name"` of `"Content-Type"` and a `"Value"` of `"application/json"` ## Colyseus Manager: - You will need to create your own Manager script that inherits from `ColyseusManager` or use and modify the provided `ExampleManager`. ```csharp public class ExampleManager : ColyseusManager ``` - Make an in-scene manager object to host your custom Manager script. - Provide your Manager with a reference to your Colyseus Settings object in the scene inspector. ## Client: - Call the `InitializeClient()` method of your Manager to create a `ColyseusClient` object which is stored in the `client` variable of `ColyseusManager`. This will be used to create/join rooms and form a connection with the server. ```csharp ExampleManager.Instance.InitializeClient(); ``` - If your Manager has additional classes that need reference to your `Client`, you can override `InitializeClient` and make those connections in there. ```csharp //In ExampleManager.cs public override void InitializeClient() { base.InitializeClient(); //Pass the newly created Client reference to our RoomController _roomController.SetClient(client); } ``` - You can get available rooms on the server by calling `GetAvailableRooms` of `ColyseusClient`: ```csharp return await GetAvailableRooms(roomName, headers); ``` ## Connecting to a Room: - There are several ways to create and/or join a room. - You can create a room by calling the `Create` method of `ColyseusClient` which will automatically create an instance of the room on the server and join it: ```csharp ExampleRoomState room = await client.Create(roomName); ``` - You can join a specific room by calling `JoinById`: ```csharp ExampleRoomState room = await client.JoinById(roomId); ``` - You can call the `JoinOrCreate` method of `ColyseusClient` which will matchmake into an available room, if able to, or will create a new instance of the room and then join it on the server: ```csharp ExampleRoomState room = await client.JoinOrCreate(roomName); ``` ## Room Options: - When creating a new room you have the ability to pass in a dictionary of room options, such as a minimum number of players required to start a game or the name of the custom logic file to run on your server. - Options are of type `object` and are keyed by the type `string`: ```csharp Dictionary roomOptions = new Dictionary { ["YOUR_ROOM_OPTION_1"] = "option 1", ["YOUR_ROOM_OPTION_2"] = "option 2" }; ExampleRoomState room = await ExampleManager.Instance.JoinOrCreate(roomName, roomOptions); ``` ## Room Events: `ColyseusRoom` has various events that you will want to subscribe to: ### OnJoin - Gets called after the client has successfully connected to the room. ### OnLeave - Gets called after the client has been disconnected from the room. - Has a `WebSocketCloseCode` parameter with the reason for the disconnection. ```csharp room.OnLeave += OnLeaveRoom; ``` ### OnStateChange - Any time the room's state changes, including the initial state, this event will get fired. ```csharp room.OnStateChange += OnStateChangeHandler; private static void OnStateChangeHandler(ExampleRoomState state, bool isFirstState) { // Do something with the state } ``` ### OnError - When a room related error occurs on the server it will be reported with this event. - Has parameters for an error code and an error message. ## Room Messages: You have the ability to listen for or to send custom messages from/to a room instance on the server. ### OnMessage - To add a listener you call `OnMessage` passing in the type and the action to be taken when that message is received by the client. - Messages are useful for events that occur in the room on the server. (Take a look at our [tech demos](https://docs.colyseus.io/demo/shooting-gallery/) for use case examples of using `OnMessage`) ```csharp room.OnMessage("onUserJoin", currentNetworkedUser => { _currentNetworkedUser = currentNetworkedUser; }); ``` ### Send - To send a custom message to the room on the server use the `Send` method of `ColyseusRoom` - Specify the `type` and an optional `message` parameters to send to your room. ```csharp room.Send("createEntity", new EntityCreationMessage() { creationId = creationId, attributes = attributes }); ``` ### Room State: > See how to generate your `RoomState` from [State Handling](https://docs.colyseus.io/state/schema/#client-side-schema-generation) - Each room holds its own state. The mutations of the state are synchronized automatically to all connected clients. - In regards to room state synchronization: - When the user successfully joins the room, they receive the full state from the server. - At every `patchRate`, binary patches of the state are sent to every client (default is 50ms) - `onStateChange` is called on the client-side after every patch received from the server. - Each serialization method has its own particular way to handle incoming state patches. - `ColyseusRoomState` is the base room state you will want your room state to inherit from. - Take a look at our tech demos for implementation examples of synchronizable data in a room's state such as networked entities, networked users, or room attributes. ([Shooting Gallery Tech Demo](https://docs.colyseus.io/demo/shooting-gallery/)) ```csharp public class ExampleRoomState : Schema { [Type(0, "map", typeof(MapSchema))] public MapSchema networkedEntities = new MapSchema(); [Type(1, "map", typeof(MapSchema))] public MapSchema networkedUsers = new MapSchema(); [Type(2, "map", typeof(MapSchema), "string")] public MapSchema attributes = new MapSchema(); } ``` ## Debugging If you set a breakpoint in your application while the WebSocket connection is open, the connection will be closed automatically after 3 seconds due to inactivity. To prevent the WebSocket connection from dropping, use `pingInterval: 0` in your server code during development: ```typescript import { Server, RedisPresence } from "colyseus"; const gameServer = new Server({ // ... pingInterval: 0 // HERE }); ``` Make sure to have a `pingInterval` higher than `0` on production. The default `pingInterval` value is `3000`. ================================================ FILE: Assets/Colyseus/Editor/Colyseus.Editor.asmdef ================================================ { "name": "Colyseus.Editor", "rootNamespace": "ColyseusEditor", "references": [ "ColyseusSDK" ], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } ================================================ FILE: Assets/Colyseus/Editor/Colyseus.Editor.asmdef.meta ================================================ fileFormatVersion: 2 guid: e4cb50f060ca4467c819ea269f994a8b AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Editor/README.md ================================================ # Colyseus Room Inspector Unity Editor tool for inspecting connected Colyseus room states in real-time during Play mode. ## Usage 1. Open **Window > Colyseus > Room Inspector** 2. Enter Play mode and connect to a Colyseus server 3. Active rooms are automatically discovered and displayed ### Toolbar | Button | Description | |--------|-------------| | **Auto Refresh** | Toggle automatic updates (every 0.5s) | | **Refresh Now** | Manually refresh the display | | **Copy State JSON** | Copy current state to clipboard | ### Example Output Given a room with `MapSchema players` and `float gameTime`: ``` Room: my_room (abc123) +-- Connection Info | +-- Room ID: abc123 | +-- Session ID: xyz789 | +-- Connection: Connected | +-- Source Object: NetworkManager +-- Room State +-- State Type: MyRoomState +-- players (MapSchema) [2 items] | +-- [player1] (Player) | | +-- x: 10.5 | | +-- y: 20.3 | | +-- name: "Alice" | +-- [player2] (Player) | +-- x: 15.2 | +-- y: 18.7 | +-- name: "Bob" +-- gameTime: 45.2 ``` ## Supported Types - Primitives (int, float, string, bool) - Nested Schema objects - `MapSchema` and `ArraySchema` collections ## Limitations - **Play mode only** -- not available in Edit mode - **Read-only** -- cannot edit state values - Collections limited to **100 items** displayed - Nesting limited to **10 levels** deep ## Troubleshooting **"No active Colyseus rooms found"** -- Ensure you are in Play mode and have connected to a room. The inspector discovers rooms by scanning MonoBehaviour fields via reflection. **State shows as "null"** -- The room is connected but hasn't received the initial state yet. Wait a moment or check your server-side room. **Values not updating** -- Check that Auto Refresh is enabled in the toolbar and that the room is still connected. ================================================ FILE: Assets/Colyseus/Editor/README.md.meta ================================================ fileFormatVersion: 2 guid: 6aa8c0736e5e54df49e298b67fd58dcc TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Editor/RoomInspector.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using Colyseus; using Colyseus.Schema; using UnityEditor; using UnityEngine; using System.Text; using GameDevWare.Serialization; namespace Colyseus.Editor { /// /// Static initializer to automatically capture message types from rooms /// static class RoomMessageType { // Shared storage for message types across all rooms public static readonly Dictionary> CapturedMessageTypes = new Dictionary>(); [UnityEditor.InitializeOnLoadMethod] private static void Initialize() { // Subscribe to play mode state changes to reset tracking EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } private static void OnPlayModeStateChanged(PlayModeStateChange state) { if (state == PlayModeStateChange.ExitingPlayMode || state == PlayModeStateChange.EnteredEditMode) { // Clear captured data when exiting play mode CapturedMessageTypes.Clear(); } } } /// /// Unity Editor window for inspecting connected Colyseus room states in real-time /// public class RoomInspector : EditorWindow { private Vector2 _scrollPosition; private bool _autoRefresh = true; private double _lastRefreshTime; private const double RefreshInterval = 0.5; // Refresh every 0.5 seconds private Dictionary _foldouts = new Dictionary(); private Dictionary> _messageInputs = new Dictionary>(); private Dictionary _selectedMessageTypeIndex = new Dictionary(); private Dictionary _lastSelectedMessageType = new Dictionary(); private Dictionary _rawJsonInputs = new Dictionary(); private Dictionary _useRawJson = new Dictionary(); private Dictionary _customMessageTypes = new Dictionary(); // Splitter state for Room State / Messages sections private Dictionary _roomSplitterPosition = new Dictionary(); private Dictionary _isResizingRoomSplitter = new Dictionary(); private const float MinPanelHeight = 150f; private const float SplitterHeight = 4f; // Tab selection for rooms private int _selectedRoomIndex = 0; [MenuItem("Window/Colyseus/Room Inspector (experimental)")] public static void ShowWindow() { Debug.LogWarning("The Colyseus Room Inspector is experimental. Please report any issues to https://github.com/colyseus/colyseus-unity-sdk/issues"); var window = GetWindow("Colyseus Room Inspector"); window.minSize = new Vector2(400, 300); window.Show(); } private void OnEnable() { EditorApplication.update += OnEditorUpdate; } private void OnDisable() { EditorApplication.update -= OnEditorUpdate; } private void OnEditorUpdate() { if (_autoRefresh && EditorApplication.timeSinceStartup - _lastRefreshTime > RefreshInterval) { _lastRefreshTime = EditorApplication.timeSinceStartup; Repaint(); } } private void OnGUI() { DrawToolbar(); var rooms = FindAllColyseusRooms(); if (rooms.Count == 0) { EditorGUILayout.HelpBox( "No active Colyseus rooms found.\n\n" + "Make sure:\n" + "• A scene is running (Play mode)\n" + "• You have a MonoBehaviour with a Room field\n" + "• The room is connected to the server", MessageType.Info ); return; } // Draw room tabs DrawRoomTabs(rooms); _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition); // Ensure selected index is valid if (_selectedRoomIndex >= rooms.Count) { _selectedRoomIndex = 0; } // Draw selected room content if (rooms.Count > 0 && _selectedRoomIndex >= 0 && _selectedRoomIndex < rooms.Count) { DrawRoomContent(rooms[_selectedRoomIndex]); } EditorGUILayout.EndScrollView(); } private void DrawRoomTabs(List rooms) { EditorGUILayout.BeginHorizontal(); for (int i = 0; i < rooms.Count; i++) { var room = rooms[i]; var tabLabel = !string.IsNullOrEmpty(room.Name) ? room.Name : $"Room {i + 1}"; // Add connection status indicator var statusIcon = room.IsConnected ? "●" : "○"; tabLabel = $"{statusIcon} {tabLabel}"; // Create tab style based on selection var tabStyle = new GUIStyle(EditorStyles.toolbarButton); if (i == _selectedRoomIndex) { tabStyle.fontStyle = FontStyle.Bold; tabStyle.normal.textColor = EditorGUIUtility.isProSkin ? new Color(0.4f, 0.8f, 1f) : new Color(0.1f, 0.4f, 0.8f); } if (GUILayout.Toggle(i == _selectedRoomIndex, tabLabel, tabStyle)) { _selectedRoomIndex = i; } } GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); // Draw separator line below tabs var rect = EditorGUILayout.GetControlRect(false, 2); EditorGUI.DrawRect(rect, new Color(0.3f, 0.3f, 0.3f, 0.5f)); EditorGUILayout.Space(5); } private void DrawToolbar() { EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); _autoRefresh = GUILayout.Toggle(_autoRefresh, "Auto Refresh", EditorStyles.toolbarButton, GUILayout.Width(100)); GUILayout.FlexibleSpace(); if (GUILayout.Button("Refresh Now", EditorStyles.toolbarButton, GUILayout.Width(100))) { Repaint(); } if (GUILayout.Button("Copy State JSON", EditorStyles.toolbarButton, GUILayout.Width(120))) { CopyStateToClipboard(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(5); } private void CopyStateToClipboard() { try { var rooms = FindAllColyseusRooms(); if (rooms.Count == 0) { EditorUtility.DisplayDialog("Copy State", "No active rooms found to copy.", "OK"); return; } // Ensure selected index is valid if (_selectedRoomIndex >= rooms.Count) { _selectedRoomIndex = 0; } var room = rooms[_selectedRoomIndex]; var stateData = new System.Text.StringBuilder(); stateData.AppendLine("=== Colyseus Room State ==="); stateData.AppendLine($"Captured at: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); stateData.AppendLine(); stateData.AppendLine($"Room: {room.Name} ({room.RoomId})"); stateData.AppendLine($"Session ID: {room.SessionId}"); stateData.AppendLine($"Connected: {room.IsConnected}"); stateData.AppendLine(); if (room.State != null) { stateData.AppendLine("State:"); SerializeStateToText(room.State, room.StateType, stateData, 1); } else { stateData.AppendLine("State: null"); } EditorGUIUtility.systemCopyBuffer = stateData.ToString(); Debug.Log("Room state copied to clipboard!"); EditorUtility.DisplayDialog("Copy State", "Room state has been copied to clipboard!", "OK"); } catch (Exception ex) { Debug.LogError($"Failed to copy state: {ex.Message}"); EditorUtility.DisplayDialog("Copy State", $"Failed to copy state:\n{ex.Message}", "OK"); } } private void SerializeStateToText(object obj, System.Type type, System.Text.StringBuilder sb, int indent) { if (obj == null || indent > 10) { return; } var indentStr = new string(' ', indent * 2); var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance) .Where(f => f.GetCustomAttribute() != null) .OrderBy(f => f.GetCustomAttribute().Index) .ToList(); foreach (var field in fields) { var fieldValue = field.GetValue(obj); var fieldType = field.FieldType; if (IsPrimitiveOrString(fieldType)) { sb.AppendLine($"{indentStr}{field.Name}: {fieldValue ?? "null"}"); } else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(MapSchema<>)) { var itemsProperty = fieldType.GetField("items"); var itemsValue = itemsProperty?.GetValue(fieldValue); var enumerable = itemsValue as IDictionary; var count = enumerable?.Count ?? 0; sb.AppendLine($"{indentStr}{field.Name} (MapSchema): {count} items"); if (enumerable != null && count > 0) { foreach (DictionaryEntry kvp in enumerable) { var key = kvp.Key?.ToString() ?? "null"; sb.AppendLine($"{indentStr} [{key}]:"); if (kvp.Value != null && typeof(Schema.Schema).IsAssignableFrom(kvp.Value.GetType())) { SerializeStateToText(kvp.Value, kvp.Value.GetType(), sb, indent + 2); } else { sb.AppendLine($"{indentStr} {kvp.Value}"); } } } } else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(ArraySchema<>)) { var enumerable = fieldValue as IEnumerable; var countProp = fieldType.GetProperty("Count"); var count = countProp?.GetValue(fieldValue) as int? ?? 0; sb.AppendLine($"{indentStr}{field.Name} (ArraySchema): {count} items"); if (enumerable != null && count > 0) { var index = 0; foreach (var item in enumerable) { sb.AppendLine($"{indentStr} [{index}]:"); if (item != null && typeof(Schema.Schema).IsAssignableFrom(item.GetType())) { SerializeStateToText(item, item.GetType(), sb, indent + 2); } else { sb.AppendLine($"{indentStr} {item}"); } index++; } } } else if (typeof(Schema.Schema).IsAssignableFrom(fieldType)) { sb.AppendLine($"{indentStr}{field.Name} ({fieldType.Name}):"); if (fieldValue != null) { SerializeStateToText(fieldValue, fieldType, sb, indent + 1); } else { sb.AppendLine($"{indentStr} null"); } } } } private void DrawRoomContent(RoomInfo roomInfo) { // Room Information Section DrawSection("Connection Info", () => { DrawReadOnlyField("Room ID", roomInfo.RoomId ?? "N/A"); DrawReadOnlyField("Session ID", roomInfo.SessionId ?? "N/A"); DrawReadOnlyField("Status", roomInfo.IsConnected ? "Connected" : "Disconnected"); DrawReadOnlyObjectField("Source Object", roomInfo.SourceObject, typeof(MonoBehaviour)); EditorGUILayout.Space(5); // Leave/Drop buttons EditorGUILayout.BeginHorizontal(); EditorGUI.BeginDisabledGroup(!roomInfo.IsConnected); if (GUILayout.Button("Leave", GUILayout.Height(24))) { LeaveRoom(roomInfo, consented: true); } if (GUILayout.Button("Drop", GUILayout.Height(24))) { DropRoom(roomInfo); } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); }); EditorGUILayout.Space(5); // Draw horizontal split panel for State and Messages DrawStateAndMessagesSplitPanel(roomInfo); } private void DrawSection(string title, Action content) { var foldoutKey = $"section_{title}_{EditorGUI.indentLevel}"; if (!_foldouts.ContainsKey(foldoutKey)) { _foldouts[foldoutKey] = true; } _foldouts[foldoutKey] = EditorGUILayout.Foldout(_foldouts[foldoutKey], title, true, EditorStyles.foldoutHeader); if (_foldouts[foldoutKey]) { EditorGUI.indentLevel++; content?.Invoke(); EditorGUI.indentLevel--; } } private void DrawSchemaObject(object obj, System.Type type, string path, int depth = 0) { if (obj == null || depth > 10) // Prevent infinite recursion { EditorGUILayout.LabelField("null", EditorStyles.miniLabel); return; } var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance) .Where(f => f.GetCustomAttribute() != null) .OrderBy(f => f.GetCustomAttribute().Index) .ToList(); foreach (var field in fields) { var fieldValue = field.GetValue(obj); var fieldType = field.FieldType; var fieldPath = $"{path}.{field.Name}"; // Handle different field types if (IsPrimitiveOrString(fieldType)) { DrawReadOnlyField(field.Name, fieldValue?.ToString() ?? "null"); } else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(MapSchema<>)) { DrawMapSchema(field.Name, fieldValue, fieldPath, depth); } else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(ArraySchema<>)) { DrawArraySchema(field.Name, fieldValue, fieldPath, depth); } else if (typeof(Schema.Schema).IsAssignableFrom(fieldType)) { DrawNestedSchema(field.Name, fieldValue, fieldType, fieldPath, depth); } else { DrawReadOnlyField(field.Name, $"<{fieldType.Name}>"); } } } private void DrawMapSchema(string fieldName, object mapObj, string path, int depth) { var foldoutKey = $"map_{path}"; if (!_foldouts.ContainsKey(foldoutKey)) { _foldouts[foldoutKey] = true; // Expand by default } if (mapObj == null) { DrawReadOnlyField(fieldName, "null (MapSchema)"); return; } var mapType = mapObj.GetType(); var countProp = mapType.GetProperty("Count"); var count = countProp?.GetValue(mapObj) as int? ?? 0; _foldouts[foldoutKey] = EditorGUILayout.Foldout( _foldouts[foldoutKey], $"{fieldName} (MapSchema) [{count} items]", true ); if (_foldouts[foldoutKey]) { EditorGUI.indentLevel++; if (count == 0) { EditorGUILayout.LabelField("(empty)", EditorStyles.miniLabel); } else { // Access the items property of MapSchema var itemsProperty = mapType.GetField("items"); var itemsValue = itemsProperty?.GetValue(mapObj); var enumerable = itemsValue as IDictionary; if (enumerable != null) { var index = 0; foreach (DictionaryEntry kvp in enumerable) { var key = kvp.Key?.ToString() ?? "null"; var value = kvp.Value; var itemPath = $"{path}[{key}]"; if (value != null && typeof(Schema.Schema).IsAssignableFrom(value.GetType())) { DrawNestedSchema($"[{key}]", value, value.GetType(), itemPath, depth + 1); } else { DrawReadOnlyField($"[{key}]", value?.ToString() ?? "null"); } index++; if (index > 100) // Limit display to prevent performance issues { EditorGUILayout.LabelField($"... and {count - 100} more items", EditorStyles.miniLabel); break; } } } else { EditorGUILayout.LabelField($"Error: Could not access MapSchema items", EditorStyles.miniLabel); } } EditorGUI.indentLevel--; } } private void DrawArraySchema(string fieldName, object arrayObj, string path, int depth) { var foldoutKey = $"array_{path}"; if (!_foldouts.ContainsKey(foldoutKey)) { _foldouts[foldoutKey] = true; // Expand by default } if (arrayObj == null) { DrawReadOnlyField(fieldName, "null (ArraySchema)"); return; } var arrayType = arrayObj.GetType(); var countProp = arrayType.GetProperty("Count"); var count = countProp?.GetValue(arrayObj) as int? ?? 0; _foldouts[foldoutKey] = EditorGUILayout.Foldout( _foldouts[foldoutKey], $"{fieldName} (ArraySchema) [{count} items]", true ); if (_foldouts[foldoutKey]) { EditorGUI.indentLevel++; if (count == 0) { EditorGUILayout.LabelField("(empty)", EditorStyles.miniLabel); } else { // Access the items field of ArraySchema (which is a List) var itemsField = arrayType.GetField("items"); var itemsValue = itemsField?.GetValue(arrayObj); var enumerable = itemsValue as IList; if (enumerable != null) { var index = 0; foreach (var item in enumerable) { var itemPath = $"{path}[{index}]"; if (item != null && typeof(Schema.Schema).IsAssignableFrom(item.GetType())) { DrawNestedSchema($"[{index}]", item, item.GetType(), itemPath, depth + 1); } else { DrawReadOnlyField($"[{index}]", item?.ToString() ?? "null"); } index++; if (index > 100) // Limit display to prevent performance issues { EditorGUILayout.LabelField($"... and {count - 100} more items", EditorStyles.miniLabel); break; } } } else { EditorGUILayout.LabelField($"Error: Could not access ArraySchema items", EditorStyles.miniLabel); } } EditorGUI.indentLevel--; } } private void DrawNestedSchema(string fieldName, object schemaObj, System.Type schemaType, string path, int depth) { var foldoutKey = $"schema_{path}"; if (!_foldouts.ContainsKey(foldoutKey)) { _foldouts[foldoutKey] = false; // Collapsed by default for nested schemas } if (schemaObj == null) { DrawReadOnlyField(fieldName, $"null ({schemaType.Name})"); return; } _foldouts[foldoutKey] = EditorGUILayout.Foldout( _foldouts[foldoutKey], $"{fieldName} ({schemaType.Name})", true ); if (_foldouts[foldoutKey]) { EditorGUI.indentLevel++; DrawSchemaObject(schemaObj, schemaType, path, depth + 1); EditorGUI.indentLevel--; } } private void DrawReadOnlyField(string label, string value) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(label, GUILayout.Width(EditorGUIUtility.labelWidth - 20)); EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); EditorGUILayout.EndHorizontal(); } private void DrawReadOnlyObjectField(string label, UnityEngine.Object obj, System.Type type) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(label, GUILayout.Width(EditorGUIUtility.labelWidth - 20)); EditorGUI.BeginDisabledGroup(true); EditorGUILayout.ObjectField(obj, type, true); EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); } private bool IsPrimitiveOrString(System.Type type) { return type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || type.IsEnum || type == typeof(DateTime); } private List FindAllColyseusRooms() { var roomInfos = new List(); var processedRooms = new HashSet(); // Avoid duplicates if (!Application.isPlaying) { return roomInfos; } // Find all MonoBehaviours in the scene var allMonoBehaviours = FindObjectsByType(FindObjectsSortMode.None); foreach (var behaviour in allMonoBehaviours) { if (behaviour == null) continue; var behaviourType = behaviour.GetType(); // Check instance fields var instanceFields = behaviourType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (var field in instanceFields) { if (IsColyseusRoomType(field.FieldType)) { var roomObj = field.GetValue(behaviour); if (roomObj != null && !processedRooms.Contains(roomObj)) { processedRooms.Add(roomObj); var roomInfo = ExtractRoomInfo(roomObj, field.FieldType, behaviour); if (roomInfo != null) { roomInfos.Add(roomInfo); } } } } // Check static fields var staticFields = behaviourType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); foreach (var field in staticFields) { if (IsColyseusRoomType(field.FieldType)) { var roomObj = field.GetValue(null); // null for static fields if (roomObj != null && !processedRooms.Contains(roomObj)) { processedRooms.Add(roomObj); var roomInfo = ExtractRoomInfo(roomObj, field.FieldType, behaviour); if (roomInfo != null) { roomInfos.Add(roomInfo); } } } } } return roomInfos; } private bool IsColyseusRoomType(System.Type type) { if (type == null) return false; // Check if it's a generic Room if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Room<>)) { return true; } // Check if it implements IRoom return typeof(IRoom).IsAssignableFrom(type); } private RoomInfo ExtractRoomInfo(object room, System.Type roomType, MonoBehaviour source) { try { var roomInfo = new RoomInfo { SourceObject = source, RoomType = roomType, RoomInstance = room }; // Get RoomId (field) var roomIdField = roomType.GetField("RoomId"); roomInfo.RoomId = roomIdField?.GetValue(room) as string; // Get SessionId (field) var sessionIdField = roomType.GetField("SessionId"); roomInfo.SessionId = sessionIdField?.GetValue(room) as string; // Get Name (field) var nameField = roomType.GetField("Name"); roomInfo.Name = nameField?.GetValue(room) as string; // Get Connection status (field) var connectionField = roomType.GetField("Connection"); var connection = connectionField?.GetValue(room); if (connection != null) { var isOpenProp = connection.GetType().GetField("IsOpen"); roomInfo.IsConnected = (isOpenProp?.GetValue(connection) as bool?) ?? false; } // Get State (property) var stateProp = roomType.GetProperty("State"); roomInfo.State = stateProp?.GetValue(room); if (roomInfo.State != null) { roomInfo.StateType = roomInfo.State.GetType(); } // Note: Room message type capture is now handled automatically // via compile-time checks in Room.OnMessage() return roomInfo; } catch (Exception ex) { Debug.LogWarning($"Failed to extract room info: {ex.Message}"); return null; } } private void DrawStateAndMessagesSplitPanel(RoomInfo roomInfo) { var roomId = roomInfo.RoomId; var splitterKey = $"room_splitter_{roomId}"; // Initialize splitter position for this room if (!_roomSplitterPosition.ContainsKey(splitterKey)) { _roomSplitterPosition[splitterKey] = 300f; // Default top panel height } if (!_isResizingRoomSplitter.ContainsKey(splitterKey)) { _isResizingRoomSplitter[splitterKey] = false; } // Calculate available height (estimate remaining scroll view height) var availableHeight = 600f; // Default available height var topPanelHeight = Mathf.Clamp(_roomSplitterPosition[splitterKey], MinPanelHeight, availableHeight - MinPanelHeight - SplitterHeight); var bottomPanelHeight = availableHeight - topPanelHeight - SplitterHeight; // Top Panel - Room State EditorGUILayout.BeginVertical(GUILayout.Height(topPanelHeight)); DrawStateSection(roomInfo); EditorGUILayout.EndVertical(); // Horizontal Splitter DrawHorizontalSplitter(splitterKey, availableHeight); // Bottom Panel - Messages EditorGUILayout.BeginVertical(GUILayout.Height(bottomPanelHeight)); DrawMessagesSection(roomInfo); EditorGUILayout.EndVertical(); } private void DrawHorizontalSplitter(string splitterKey, float availableHeight) { var splitterRect = EditorGUILayout.GetControlRect(GUILayout.Height(SplitterHeight), GUILayout.ExpandWidth(true)); EditorGUI.DrawRect(splitterRect, new Color(0.3f, 0.3f, 0.3f, 0.5f)); EditorGUIUtility.AddCursorRect(splitterRect, MouseCursor.ResizeVertical); var e = Event.current; if (e.type == EventType.MouseDown && splitterRect.Contains(e.mousePosition)) { _isResizingRoomSplitter[splitterKey] = true; e.Use(); } if (_isResizingRoomSplitter[splitterKey]) { if (e.type == EventType.MouseDrag) { _roomSplitterPosition[splitterKey] += e.delta.y; _roomSplitterPosition[splitterKey] = Mathf.Clamp( _roomSplitterPosition[splitterKey], MinPanelHeight, availableHeight - MinPanelHeight - SplitterHeight ); Repaint(); e.Use(); } if (e.type == EventType.MouseUp) { _isResizingRoomSplitter[splitterKey] = false; e.Use(); } } } private void DrawStateSection(RoomInfo roomInfo) { var stateScrollKey = $"state_scroll_{roomInfo.RoomId}"; if (!_foldouts.ContainsKey(stateScrollKey)) { _foldouts[stateScrollKey] = true; } DrawSection("Room State", () => { if (roomInfo.State != null) { DrawReadOnlyField("State Type", roomInfo.StateType?.Name ?? "Unknown"); EditorGUILayout.Space(3); DrawSchemaObject(roomInfo.State, roomInfo.StateType, "state"); } else { EditorGUILayout.HelpBox("State is null or not yet initialized", MessageType.Warning); } }); } private void DrawMessagesSection(RoomInfo roomInfo) { var roomId = roomInfo.RoomId; if (string.IsNullOrEmpty(roomId)) { return; } DrawSection("Messages", () => { // Access message types from static capture if (!RoomMessageType.CapturedMessageTypes.ContainsKey(roomId) || RoomMessageType.CapturedMessageTypes[roomId] == null || RoomMessageType.CapturedMessageTypes[roomId].Count == 0) { EditorGUILayout.HelpBox( "No message types available.\n\n" + "Waiting for '__playground_message_types' message from server.", MessageType.Info ); return; } if (!_messageInputs.ContainsKey(roomId)) { _messageInputs[roomId] = new Dictionary(); } if (!_selectedMessageTypeIndex.ContainsKey(roomId)) { _selectedMessageTypeIndex[roomId] = 0; } var messageTypes = RoomMessageType.CapturedMessageTypes[roomId]; var messageInputs = _messageInputs[roomId]; // Create array of message type names for popup var messageTypeList = messageTypes.Keys.ToList(); messageTypeList.Add("* (Custom)"); var messageTypeNames = messageTypeList.ToArray(); if (messageTypeNames.Length == 0) { EditorGUILayout.HelpBox("No message types available. Enable playground in the server to see message types here.", MessageType.Info); return; } // Ensure index is valid if (_selectedMessageTypeIndex[roomId] >= messageTypeNames.Length) { _selectedMessageTypeIndex[roomId] = 0; } // Draw message interface DrawMessageInterface(roomInfo, messageTypeNames, messageTypes, messageInputs); }); } private void DrawMessageInterface(RoomInfo roomInfo, string[] messageTypeNames, Dictionary messageTypes, Dictionary messageInputs) { var roomId = roomInfo.RoomId; // Message type selector EditorGUILayout.LabelField("Message Type:", EditorStyles.boldLabel); var previousIndex = _selectedMessageTypeIndex[roomId]; _selectedMessageTypeIndex[roomId] = EditorGUILayout.Popup( _selectedMessageTypeIndex[roomId], messageTypeNames, GUILayout.Height(20) ); var selectedMessageName = messageTypeNames[_selectedMessageTypeIndex[roomId]]; var isCustomMessage = selectedMessageName == "* (Custom)"; // Handle custom message type if (isCustomMessage) { var customKey = $"{roomId}_custom"; if (!_customMessageTypes.ContainsKey(customKey)) { _customMessageTypes[customKey] = ""; } EditorGUILayout.Space(4); EditorGUILayout.LabelField("Custom Message Type:", EditorStyles.boldLabel); _customMessageTypes[customKey] = EditorGUILayout.TextField(_customMessageTypes[customKey], GUILayout.Height(20)); selectedMessageName = _customMessageTypes[customKey]; } IDictionary messageSchema = !isCustomMessage && messageTypes.ContainsKey(selectedMessageName) ? messageTypes[selectedMessageName] as IDictionary : null; EditorGUILayout.Space(4); // Initialize raw JSON toggle state var rawJsonKey = $"{roomId}_{selectedMessageName}"; if (!_useRawJson.ContainsKey(rawJsonKey)) { _useRawJson[rawJsonKey] = false; } // Initialize raw JSON input if (!_rawJsonInputs.ContainsKey(rawJsonKey)) { _rawJsonInputs[rawJsonKey] = messageSchema != null ? GenerateDefaultJSON(messageSchema) : "{}"; } // Check if message type changed - update raw JSON when switching message types var currentMessageKey = isCustomMessage ? "* (Custom)" : selectedMessageName; if (!_lastSelectedMessageType.ContainsKey(roomId) || _lastSelectedMessageType[roomId] != currentMessageKey) { _lastSelectedMessageType[roomId] = currentMessageKey; _rawJsonInputs[rawJsonKey] = messageSchema != null ? GenerateDefaultJSON(messageSchema) : "{}"; // Clear all field inputs for this message type if (!isCustomMessage) { var keysToRemove = messageInputs.Keys.Where(k => k.StartsWith($"{roomId}_{selectedMessageName}_field_")).ToList(); foreach (var key in keysToRemove) { messageInputs.Remove(key); } } } // Toggle between form fields and raw JSON var previousUseRawJson = _useRawJson[rawJsonKey]; // Only show toggle if schema is available if (messageSchema != null) { _useRawJson[rawJsonKey] = EditorGUILayout.Toggle("Use Raw JSON", _useRawJson[rawJsonKey]); EditorGUILayout.Space(4); } // When switching to raw JSON mode, populate from field values if (_useRawJson[rawJsonKey] && !previousUseRawJson && messageSchema != null) { _rawJsonInputs[rawJsonKey] = GenerateJSONFromFields(roomId, selectedMessageName, messageSchema, messageInputs); } // If raw JSON mode is enabled or schema is invalid, show JSON text area if (_useRawJson[rawJsonKey] || messageSchema == null) { EditorGUILayout.Space(4); EditorGUILayout.LabelField("JSON Payload:", EditorStyles.boldLabel); // Multi-line text area for JSON input EditorGUILayout.BeginVertical(EditorStyles.helpBox); var textAreaStyle = new GUIStyle(EditorStyles.textArea); textAreaStyle.wordWrap = true; textAreaStyle.stretchHeight = true; _rawJsonInputs[rawJsonKey] = EditorGUILayout.TextArea( _rawJsonInputs[rawJsonKey], textAreaStyle, GUILayout.MinHeight(40), GUILayout.MaxHeight(80) ); EditorGUILayout.EndVertical(); } else { // Get required fields List requiredFields = null; if (messageSchema.Contains("required")) { requiredFields = messageSchema["required"] as List; } // Draw individual field inputs if (messageSchema.Contains("properties")) { var properties = messageSchema["properties"] as IDictionary; if (properties != null && properties.Count > 0) { EditorGUILayout.LabelField("Payload:", EditorStyles.boldLabel); EditorGUI.indentLevel++; foreach (DictionaryEntry prop in properties) { var propName = prop.Key as string; var propSchema = prop.Value as IDictionary; if (propSchema != null && propSchema.Contains("type")) { var isRequired = requiredFields != null && requiredFields.Any(r => r.ToString() == propName); DrawMessageField(roomId, selectedMessageName, propName, propSchema, isRequired, messageInputs); } } EditorGUI.indentLevel--; } } else { EditorGUILayout.HelpBox("No fields defined for this message type.", MessageType.Info); } } EditorGUILayout.Space(8); // Send button var canSend = !isCustomMessage || !string.IsNullOrWhiteSpace(selectedMessageName); var buttonLabel = isCustomMessage && string.IsNullOrWhiteSpace(selectedMessageName) ? "Send (enter message type above)" : $"Send '{selectedMessageName}'"; EditorGUI.BeginDisabledGroup(!canSend); if (GUILayout.Button(buttonLabel, GUILayout.Height(30))) { // Use raw JSON when explicitly enabled OR when messageSchema is null if (_useRawJson[rawJsonKey] || messageSchema == null) { SendMessageFromRawJson(roomInfo, selectedMessageName, _rawJsonInputs[rawJsonKey]); } else { SendMessageFromFields(roomInfo, selectedMessageName, messageSchema, messageInputs); } } EditorGUI.EndDisabledGroup(); } private void DrawMessageField(string roomId, string messageName, string fieldName, IDictionary fieldSchema, bool isRequired, Dictionary messageInputs) { var fieldKey = $"{roomId}_{messageName}_field_{fieldName}"; var fieldType = fieldSchema["type"] as string; // Initialize with default value if not exists if (!messageInputs.ContainsKey(fieldKey)) { messageInputs[fieldKey] = GetDefaultValueForType(fieldSchema); } // Create label with asterisk for required fields var label = isRequired ? $"{fieldName} *" : fieldName; // Add description as tooltip if available GUIContent labelContent; if (fieldSchema.Contains("description")) { var description = fieldSchema["description"].ToString(); labelContent = new GUIContent(label, description); } else { labelContent = new GUIContent(label); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(labelContent, GUILayout.Width(EditorGUIUtility.labelWidth - 20)); // Draw appropriate input control based on field type switch (fieldType) { case "boolean": var boolValue = messageInputs[fieldKey] == "true"; boolValue = EditorGUILayout.Toggle(boolValue); messageInputs[fieldKey] = boolValue.ToString().ToLower(); break; case "integer": if (int.TryParse(messageInputs[fieldKey], out int intValue)) { intValue = EditorGUILayout.IntField(intValue); messageInputs[fieldKey] = intValue.ToString(); } else { messageInputs[fieldKey] = EditorGUILayout.TextField(messageInputs[fieldKey]); } break; case "number": if (float.TryParse(messageInputs[fieldKey], out float floatValue)) { floatValue = EditorGUILayout.FloatField(floatValue); messageInputs[fieldKey] = floatValue.ToString(); } else { messageInputs[fieldKey] = EditorGUILayout.TextField(messageInputs[fieldKey]); } break; case "string": // Remove quotes if present for display var stringValue = messageInputs[fieldKey]; if (stringValue.StartsWith("\"") && stringValue.EndsWith("\"")) { stringValue = stringValue.Substring(1, stringValue.Length - 2); } stringValue = EditorGUILayout.TextField(stringValue); messageInputs[fieldKey] = stringValue; break; case "array": case "object": // For complex types, use a text field with JSON EditorGUILayout.LabelField($"({fieldType})", GUILayout.Width(60)); messageInputs[fieldKey] = EditorGUILayout.TextField(messageInputs[fieldKey]); break; default: messageInputs[fieldKey] = EditorGUILayout.TextField(messageInputs[fieldKey]); break; } EditorGUILayout.EndHorizontal(); } private void SendMessageFromFields(RoomInfo roomInfo, string messageName, IDictionary messageSchema, Dictionary messageInputs) { try { var roomId = roomInfo.RoomId; var messageData = new Dictionary(); // Build message object from field values if (messageSchema.Contains("properties")) { var properties = messageSchema["properties"] as IDictionary; if (properties != null) { foreach (DictionaryEntry prop in properties) { var propName = prop.Key as string; var propSchema = prop.Value as IDictionary; if (propSchema == null || !propSchema.Contains("type")) continue; var fieldKey = $"{roomId}_{messageName}_field_{propName}"; if (!messageInputs.ContainsKey(fieldKey)) continue; var fieldValue = messageInputs[fieldKey]; var fieldType = propSchema["type"] as string; // Convert field value to appropriate type object typedValue = ConvertFieldValue(fieldValue, fieldType); messageData[propName] = typedValue; } } } // Get the Send method from the room var sendMethod = roomInfo.RoomType.GetMethod("Send", new[] { typeof(string), typeof(object) }); if (sendMethod == null) { EditorUtility.DisplayDialog("Error", "Failed to find Send method on room.", "OK"); return; } // Invoke Send method var task = sendMethod.Invoke(roomInfo.RoomInstance, new object[] { messageName, messageData }) as System.Threading.Tasks.Task; if (task != null) { // Note: We can't await in a non-async void method, but the task will execute Debug.Log($"Message '{messageName}' sent"); } } catch (Exception ex) { Debug.LogError($"Failed to send message '{messageName}': {ex.Message}\n{ex.StackTrace}"); EditorUtility.DisplayDialog("Error", $"Failed to send message:\n{ex.Message}", "OK"); } } private void LeaveRoom(RoomInfo roomInfo, bool consented) { try { // Get the Leave method from the room (with bool parameter) var leaveMethod = roomInfo.RoomType.GetMethod("Leave", new[] { typeof(bool) }); if (leaveMethod == null) { EditorUtility.DisplayDialog("Error", "Failed to find Leave method on room.", "OK"); return; } // Invoke Leave method var task = leaveMethod.Invoke(roomInfo.RoomInstance, new object[] { consented }) as System.Threading.Tasks.Task; if (task != null) { Debug.Log($"Room '{roomInfo.Name}' - Leave requested"); } } catch (Exception ex) { Debug.LogError($"Failed to leave room: {ex.Message}\n{ex.StackTrace}"); EditorUtility.DisplayDialog("Error", $"Failed to leave room:\n{ex.Message}", "OK"); } } private void DropRoom(RoomInfo roomInfo) { try { // Get the Connection field from the room var connectionField = roomInfo.RoomType.GetField("Connection"); if (connectionField == null) { EditorUtility.DisplayDialog("Error", "Failed to find Connection field on room.", "OK"); return; } var connection = connectionField.GetValue(roomInfo.RoomInstance); if (connection == null) { EditorUtility.DisplayDialog("Error", "Connection is null.", "OK"); return; } // Get the Drop method from the connection var dropMethod = connection.GetType().GetMethod("Drop"); if (dropMethod == null) { EditorUtility.DisplayDialog("Error", "Failed to find Drop method on connection.", "OK"); return; } // Invoke Drop method dropMethod.Invoke(connection, null); Debug.Log($"Room '{roomInfo.Name}' - Connection dropped"); } catch (Exception ex) { Debug.LogError($"Failed to drop room connection: {ex.Message}\n{ex.StackTrace}"); EditorUtility.DisplayDialog("Error", $"Failed to drop connection:\n{ex.Message}", "OK"); } } private void SendMessageFromRawJson(RoomInfo roomInfo, string messageName, string jsonData) { try { // Parse JSON to object object messageData; try { messageData = Json.Deserialize(typeof(Dictionary), jsonData); } catch (Exception jsonEx) { EditorUtility.DisplayDialog("JSON Parse Error", $"Failed to parse JSON:\n{jsonEx.Message}", "OK"); Debug.LogError($"JSON parse error: {jsonEx.Message}\n{jsonData}"); return; } // Get the Send method from the room var sendMethod = roomInfo.RoomType.GetMethod("Send", new[] { typeof(string), typeof(object) }); if (sendMethod == null) { EditorUtility.DisplayDialog("Error", "Failed to find Send method on room.", "OK"); return; } // Invoke Send method var task = sendMethod.Invoke(roomInfo.RoomInstance, new object[] { messageName, messageData }) as System.Threading.Tasks.Task; if (task != null) { Debug.Log($"Message '{messageName}' sent with raw JSON"); } } catch (Exception ex) { Debug.LogError($"Failed to send message '{messageName}': {ex.Message}\n{ex.StackTrace}"); EditorUtility.DisplayDialog("Error", $"Failed to send message:\n{ex.Message}", "OK"); } } private object ConvertFieldValue(string fieldValue, string fieldType) { switch (fieldType) { case "boolean": return fieldValue == "true"; case "integer": if (int.TryParse(fieldValue, out int intValue)) return intValue; return 0; case "number": if (double.TryParse(fieldValue, out double doubleValue)) return doubleValue; return 0.0; case "string": return fieldValue; case "array": try { return Json.Deserialize(typeof(List), fieldValue); } catch { return new List(); } case "object": try { return Json.Deserialize(typeof(Dictionary), fieldValue); } catch { return new Dictionary(); } default: return fieldValue; } } private string GenerateDefaultJSON(IDictionary schema) { try { if (!schema.Contains("properties")) { return "{}"; } var properties = schema["properties"] as IDictionary; if (properties == null || properties.Count == 0) { return "{}"; } var sb = new StringBuilder(); sb.AppendLine("{"); var propCount = 0; foreach (DictionaryEntry prop in properties) { var propName = prop.Key as string; var propSchema = prop.Value as IDictionary; if (propSchema == null) continue; sb.Append($" \"{propName}\": "); var defaultValue = GetDefaultValueForType(propSchema); sb.Append(defaultValue); propCount++; if (propCount < properties.Count) { sb.AppendLine(","); } else { sb.AppendLine(); } } sb.Append("}"); return sb.ToString(); } catch (Exception ex) { Debug.LogWarning($"Failed to generate default JSON: {ex.Message}"); return "{}"; } } private string GetDefaultValueForType(IDictionary propSchema) { if (!propSchema.Contains("type")) { return "null"; } var type = propSchema["type"] as string; switch (type) { case "string": return "\"\""; case "number": case "integer": return "0"; case "boolean": return "false"; case "array": return "[]"; case "object": return "{}"; default: return "null"; } } private string GenerateJSONFromFields(string roomId, string messageName, IDictionary messageSchema, Dictionary messageInputs) { try { if (!messageSchema.Contains("properties")) { return "{}"; } var properties = messageSchema["properties"] as IDictionary; if (properties == null || properties.Count == 0) { return "{}"; } var sb = new StringBuilder(); sb.AppendLine("{"); var propCount = 0; var totalProps = properties.Count; foreach (DictionaryEntry prop in properties) { var propName = prop.Key as string; var propSchema = prop.Value as IDictionary; if (propSchema == null || !propSchema.Contains("type")) continue; var fieldKey = $"{roomId}_{messageName}_field_{propName}"; var fieldType = propSchema["type"] as string; sb.Append($" \"{propName}\": "); // Get value from field input or use default string jsonValue; if (messageInputs.ContainsKey(fieldKey)) { var fieldValue = messageInputs[fieldKey]; jsonValue = ConvertFieldValueToJSON(fieldValue, fieldType); } else { jsonValue = GetDefaultValueForType(propSchema); } sb.Append(jsonValue); propCount++; if (propCount < totalProps) { sb.AppendLine(","); } else { sb.AppendLine(); } } sb.Append("}"); return sb.ToString(); } catch (Exception ex) { Debug.LogWarning($"Failed to generate JSON from fields: {ex.Message}"); return GenerateDefaultJSON(messageSchema); } } private string ConvertFieldValueToJSON(string fieldValue, string fieldType) { switch (fieldType) { case "boolean": return fieldValue == "true" ? "true" : "false"; case "integer": if (int.TryParse(fieldValue, out int intValue)) return intValue.ToString(); return "0"; case "number": if (double.TryParse(fieldValue, out double doubleValue)) return doubleValue.ToString(); return "0"; case "string": // Escape quotes and wrap in quotes var escaped = fieldValue.Replace("\\", "\\\\").Replace("\"", "\\\""); return $"\"{ escaped}\""; case "array": case "object": // Try to parse as JSON, otherwise treat as string try { var parsed = Json.Deserialize(typeof(object), fieldValue); return fieldValue; // If it parses, use as-is } catch { return fieldValue.StartsWith("[") || fieldValue.StartsWith("{") ? fieldValue : "{}"; } default: return $"\"{fieldValue}\""; } } private async void SendMessage(RoomInfo roomInfo, string messageName, string jsonData) { try { // Parse JSON to object var messageData = ParseJSON(jsonData); // Get the Send method from the room var sendMethod = roomInfo.RoomType.GetMethod("Send", new[] { typeof(string), typeof(object) }); if (sendMethod == null) { EditorUtility.DisplayDialog("Error", "Failed to find Send method on room.", "OK"); return; } // Invoke Send method var task = sendMethod.Invoke(roomInfo.RoomInstance, new object[] { messageName, messageData }) as System.Threading.Tasks.Task; if (task != null) { await task; Debug.Log($"Message '{messageName}' sent successfully!"); } } catch (Exception ex) { Debug.LogError($"Failed to send message '{messageName}': {ex.Message}\n{ex.StackTrace}"); EditorUtility.DisplayDialog("Error", $"Failed to send message:\n{ex.Message}", "OK"); } } private object ParseJSON(string json) { // Parse JSON using GameDevWare.Serialization.Json try { return Json.Deserialize(typeof(Dictionary), json); } catch (Exception ex) { Debug.LogError($"Failed to parse JSON: {ex.Message}"); throw; } } private void DrawSchemaInfo(IDictionary schema) { if (schema == null) { EditorGUILayout.LabelField("No schema information available", EditorStyles.miniLabel); return; } // Display schema type if (schema.Contains("type")) { DrawReadOnlyField("Type", schema["type"].ToString()); } // Display properties if (schema.Contains("properties")) { var properties = schema["properties"] as IDictionary; if (properties != null && properties.Count > 0) { EditorGUILayout.LabelField("Properties:", EditorStyles.boldLabel); EditorGUI.indentLevel++; foreach (DictionaryEntry prop in properties) { var propName = prop.Key as string; var propSchema = prop.Value as IDictionary; if (propSchema != null && propSchema.Contains("type")) { var propType = propSchema["type"].ToString(); var propInfo = propType; // Add description if available if (propSchema.Contains("description")) { propInfo += $" - {propSchema["description"]}"; } DrawReadOnlyField(propName, propInfo); } } EditorGUI.indentLevel--; } } // Display required fields if (schema.Contains("required")) { var required = schema["required"] as List; if (required != null && required.Count > 0) { var requiredStr = string.Join(", ", required.Select(r => r.ToString())); DrawReadOnlyField("Required", requiredStr); } } } private class RoomInfo { public string RoomId; public string SessionId; public string Name; public bool IsConnected; public object State; public System.Type StateType; public System.Type RoomType; public MonoBehaviour SourceObject; public object RoomInstance; } } } ================================================ FILE: Assets/Colyseus/Editor/RoomInspector.cs.meta ================================================ fileFormatVersion: 2 guid: 7306c18ccdb7b40c78a54abc8f917b50 ================================================ FILE: Assets/Colyseus/Editor.meta ================================================ fileFormatVersion: 2 guid: 12db36416327a4e74873a275325fd6f5 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/LICENSE ================================================ Copyright (c) 2021 Lucid Sight Copyright (c) 2015-2021 Endel Dreyer MIT License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Assets/Colyseus/LICENSE.meta ================================================ fileFormatVersion: 2 guid: 28fed75626a4d4144b26bef3670248f7 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Auth.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using GameDevWare.Serialization; namespace Colyseus { public interface IAuthData { string Token { get; } IndexedDictionary RawUser { get; set; } Type UserType { get; } } [Serializable] public class AuthData : IAuthData { public string token; public T user; private IndexedDictionary rawUser; public AuthData() { } public AuthData(string _token, IndexedDictionary userData) { token = _token; rawUser = userData; if (typeof(T) == typeof(IndexedDictionary)) { user = (T)(object)rawUser; } else if (userData != null) { user = ConvertType(userData); } } public string Token { get => token; } public IndexedDictionary RawUser { get { // TODO: refactor here... if (rawUser == null && typeof(T) == typeof(IndexedDictionary)) { rawUser = (IndexedDictionary)(object)user; } return rawUser; } set => rawUser = value; } public Type UserType { get => typeof(T); } public static T ConvertType(IndexedDictionary rawUser) { Type targetType = typeof(T); T instance = (T)Activator.CreateInstance(targetType); for (var i = 0; i < rawUser.Keys.Count; i++) { var field = targetType.GetField(rawUser.Keys[i]); if (field != null) { try { field.SetValue(instance, Convert.ChangeType(rawUser.Values[i], field.FieldType)); } catch (Exception e) { ColyseusContext.Logger.LogWarning("Colyseus.Auth: cannot convert " + targetType.ToString() + " property '" + field.Name + "' from " + rawUser.Values[i].GetType() + " to " + field.FieldType + " (" + e.Message + ")"); } } } return instance; } } public interface IAuthChangeHandler { Type Type { get; } Type UserType { get; set; } void Invoke(object authData); } public class AuthChangeHandler : IAuthChangeHandler { private Type userType; public Action Action; public void Invoke(object authData) { Action.Invoke((T)authData); } public Type Type { get => typeof(T); } public Type UserType { get => userType; set => userType = value; } } /// /// Colyseus.Auth /// /// /// Colyseus Authentication Module Tools. /// See https://docs.colyseus.io/authentication/module/ /// public class Auth { public static string PATH = "auth"; public static string TOKEN_CACHE_KEY = "AuthToken"; private Client _client; private List OnChangeHandlers = new List(); private bool initialized = false; public Auth(Client client) { _client = client; Token = ColyseusContext.TokenStorage.GetToken(TOKEN_CACHE_KEY); } public string Token { get => _client.Http.AuthToken; set => _client.Http.AuthToken = value; } public async Task OnChange(Action> callback) { var handler = new AuthChangeHandler> { Action = callback, UserType = typeof(T) }; OnChangeHandlers.Add(handler); if (!initialized) { initialized = true; try { emitChange(new AuthData { token = Token, user = await GetUserData() }); } catch (Exception e) { ColyseusContext.Logger.LogWarning(e.ToString()); emitChange(new AuthData { user = null, token = null }); } } return () => OnChangeHandlers.Remove(handler); } public async Task GetUserData() { if (string.IsNullOrEmpty(Token)) { throw new Exception("missing Auth.Token"); } else { return getAuthData(await _client.Http.Request>>("GET", $"{PATH}/userdata")).user; } } public async Task> RegisterWithEmailAndPassword(string email, string password, Dictionary options = null) { var response = getAuthData(await _client.Http.Request>>("POST", $"{PATH}/register", new Dictionary { { "email", email }, { "password", password }, { "options", options }, })); emitChange(response); return response; } public async Task RegisterWithEmailAndPassword(string email, string password, Dictionary options = null) { return await RegisterWithEmailAndPassword>(email, password, options); } public async Task> SignInWithEmailAndPassword(string email, string password) { var response = getAuthData(await _client.Http.Request>>("POST", $"{PATH}/login", new Dictionary { { "email", email }, { "password", password }, })); emitChange(response); return response; } public async Task SignInWithEmailAndPassword(string email, string password) { return await SignInWithEmailAndPassword>(email, password); } public async Task> SignInAnonymously(Dictionary options = null) { var response = getAuthData(await _client.Http.Request>>("POST", $"{PATH}/anonymous", options)); emitChange(response); return response; } public async Task SignInAnonymously(Dictionary options = null) { return await SignInAnonymously>(options); } public async Task> SignInWithProvider(string providerName, Dictionary settings = null) { await Task.Run(() => {/* Satisfy the compiler async/await. This method is not implemented yet. */}); // // Implementation reference: https://github.com/colyseus/colyseus.js/blob/1f2208d4ff49e858a737e4e7d1581148de196cce/src/Auth.ts#L112C26-L161 // throw new Exception("Not implemented. See implementation reference on JavaScript SDK"); } public async Task SignInWithProvider(string providerName, Dictionary settings = null) { return await SignInWithProvider>(providerName, settings); } public async Task SendResetPasswordEmail(string email, string password) { return await _client.Http.Request("POST", $"{PATH}/login", new Dictionary { { "email", email }, { "password", password }, }); } public void SignOut() { emitChange(new AuthData { token = null, user = null}); } private void emitChange(IAuthData authData) { Token = authData.Token; if (!string.IsNullOrEmpty(Token)) { ColyseusContext.TokenStorage.SetToken(TOKEN_CACHE_KEY, authData.Token); } else { ColyseusContext.TokenStorage.DeleteToken(TOKEN_CACHE_KEY); } OnChangeHandlers.ForEach((handler) => { if (authData.GetType() == handler.Type) { handler.Invoke(authData); } else if (authData.UserType == typeof(IndexedDictionary)) { // convert AuthData object instance = Activator.CreateInstance(handler.Type, authData.Token, authData.RawUser); handler.Invoke(instance); } else if (authData.RawUser == null) { object instance = Activator.CreateInstance(handler.Type, authData.Token, null); handler.Invoke(instance); } else { ColyseusContext.Logger.Log("Not triggering..."); } }); } private AuthData getAuthData(AuthData> authData) { if (typeof(T) == typeof(IndexedDictionary)) { return (AuthData)(object)authData; } else { return new AuthData(authData.token, authData.RawUser); } } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Auth.cs.meta ================================================ fileFormatVersion: 2 guid: c500d710fb1854e15ac69bebeab976b6 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Client.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; // ReSharper disable InconsistentNaming namespace Colyseus { /// /// Options for latency measurement. /// public class LatencyOptions { /// /// "ws" for WebSocket, "h3" for WebTransport (default: "ws") /// public string Protocol { get; set; } = "ws"; /// /// Number of pings to send (default: 1). Returns the average latency when > 1. /// public int PingCount { get; set; } = 1; } /// /// Colyseus.Client /// /// /// Provides integration between Colyseus Game Server through WebSocket protocol ( /// RFC 6455). /// public class Client { /// /// Authentication tools, see: https://docs.colyseus.io/auth/ /// public Auth Auth; /// /// Reference to the client's /// private UriBuilder Endpoint; /// /// Object to perform s to the server. /// public HTTP Http; /// /// Initializes a new instance of the class with /// the specified Colyseus Game Server endpoint. /// /// /// A that represents the WebSocket URL to connect. /// public Client(string endpoint) { Endpoint = new UriBuilder(endpoint); // Create Settings object to pass to the ColyseusRequest object Settings settings = Settings.Create(); settings.colyseusServerAddress = $"{Endpoint.Host}{Endpoint.Path}"; settings.colyseusServerPort = Endpoint.Port.ToString(); settings.useSecureProtocol = string.Equals(Endpoint.Scheme, "wss") || string.Equals(Endpoint.Scheme, "https"); Settings = settings; Auth = new Auth(this); } /// /// Initializes a new instance of the class with /// the specified Colyseus Settings object. /// /// The settings you wish to use /// Determines whether the connection endpoint should use either web socket or http protocols. public Client(Settings settings) { Settings = settings; Auth = new Auth(this); } /// /// The getter for the currently assigned to this client object /// private Settings _colyseusSettings; public Settings Settings { get { return _colyseusSettings; } set { _colyseusSettings = value; Endpoint = new UriBuilder(_colyseusSettings.WebSocketEndpoint); // Instantiate our ColyseusRequest object with the settings object Http = new HTTP(_colyseusSettings); } } /// /// Join or Create a /// /// Room identifier /// Dictionary of options to pass to the room upon creation/joining /// Dictionary of headers to pass to the server when we create/join the room /// Type of we want to join or create /// via async task public async Task> JoinOrCreate(string roomName, Dictionary options = null, Dictionary headers = null) where T : Schema.Schema { return await CreateMatchMakeRequest("joinOrCreate", roomName, options, headers); } /// /// Create a /// /// Room identifier /// Dictionary of options to pass to the room upon creation /// Dictionary of headers to pass to the server when we create the room /// Type of we want to create /// via async task public async Task> Create(string roomName, Dictionary options = null, Dictionary headers = null) where T : Schema.Schema { return await CreateMatchMakeRequest("create", roomName, options, headers); } /// /// Join a /// /// Room identifier /// Dictionary of options to pass to the room upon joining /// Dictionary of headers to pass to the server when we join the room /// Type of we want to join /// via async task public async Task> Join(string roomName, Dictionary options = null, Dictionary headers = null) where T : Schema.Schema { return await CreateMatchMakeRequest("join", roomName, options, headers); } /// /// Join a by ID /// /// ID of the room /// Dictionary of options to pass to the room upon joining /// Dictionary of headers to pass to the server when we join the room /// Type of we want to join /// via async task public async Task> JoinById(string roomId, Dictionary options = null, Dictionary headers = null) where T : Schema.Schema { return await CreateMatchMakeRequest("joinById", roomId, options, headers); } /// /// Reconnect to a /// /// Previously connected ReconnectionToken /// Dictionary of headers to pass to the server when we reconnect to the room /// Type of we want to reconnect with /// via async task public async Task> Reconnect(ReconnectionToken reconnectionToken, Dictionary headers = null) where T : Schema.Schema { Dictionary options = new Dictionary(); options.Add("reconnectionToken", reconnectionToken.Token); return await CreateMatchMakeRequest("reconnect", reconnectionToken.RoomId, options, headers); } // // FossilDelta/None serializer versions for joining the state // /// /// Join or Create a /// /// Room identifier /// Dictionary of options to pass to the room upon creation/joining /// Dictionary of headers to pass to the server when we create/join the room /// via async task public async Task> JoinOrCreate(string roomName, Dictionary options = null, Dictionary headers = null) { return await CreateMatchMakeRequest("joinOrCreate", roomName, options, headers); } /// /// Create a /// /// Room identifier /// Dictionary of options to pass to the room upon creation /// Dictionary of headers to pass to the server when we create the room /// via async task public async Task> Create(string roomName, Dictionary options = null, Dictionary headers = null) { return await CreateMatchMakeRequest("create", roomName, options, headers); } /// /// Join a /// /// Room identifier /// Dictionary of options to pass to the room upon joining /// Dictionary of headers to pass to the server when we join the room /// via async task public async Task> Join(string roomName, Dictionary options = null, Dictionary headers = null) { return await CreateMatchMakeRequest("join", roomName, options, headers); } /// /// Join a by ID /// /// ID of the room /// Dictionary of options to pass to the room upon joining /// Dictionary of headers to pass to the server when we join the room /// via async task public async Task> JoinById(string roomId, Dictionary options = null, Dictionary headers = null) { return await CreateMatchMakeRequest("joinById", roomId, options, headers); } /// /// Reconnect to a /// /// ID of the room /// Previously connected sessionId /// Dictionary of headers to pass to the server when we reconnect to the room /// via async task public async Task> Reconnect(string roomId, string sessionId, Dictionary headers = null) { Dictionary options = new Dictionary(); options.Add("sessionId", sessionId); return await CreateMatchMakeRequest("joinById", roomId, options, headers); } /// /// Consume the seat reservation /// /// The response from the matchmaking attempt /// Dictionary of headers to pass to the server /// Previous Room{T} instance to re-establish the server connection: Please do not use this devMode param for general purposes /// Type of we're consuming the seat from /// in which we now have a seat via async task public async Task> ConsumeSeatReservation(SeatReservation response, Dictionary headers = null) where T : Schema.Schema { Room room = new Room(response.name) { RoomId = response.roomId, SessionId = response.sessionId }; Dictionary queryString = new Dictionary { { "sessionId", room.SessionId } }; // forward reconnection token if (response.reconnectionToken != null) { queryString.Add("reconnectionToken", response.reconnectionToken); } room.SetConnection(CreateConnection(response, queryString, headers)); TaskCompletionSource> tcs = new TaskCompletionSource>(); void OnError(int code, string message) { room.OnError -= OnError; tcs.SetException(new MatchMakeException(code, message)); } void OnJoin() { room.OnError -= OnError; tcs.TrySetResult(room); } room.OnError += OnError; room.OnJoin += OnJoin; _ = room.Connect(); return await tcs.Task; } /// /// Create a match making request /// /// The type of request we're making (join, create, etc) /// Room identifierroom we're trying to match /// Dictionary of options to use in the match making process /// Dictionary of headers to pass to the server /// Type of we want to match with /// we have matched with via async task /// Thrown if there is a network related error /// Thrown if there is an error in the match making process on the server side protected async Task> CreateMatchMakeRequest(string method, string roomName, Dictionary options, Dictionary headers) where T : Schema.Schema { if (options == null) { options = new Dictionary(); } if (headers == null) { headers = new Dictionary(); } var response = await Http.Post($"matchmake/{method}/{roomName}", options, headers); if (response == null) { throw new Exception($"Error with request: {response}"); } // forward reconnection token on reconnect if (method == "reconnect") { response.reconnectionToken = (string)options["reconnectionToken"]; } return await ConsumeSeatReservation(response, headers); } /// /// Create a connection with a room /// /// Additional info used as the /// Dictionary of options to use when connecting /// Dictionary of headers to pass when connecting /// protected Connection CreateConnection( SeatReservation room, Dictionary options = null, Dictionary headers = null ) { if (room.protocol != null && room.protocol == "h3") { // TODO: support h3 protocol (WebTransport) throw new Exception("WebTransport protocol is not supported yet. Please use WebSocket protocol instead."); } if (options == null) { options = new Dictionary(); } // Add authentication token to query string if (!string.IsNullOrEmpty(Http.AuthToken)) { options.Add("_authToken", Http.AuthToken); } List list = new List(); foreach (KeyValuePair item in options) { list.Add(item.Key + "=" + (item.Value != null ? Convert.ToString(item.Value) : "null")); } // Try to connect directly to custom publicAddress, if present. var endpoint = (room.publicAddress != null && room.publicAddress.Length > 0) ? new Uri($"{Endpoint.Scheme}://{room.publicAddress}") : Endpoint.Uri; var basePath = endpoint.AbsolutePath; // make sure to end path with backslash if (basePath.Length > 0 && !basePath.EndsWith("/")) { basePath += "/"; } UriBuilder uriBuilder = new UriBuilder(endpoint) { Path = $"{basePath}{room.processId}/{room.roomId}", Query = string.Join("&", list.ToArray()) }; return new Connection(uriBuilder.ToString(), headers); } /// /// Select the endpoint with the lowest latency. /// /// Array of endpoints to select from. /// Latency measurement options (protocol, pingCount). /// The client with the lowest latency. public static async Task SelectByLatency(string[] endpoints, LatencyOptions latencyOptions = null) { if (latencyOptions == null) { latencyOptions = new LatencyOptions(); } var clients = new Client[endpoints.Length]; for (int i = 0; i < endpoints.Length; i++) { clients[i] = new Client(endpoints[i]); } var latencyTasks = new Task<(int index, double latency, bool success)>[clients.Length]; for (int i = 0; i < clients.Length; i++) { int index = i; latencyTasks[i] = MeasureClientLatency(clients[index], index, latencyOptions); } var results = await Task.WhenAll(latencyTasks); int bestIndex = -1; double bestLatency = double.MaxValue; for (int i = 0; i < results.Length; i++) { if (results[i].success && results[i].latency < bestLatency) { bestLatency = results[i].latency; bestIndex = results[i].index; } } if (bestIndex == -1) { throw new Exception("All endpoints failed to respond"); } return clients[bestIndex]; } private static async Task<(int index, double latency, bool success)> MeasureClientLatency(Client client, int index, LatencyOptions options) { try { var latency = await client.GetLatency(options); var settings = client.Settings; ColyseusContext.Logger.Log($"Endpoint Latency: {latency}ms - {settings.colyseusServerAddress}:{settings.colyseusServerPort}"); return (index, latency, success: true); } catch (Exception) { return (index, latency: double.MaxValue, success: false); } } /// /// Create a new connection with the server, and measure the latency. /// /// Latency measurement options (protocol, pingCount). /// The average latency in milliseconds. public Task GetLatency(LatencyOptions options = null) { if (options == null) { options = new LatencyOptions(); } var protocol = options.Protocol ?? "ws"; var pingCount = options.PingCount > 0 ? options.PingCount : 1; if (protocol == "h3") { throw new Exception("WebTransport protocol is not supported yet. Please use WebSocket protocol instead."); } var tcs = new TaskCompletionSource(); var latencies = new List(); long pingStart = 0; var conn = new Connection(Endpoint.ToString(), null); void OnOpen() { pingStart = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); _ = conn.Send(new byte[] { Protocol.PING }); } void OnMessage(byte[] data) { latencies.Add(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - pingStart); if (latencies.Count < pingCount) { // Send another ping pingStart = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); _ = conn.Send(new byte[] { Protocol.PING }); } else { // Done, calculate average and close conn.OnOpen -= OnOpen; conn.OnMessage -= OnMessage; conn.OnError -= OnError; _ = conn.Close(); double sum = 0; for (int i = 0; i < latencies.Count; i++) { sum += latencies[i]; } tcs.TrySetResult(sum / latencies.Count); } } void OnError(string errorMsg) { conn.OnOpen -= OnOpen; conn.OnMessage -= OnMessage; conn.OnError -= OnError; tcs.TrySetException(new MatchMakeException((int)CloseCode.ABNORMAL_CLOSURE, $"Failed to get latency: {errorMsg}")); } conn.OnOpen += OnOpen; conn.OnMessage += OnMessage; conn.OnError += OnError; _ = conn.Connect(); return tcs.Task; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Client.cs.meta ================================================ fileFormatVersion: 2 guid: b31ab8167fc564dee91f45b0dd6d57ff ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Connection.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; // ReSharper disable InconsistentNaming namespace Colyseus { /// /// WebSocket connection representation with some custom functionality /// public class Connection { public event WebSocketOpenEventHandler OnOpen; public event WebSocketMessageEventHandler OnMessage; public event WebSocketErrorEventHandler OnError; public event WebSocketCloseEventHandler OnClose; /// /// Is the connection currently open /// public bool IsOpen; private WebSocketTransport _transport; private string _url; private Dictionary _headers; public Connection(string url, Dictionary headers) { _url = url; _headers = headers; } public async Task Connect() { _transport = new WebSocketTransport(); _transport.OnOpen += () => { IsOpen = true; OnOpen?.Invoke(); }; _transport.OnMessage += (data) => OnMessage?.Invoke(data); _transport.OnError += (msg) => OnError?.Invoke(msg); _transport.OnClose += (code) => { IsOpen = false; OnClose?.Invoke(code); }; await _transport.Connect(_url, _headers); } public Task Send(byte[] data) { return _transport.Send(data); } public Task Close() { return _transport.Close(); } public void Drop() { CancelConnection(); } public void CancelConnection() { _transport?.CancelConnection(); } #if !UNITY_WEBGL || UNITY_EDITOR /// /// Dispatch queued WebSocket callbacks manually from a custom game loop. /// This is only needed when no SynchronizationContext or external dispatcher is available. /// public void DispatchMessageQueue() { _transport?.DispatchMessageQueue(); } #endif /// /// Reconnect to the same endpoint with a new reconnection token /// /// The token to use for reconnection public async Task Reconnect(string reconnectionToken) { var uri = new Uri(_url); var queryParams = new List(); // Preserve existing query parameters if (!string.IsNullOrEmpty(uri.Query)) { var existingQuery = uri.Query.TrimStart('?'); if (!string.IsNullOrEmpty(existingQuery)) { foreach (var param in existingQuery.Split('&')) { var key = param.Split('=')[0]; // Skip params we're going to override if (key != "reconnectionToken" && key != "skipHandshake") { queryParams.Add(param); } } } } queryParams.Add("reconnectionToken=" + Uri.EscapeDataString(reconnectionToken)); queryParams.Add("skipHandshake=1"); var uriBuilder = new UriBuilder(uri) { Query = string.Join("&", queryParams) }; _url = uriBuilder.ToString(); ColyseusContext.Logger.Log($"Reconnecting to {_url}"); await Connect(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Connection.cs.meta ================================================ fileFormatVersion: 2 guid: c7b2c7c29325c4b2f81ddd0c68c1f995 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/HTTP.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using System.IO; using GameDevWare.Serialization; namespace Colyseus { [Serializable] public class ErrorResponse { public string error; } /// /// Class for building out server requests /// public class HTTP { public string AuthToken; private Settings _settings; public HTTP(Settings settings) { _settings = settings; } public async Task Get(string uriPath, Dictionary headers = null) { return await Request("GET", uriPath, null, headers); } public async Task Get(string uriPath, Dictionary headers = null) { return await Request("GET", uriPath, null, headers); } public async Task Post(string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { return await Request("POST", uriPath, jsonBody, headers); } public async Task Post(string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { return await Request("POST", uriPath, jsonBody, headers); } public async Task Delete(string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { return await Request("DELETE", uriPath, jsonBody, headers); } public async Task Delete(string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { return await Request("DELETE", uriPath, jsonBody, headers); } public async Task Put(string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { return await Request("PUT", uriPath, jsonBody, headers); } public async Task Put(string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { return await Request("PUT", uriPath, jsonBody, headers); } public async Task Request(string uriMethod, string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { return Json.Deserialize(await Request(uriMethod, uriPath, jsonBody, headers)); } public async Task Request(string uriMethod, string uriPath, Dictionary jsonBody = null, Dictionary headers = null) { byte[] body = null; if (jsonBody != null) { MemoryStream jsonBodyStream = new MemoryStream(); Json.Serialize(jsonBody, jsonBodyStream); body = jsonBodyStream.ToArray(); } var allHeaders = new Dictionary(); foreach (KeyValuePair pair in _settings.Headers) { allHeaders[pair.Key] = pair.Value; } if (!string.IsNullOrEmpty(AuthToken)) { allHeaders["Authorization"] = "Bearer " + AuthToken; } if (headers != null) { foreach (KeyValuePair header in headers) { allHeaders[header.Key] = header.Value; } } return await ColyseusContext.HttpClient.Request(uriMethod, GetRequestURL(uriPath), body, allHeaders); } public string GetRequestURL(string pathWithQueryString) { var splittedPath = pathWithQueryString.Split('?'); var path = splittedPath[0]; var query = (splittedPath.Length > 1) ? splittedPath[1] : ""; string forwardSlash = ""; if (!_settings.WebRequestEndpoint.EndsWith("/")) { forwardSlash = "/"; } // WebRequestEndpoint will include any path that is included with the server address field of the server settings object so we need to add the request specific path to the WebRequestEndpoint value UriBuilder uriBuilder = new UriBuilder($"{_settings.WebRequestEndpoint}{forwardSlash}{path}"); uriBuilder.Port = _settings.GetPort(); if (!string.IsNullOrEmpty(query)) { uriBuilder.Query = query; } return uriBuilder.ToString(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/HTTP.cs.meta ================================================ fileFormatVersion: 2 guid: ee3501581e55f44c8909b6ec76944724 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Manager.cs ================================================ using System.Collections.Generic; using UnityEngine; // ReSharper disable InconsistentNaming namespace Colyseus { /// /// Base manager class /// /// public class Manager : MonoBehaviour where T: Component { /// /// Reference to the Colyseus settings object. /// [SerializeField] protected Settings _colyseusSettings; // Getters //========================== /// /// The singleton instance of the Colyseus Manager. /// public static T Instance { get; private set; } /// /// Returns the Colyseus server address as defined /// in the object /// public string ColyseusServerAddress { get { return _colyseusSettings.colyseusServerAddress; } } /// /// Returns the Colyseus server port as defined /// in the object /// public string ColyseusServerPort { get { return _colyseusSettings.colyseusServerPort; } } /// /// Returned if the desired protocol security as defined /// in the object /// public bool ColyseusUseSecure { get { return _colyseusSettings.useSecureProtocol; } } //========================== /// /// The primary Client object responsible for making connections to the server. /// protected Client client; /// /// callback when the manager object has been destroyed. /// protected virtual void OnDestroy() { } /// /// callback when the script instance is being loaded. /// protected virtual void Awake() { InitializeInstance(); } /// /// Initializes the Colyseus manager singleton. /// private void InitializeInstance() { if (Instance != null) { Destroy(gameObject); return; } Instance = this as T; } /// /// callback when a script is enabled just before any of the Update methods are called the first time. /// protected virtual void Start() { } /// /// Frame-rate independent message for physics calculations. /// protected virtual void FixedUpdate() { } /// /// Override the current /// /// The new settings to use public virtual void OverrideSettings(Settings newSettings) { _colyseusSettings = newSettings; if (client != null) { client.Settings = newSettings; } } /// /// Get a copy of the manager's settings configuration /// /// public virtual Settings CloneSettings() { return Settings.Clone(_colyseusSettings); } /// /// Creates a new object, with the given endpoint, and returns it /// /// URL to the Colyseus server /// public Client CreateClient(string endpoint) { client = new Client(endpoint); return client; } /// /// /// Create a new along with any other client initialization you may need to perform /// /// public virtual void InitializeClient() { CreateClient(_colyseusSettings.WebSocketEndpoint); } /// /// callback that gets called just before app exit. /// protected virtual void OnApplicationQuit() { } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Manager.cs.meta ================================================ fileFormatVersion: 2 guid: dfd046c2a7f284b40abeb2e9c6030c2a ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/ColyseusContext.cs ================================================ using System; namespace Colyseus { public static class ColyseusContext { public static ILogger Logger { get; set; } public static IHttpClient HttpClient { get; set; } public static ITokenStorage TokenStorage { get; set; } /// /// When set, WebSocketTransport registers websockets here for external /// dispatching (e.g. MonoGame or a custom engine loop) instead of relying /// on SynchronizationContext dispatch or the shared fallback dispatcher. /// public static Action RegisterWebSocketForDispatch { get; set; } public static Action UnregisterWebSocketForDispatch { get; set; } static ColyseusContext() { SetDefaults(); } public static void SetDefaults() { Logger = new ConsoleLogger(); HttpClient = new DefaultHttpClient(); TokenStorage = new InMemoryTokenStorage(); RegisterWebSocketForDispatch = null; UnregisterWebSocketForDispatch = null; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/ColyseusContext.cs.meta ================================================ fileFormatVersion: 2 guid: e9d11516d4fd945f6bc6f9c38f59484f ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Defaults/ConsoleLogger.cs ================================================ using System; namespace Colyseus { public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } public void LogWarning(string message) { Console.WriteLine("[WARNING] " + message); } public void LogError(string message) { Console.Error.WriteLine("[ERROR] " + message); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Defaults/ConsoleLogger.cs.meta ================================================ fileFormatVersion: 2 guid: c61bec0c4a8f5466ead316cf8b48a7c3 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Defaults/DefaultHttpClient.cs ================================================ using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; namespace Colyseus { public class DefaultHttpClient : IHttpClient { private static readonly HttpClient _client = new HttpClient(); public async Task Request(string method, string url, byte[] body, Dictionary headers) { var request = new HttpRequestMessage(new HttpMethod(method), url); if (body != null) { request.Content = new ByteArrayContent(body); request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); } if (headers != null) { foreach (var header in headers) { request.Headers.TryAddWithoutValidation(header.Key, header.Value); } } var response = await _client.SendAsync(request); var responseBody = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { throw new HttpException((int)response.StatusCode, responseBody); } return responseBody; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Defaults/DefaultHttpClient.cs.meta ================================================ fileFormatVersion: 2 guid: b76ed9cf151944e27a2b3dd129f71ec1 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Defaults/InMemoryTokenStorage.cs ================================================ using System.Collections.Generic; namespace Colyseus { public class InMemoryTokenStorage : ITokenStorage { private Dictionary _tokens = new Dictionary(); public string GetToken(string key) { _tokens.TryGetValue(key, out var value); return value ?? string.Empty; } public void SetToken(string key, string value) { _tokens[key] = value; } public void DeleteToken(string key) { _tokens.Remove(key); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Defaults/InMemoryTokenStorage.cs.meta ================================================ fileFormatVersion: 2 guid: f08fd06971bb7429493182bdbe6bc586 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Defaults.meta ================================================ fileFormatVersion: 2 guid: a936aa0bb60cb45839f98f94e6b99ff3 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/IHttpClient.cs ================================================ using System.Collections.Generic; using System.Threading.Tasks; namespace Colyseus { public interface IHttpClient { Task Request(string method, string url, byte[] body, Dictionary headers); } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/IHttpClient.cs.meta ================================================ fileFormatVersion: 2 guid: d0b5fc7c6915d4fbb8d3912865fa3a39 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/ILogger.cs ================================================ namespace Colyseus { public interface ILogger { void Log(string message); void LogWarning(string message); void LogError(string message); } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/ILogger.cs.meta ================================================ fileFormatVersion: 2 guid: 7a651e0ae0860483aa3bdd31fb0c1ff0 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/ITokenStorage.cs ================================================ namespace Colyseus { public interface ITokenStorage { string GetToken(string key); void SetToken(string key, string value); void DeleteToken(string key); } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/ITokenStorage.cs.meta ================================================ fileFormatVersion: 2 guid: a28d0b00b73cb4b5e88d58109c69b4bc ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/IWebSocket.cs ================================================ namespace Colyseus { public delegate void WebSocketOpenEventHandler(); public delegate void WebSocketMessageEventHandler(byte[] data); public delegate void WebSocketErrorEventHandler(string errorMsg); public delegate void WebSocketCloseEventHandler(int closeCode); } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/IWebSocket.cs.meta ================================================ fileFormatVersion: 2 guid: 3fe20329183ae4a0eb091fee0b8a61bc ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/PreserveAttribute.cs ================================================ using System; namespace Colyseus { /// /// Custom Preserve attribute that works on all platforms. /// Unity's IL2CPP linker recognizes any attribute named "Preserve" regardless of namespace. /// [AttributeUsage(AttributeTargets.All)] public sealed class PreserveAttribute : Attribute { } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/PreserveAttribute.cs.meta ================================================ fileFormatVersion: 2 guid: 3123978311a4e4dd68b79ae85a198a18 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityHttpClient.cs ================================================ #if UNITY_5_3_OR_NEWER using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; using GameDevWare.Serialization; namespace Colyseus { public class UnityHttpClient : IHttpClient { public async Task Request(string method, string url, byte[] body, Dictionary headers) { using (UnityWebRequest req = new UnityWebRequest()) { req.method = method; req.url = url; if (body != null) { req.uploadHandler = new UploadHandlerRaw(body) { contentType = "application/json" }; } if (headers != null) { foreach (KeyValuePair header in headers) { req.SetRequestHeader(header.Key, header.Value); } } req.downloadHandler = new DownloadHandlerBuffer(); await req.SendWebRequest(); #if UNITY_2020_1_OR_NEWER if (req.result == UnityWebRequest.Result.ConnectionError || req.result == UnityWebRequest.Result.ProtocolError) #else if (req.isNetworkError || req.isHttpError) #endif { var errorMessage = req.error; if (!string.IsNullOrEmpty(req.downloadHandler.text)) { try { var data = Json.Deserialize(req.downloadHandler.text); if (!string.IsNullOrEmpty(data.error)) { errorMessage = data.error; } } catch { } } throw new HttpException((int)req.responseCode, errorMessage); } return req.downloadHandler.text; } } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityHttpClient.cs.meta ================================================ fileFormatVersion: 2 guid: 3cf7b230495374a76948cb8b92393feb ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityLogger.cs ================================================ #if UNITY_5_3_OR_NEWER using UnityEngine; namespace Colyseus { public class UnityLogger : ILogger { public void Log(string message) { Debug.Log(message); } public void LogWarning(string message) { Debug.LogWarning(message); } public void LogError(string message) { Debug.LogError(message); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityLogger.cs.meta ================================================ fileFormatVersion: 2 guid: 641a11947d6ae4ca785718cc095a8422 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityPlatform.cs ================================================ #if UNITY_5_3_OR_NEWER using UnityEngine; namespace Colyseus { /// /// Auto-initializes ColyseusContext with Unity-specific implementations. /// This runs before any scene loads, ensuring all Unity users get the correct platform bindings. /// public static class UnityPlatform { [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] public static void Initialize() { ColyseusContext.Logger = new UnityLogger(); ColyseusContext.HttpClient = new UnityHttpClient(); ColyseusContext.TokenStorage = new UnityTokenStorage(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityPlatform.cs.meta ================================================ fileFormatVersion: 2 guid: 7f3783fa34f724310885d356cb346c8b ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnitySettings.cs ================================================ #if UNITY_5_3_OR_NEWER using System; using UnityEngine; namespace Colyseus { /// /// ScriptableObject wrapper around the core Settings POCO for Unity inspector compatibility. /// Maintains backward compatibility with existing Unity projects that use Settings as a ScriptableObject. /// [CreateAssetMenu(fileName = "MyServerSettings", menuName = "Colyseus/Server Settings Scriptable Object", order = 1)] [Serializable] public class UnitySettings : ScriptableObject { /// /// The server address /// public string colyseusServerAddress = "localhost"; /// /// The port to connect to /// public string colyseusServerPort = "2567"; /// /// If true, we use secure protocols (wss, https) otherwise we use ws, http /// public bool useSecureProtocol = false; [SerializeField] private Settings.RequestHeader[] _requestHeaders; /// /// Convert this UnitySettings to a core Settings object /// public Settings ToSettings() { var settings = new Settings { colyseusServerAddress = colyseusServerAddress, colyseusServerPort = colyseusServerPort, useSecureProtocol = useSecureProtocol }; if (_requestHeaders != null) { settings.SetRequestHeaders(_requestHeaders); } return settings; } /// /// Implicit conversion to Settings for backward compatibility /// public static implicit operator Settings(UnitySettings unitySettings) { return unitySettings.ToSettings(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnitySettings.cs.meta ================================================ fileFormatVersion: 2 guid: 6d1d0994d204e41f2a1ad4fd8d0062a9 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityTokenStorage.cs ================================================ #if UNITY_5_3_OR_NEWER using UnityEngine; namespace Colyseus { public class UnityTokenStorage : ITokenStorage { public string GetToken(string key) { return PlayerPrefs.GetString(key); } public void SetToken(string key, string value) { PlayerPrefs.SetString(key, value); } public void DeleteToken(string key) { PlayerPrefs.DeleteKey(key); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity/UnityTokenStorage.cs.meta ================================================ fileFormatVersion: 2 guid: 49c288c14bb62415b8ccd07edc0fcae2 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform/Unity.meta ================================================ fileFormatVersion: 2 guid: d570a06a0e1f64179a4e50e694ea47a5 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Platform.meta ================================================ fileFormatVersion: 2 guid: 11ca095b9c22845eca472b0e5ae92d6d folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/ErrorCode.cs ================================================ // ReSharper disable InconsistentNaming namespace Colyseus { /// /// Colyseus error codes mapping. /// public class ErrorCode { public static int MATCHMAKE_NO_HANDLER = 4210; public static int MATCHMAKE_INVALID_CRITERIA = 4211; public static int MATCHMAKE_INVALID_ROOM_ID = 4212; public static int MATCHMAKE_UNHANDLED = 4213; public static int MATCHMAKE_EXPIRED = 4214; public static int AUTH_FAILED = 4215; public static int APPLICATION_ERROR = 4216; /// /// When local schema is different from schema on the server. /// public static int SCHEMA_MISMATCH = 4300; } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/ErrorCode.cs.meta ================================================ fileFormatVersion: 2 guid: 91ff24d8f9ec0420ebf7619585071190 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/MatchMakeResponse.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Colyseus { /// /// Wrapper class for a match making response /// /// Returns room and sessionId if successful; code and error if not [Serializable] public class SeatReservation { public string name; public string sessionId; public string roomId; public string publicAddress; public string processId; public string reconnectionToken; public bool devMode; public string protocol; } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/MatchMakeResponse.cs.meta ================================================ fileFormatVersion: 2 guid: 96657d6a1a8244a298dd8bf4c14906c2 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/MessageHandler.cs ================================================ using System; namespace Colyseus { /// /// Base interface for MessageHandlers /// public interface IMessageHandler { /// /// Message Type /// Type Type { get; } /// /// Base invocation for the MessageHandler /// /// The data to be passed into the function void Invoke(object message); } /// /// Base Implementation of the IMessageHandler interface /// /// Message Type public class MessageHandler : IMessageHandler { /// /// The Action this message will invoke /// public Action Action; /// /// Invokes this message's Action /// /// Data for the Action, will be cast to "T" public void Invoke(object message) { Action.Invoke((T) message); } /// /// Implementation of the interface Type /// /// typeof(T) public Type Type { get { return typeof(T); } } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/MessageHandler.cs.meta ================================================ fileFormatVersion: 2 guid: c67f9a6a0cc1b422195dd94a31f6e4e8 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/Protocol.cs ================================================ // ReSharper disable InconsistentNaming namespace Colyseus { /// /// Colyseus server protocol codes mapping. /// public class Protocol { /// /// When client receives its unique id. /// public static byte USER_ID = 1; // // Room-related (9~19) // /// /// When JOIN is requested. /// public static byte JOIN_REQUEST = 9; /// /// When JOIN request is accepted. /// public static byte JOIN_ROOM = 10; /// /// When an error has happened in the server-side. /// public static byte ERROR = 11; /// /// When server explicitly removes from the /// public static byte LEAVE_ROOM = 12; /// /// When server sends data to a particular /// public static byte ROOM_DATA = 13; /// /// When server sends state to its clients. /// public static byte ROOM_STATE = 14; /// /// When server sends state to its clients. /// public static byte ROOM_STATE_PATCH = 15; /// /// When server sends a Schema-encoded message. /// public static byte ROOM_DATA_SCHEMA = 16; public static byte ROOM_DATA_BYTES = 17; /// /// Ping message for measuring round-trip latency. /// public static byte PING = 18; // // Matchmaking messages (20~30) // public static byte ROOM_LIST = 20; // // Generic messages (50~60) // /// /// When server doesn't understand a request, it returns to the /// public static byte BAD_REQUEST = 50; // public Protocol (){} } public enum CloseCode { NORMAL_CLOSURE = 1000, GOING_AWAY = 1001, NO_STATUS_RECEIVED = 1005, ABNORMAL_CLOSURE = 1006, CONSENTED = 4000, SERVER_SHUTDOWN = 4001, WITH_ERROR = 4002, FAILED_TO_RECONNECT = 4003, MAY_TRY_RECONNECT = 4010, } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/Protocol.cs.meta ================================================ fileFormatVersion: 2 guid: c8b23e2aef1384ae9af846c81bbe9b72 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/RoomAvailable.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Colyseus { /// /// Wrapper class for important, shorthand Room information /// [Serializable] public class RoomAvailable { /// /// Current client count /// public int clients; /// /// Maximum clients in this room (may be Infinity when unlimited) /// public double maxClients; /// /// Room name /// public string name; /// /// Public host address (optional) /// public string publicAddress; /// /// Process ID used for connection /// public string processId; /// /// Room ID /// public string roomId; // public object metadata; } /// /// Get a collection of rooms /// /// Type of room inherited from [Serializable] public class RoomAvailableCollection { /// /// Rooms in this collection /// public T[] rooms; } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol/RoomAvailable.cs.meta ================================================ fileFormatVersion: 2 guid: 4844d7e0171e642b283b1c9bce462e70 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Protocol.meta ================================================ fileFormatVersion: 2 guid: 1bde545c640ce40139e914607b757e39 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Room.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using Colyseus.Schema; using GameDevWare.Serialization; namespace Colyseus { using Decode = Schema.Utils.Decode; using Encode = Schema.Utils.Encode; public delegate void NoArgsEventHandler(); /// /// Delegate function for when leaves this room. /// /// Reason for closure public delegate void CloseWithCodeEventHandler(int code); // , string reason /// /// Delegate function for when some error has been triggered in the room. /// /// Error code /// Error message public delegate void ErrorEventHandler(int code, string message); /// /// Interface for functions expected of any . /// public interface IRoom { event CloseWithCodeEventHandler OnLeave; /// /// Connection task /// /// Task that completes upon connection (or failure to connect) Task Connect(); /// /// Disconnection task /// /// True if by user's choice, false otherwise /// Task that completes upon Leaving Task Leave(bool consented); } [Serializable] public class ReconnectionToken { public string RoomId; public string Token; } /// /// Configuration options for automatic reconnection behavior /// [Serializable] public class ReconnectionOptions { /// /// Whether automatic reconnection is enabled. /// Set to false to disable automatic reconnection entirely. /// public bool Enabled = true; /// /// The maximum number of reconnection attempts. /// public int MaxRetries = 15; /// /// The minimum delay between reconnection attempts (in milliseconds). /// public int MinDelay = 100; /// /// The maximum delay between reconnection attempts (in milliseconds). /// public int MaxDelay = 5000; /// /// The minimum uptime of the room before reconnection attempts can be made (in milliseconds). /// public int MinUptime = 5000; /// /// The current number of reconnection attempts. /// public int RetryCount = 0; /// /// The initial delay between reconnection attempts (in milliseconds). /// public int Delay = 100; /// /// Whether the room is currently reconnecting. /// public bool IsReconnecting = false; /// /// The maximum number of enqueued messages to buffer. /// public int MaxEnqueuedMessages = 10; /// /// Buffer for messages sent while connection is not open. /// These messages will be sent once the connection is re-established. /// public List EnqueuedMessages = new List(); /// /// The function to calculate the delay between reconnection attempts. /// public Func Backoff = ExponentialBackoff; /// /// Default exponential backoff function. /// /// The current attempt number. /// The initial delay between reconnection attempts. /// The delay between reconnection attempts. public static int ExponentialBackoff(int attempt, int delay) { return (int)Math.Floor(Math.Pow(2, attempt) * delay); } } public class Room : IRoom where T : Schema.Schema { /// /// Delegate for room state changes /// /// The state change received /// Flag if first state received public delegate void StateChangeEventHandler(T state, bool isFirstState); /// /// Reference to the room's WebSocket Connection /// public Connection Connection; /// /// Room ID /// public string RoomId; /// /// Room name /// public string Name; /// /// Dictionary of the message handlers that have been provided to the room /// protected Dictionary OnMessageHandlers = new Dictionary(); /// /// Reference to the Serializer this room uses, determined and then generated based on the /// internal ISerializer Serializer; /// /// ID to determine which kind of serializer this room uses ( or /// ) /// public string SerializerId; /// /// The room's session ID /// public string SessionId; /// /// Reconnection Token for this room session. (must be provided for client.Reconnect()) /// public ReconnectionToken ReconnectionToken; /// /// Configuration options for automatic reconnection behavior. /// public ReconnectionOptions Reconnection = new ReconnectionOptions(); /// /// Timestamp when the room was joined (used for minUptime check). /// protected long JoinedAtTime = 0; /// /// Timestamp of the last ping request (in milliseconds). /// private long _lastPingTime = 0; /// /// Callback to invoke when ping response is received. /// private Action _pingCallback = null; /// /// Initializes a new instance of the class. /// It synchronizes state automatically with the server and send and receive messaes. /// /// The Room identifier public Room(string name) { Name = name; OnLeave += (code) => Destroy(); #if UNITY_EDITOR // Register handler for editor Room Inspector message type capture // Uses reflection to avoid assembly reference from runtime to editor OnMessage>("__playground_message_types", (messageTypes) => { try { var editorAssembly = System.Reflection.Assembly.Load("Colyseus.Editor"); if (editorAssembly != null) { var captureType = editorAssembly.GetType("Colyseus.Editor.RoomMessageType"); if (captureType != null) { var field = captureType.GetField("CapturedMessageTypes", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); if (field != null) { var dict = field.GetValue(null) as Dictionary>; if (dict != null && !string.IsNullOrEmpty(RoomId)) { dict[RoomId] = messageTypes; } } } } } catch { // Silently ignore if editor assembly is not available } }); #endif } /// /// Getter for the 's current state /// public T State { get { return Serializer.GetState(); } } [Obsolete(".Id is deprecated. Please use .RoomId instead.")] public string Id { get { return RoomId; } } /// /// Occurs when leaves this room. /// public event CloseWithCodeEventHandler OnLeave; /// /// Implementation of /// /// Response from .Connect() public async Task Connect() { await Connection.Connect(); } /// /// Leave the room /// /// If the user agreed to this disconnection /// Connection closure depending on user consent public async Task Leave(bool consented = true) { if (!Connection.IsOpen) { return; } if (RoomId != null) { var tcs = new TaskCompletionSource(); void onLeave(int code) { tcs.TrySetResult(code); } OnLeave += onLeave; try { if (consented) { await Connection.Send(new[] {Protocol.LEAVE_ROOM}); } else { await Connection.Close(); } } catch (OperationCanceledException) { // Connection already dropped — leave will complete via OnLeave } try { // Wait for the connection to fully close (with timeout to avoid hanging) await Task.WhenAny(tcs.Task, Task.Delay(5000)); } finally { OnLeave -= onLeave; } } else { OnLeave?.Invoke((int)CloseCode.CONSENTED); } } // Internal OnJoin event. It is used by Client.cs during matchmaking. internal event NoArgsEventHandler OnJoin; // /// Occurs when the room connection is dropped unexpectedly. /// Use to notify the user that a reconnection is being made. /// public event CloseWithCodeEventHandler OnDrop; /// /// Occurs when automatically reconnected to the room after a connection drop. /// public event NoArgsEventHandler OnReconnect; /// /// Occurs when some error has been triggered in the room. /// public event ErrorEventHandler OnError; /// /// Occurs after applying the patched state on this . /// public event StateChangeEventHandler OnStateChange; /// /// Called by the upon connection to a room /// /// The connection created by the client public void SetConnection(Connection connection) { Connection = connection; Connection.OnClose += code => { if (JoinedAtTime == 0) { OnError?.Invoke(code, "Connection closed before joining room"); return; } if ( code == (int) CloseCode.NO_STATUS_RECEIVED || code == (int) CloseCode.ABNORMAL_CLOSURE || code == (int) CloseCode.GOING_AWAY || code == (int) CloseCode.MAY_TRY_RECONNECT ) { OnDrop?.Invoke(code); HandleReconnection(code); } else { OnLeave?.Invoke(code); } }; // TODO: expose WebSocket error code! // Connection.OnError += (code, message) => OnError?.Invoke(code, message); Connection.OnError += message => this.OnError?.Invoke(0, message); Connection.OnMessage += bytes => this.ParseMessage(bytes); } /// /// Response to state changes received as messages /// /// /// /// Invokes everything subscribed to /// Byte array of the new state data /// Offset to provide the room's public void SetState(byte[] encodedState, int offset) { Serializer.SetState(encodedState, offset); OnStateChange?.Invoke(Serializer.GetState(), true); } /// /// Send a message by number type, without payload /// /// Message type public async Task Send(byte type) { byte[] bytes = new[] {Protocol.ROOM_DATA, type}; // If connection is not open, buffer the message if (!Connection.IsOpen) { EnqueueMessage(bytes); return; } await Connection.Send(bytes); } /// /// Send a message by number type with payload /// /// Message type /// Message payload public async Task Send(byte type, object message) { MemoryStream serializationOutput = new MemoryStream(); MsgPack.Serialize(message, serializationOutput, SerializationOptions.SuppressTypeInformation); byte[] initialBytes = {Protocol.ROOM_DATA, type}; byte[] encodedMessage = serializationOutput.ToArray(); byte[] bytes = new byte[initialBytes.Length + encodedMessage.Length]; Buffer.BlockCopy(initialBytes, 0, bytes, 0, initialBytes.Length); Buffer.BlockCopy(encodedMessage, 0, bytes, initialBytes.Length, encodedMessage.Length); // If connection is not open, buffer the message if (!Connection.IsOpen) { EnqueueMessage(bytes); return; } await Connection.Send(bytes); } /// /// Send a message by string type, without payload /// /// Message type public async Task Send(string type) { byte[] encodedType = Encoding.UTF8.GetBytes(type); byte[] initialBytes = Encode.getInitialBytesFromEncodedType(encodedType, Protocol.ROOM_DATA); byte[] bytes = new byte[initialBytes.Length + encodedType.Length]; Buffer.BlockCopy(initialBytes, 0, bytes, 0, initialBytes.Length); Buffer.BlockCopy(encodedType, 0, bytes, initialBytes.Length, encodedType.Length); // If connection is not open, buffer the message if (!Connection.IsOpen) { EnqueueMessage(bytes); return; } await Connection.Send(bytes); } /// /// Send a message by string type with payload /// /// Message type /// Message payload public async Task Send(string type, object message) { MemoryStream serializationOutput = new MemoryStream(); MsgPack.Serialize(message, serializationOutput, SerializationOptions.SuppressTypeInformation); byte[] encodedType = Encoding.UTF8.GetBytes(type); byte[] initialBytes = Encode.getInitialBytesFromEncodedType(encodedType, Protocol.ROOM_DATA); byte[] encodedMessage = serializationOutput.ToArray(); byte[] bytes = new byte[encodedType.Length + encodedMessage.Length + initialBytes.Length]; Buffer.BlockCopy(initialBytes, 0, bytes, 0, initialBytes.Length); Buffer.BlockCopy(encodedType, 0, bytes, initialBytes.Length, encodedType.Length); Buffer.BlockCopy(encodedMessage, 0, bytes, initialBytes.Length + encodedType.Length, encodedMessage.Length); // If connection is not open, buffer the message if (!Connection.IsOpen) { EnqueueMessage(bytes); return; } await Connection.Send(bytes); } /// /// Send a message by number type with raw bytes payload /// /// Message type /// Message payload public async Task SendBytes(byte type, byte[] bytes) { byte[] initialBytes = { Protocol.ROOM_DATA_BYTES, type }; byte[] bytesToSend = new byte[initialBytes.Length + bytes.Length]; Buffer.BlockCopy(initialBytes, 0, bytesToSend, 0, initialBytes.Length); Buffer.BlockCopy(bytes, 0, bytesToSend, initialBytes.Length, bytes.Length); // If connection is not open, buffer the message if (!Connection.IsOpen) { EnqueueMessage(bytesToSend); return; } await Connection.Send(bytesToSend); } /// /// Send a message by string type with raw bytes payload /// /// Message type /// Message payload public async Task SendBytes(string type, byte[] bytes) { byte[] encodedType = Encoding.UTF8.GetBytes(type); byte[] initialBytes = Encode.getInitialBytesFromEncodedType(encodedType, Protocol.ROOM_DATA_BYTES); byte[] bytesToSend = new byte[encodedType.Length + bytes.Length + initialBytes.Length]; Buffer.BlockCopy(initialBytes, 0, bytesToSend, 0, initialBytes.Length); Buffer.BlockCopy(encodedType, 0, bytesToSend, initialBytes.Length, encodedType.Length); Buffer.BlockCopy(bytes, 0, bytesToSend, initialBytes.Length + encodedType.Length, bytes.Length); // If connection is not open, buffer the message if (!Connection.IsOpen) { EnqueueMessage(bytesToSend); return; } await Connection.Send(bytesToSend); } /// /// Send a ping to the server and measure round-trip latency. /// /// Callback invoked with the round-trip time in milliseconds public async void Ping(Action callback) { // Skip if connection is not open if (Connection == null || !Connection.IsOpen) { return; } _lastPingTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); _pingCallback = callback; await Connection.Send(new[] { Protocol.PING }); } /// /// Method to add new message handlers to the room /// /// The type of message received /// /// The type of object this message should respond with public void OnMessage(string type, Action handler) { OnMessageHandlers.Add(type, new MessageHandler { Action = handler }); } /// /// Method to add new message handlers to the room /// /// The type of message received /// /// The type of object this message should respond with public void OnMessage(byte type, Action handler) { OnMessageHandlers.Add("i" + type, new MessageHandler { Action = handler }); } /// /// The function that will be called when the receives a message /// /// The message as provided from the protected async void ParseMessage(byte[] bytes) { byte code = bytes[0]; if (code == Protocol.JOIN_ROOM) { int offset = 1; var reconnectionToken = Encoding.UTF8.GetString(bytes, offset + 1, bytes[offset]); offset += reconnectionToken.Length + 1; SerializerId = Encoding.UTF8.GetString(bytes, offset + 1, bytes[offset]); offset += SerializerId.Length + 1; if (SerializerId == "schema") { try { Serializer = new SchemaSerializer(); } catch (Exception e) { DisplaySerializerErrorHelp(e, "Consider using the \"schema-codegen\" and providing the same room state for matchmaking instead of \"" + typeof(T).Name + "\""); } } else if (SerializerId == "fossil-delta") { ColyseusContext.Logger.LogError( "FossilDelta Serialization has been deprecated. It is highly recommended that you update your code to use the Schema Serializer. Otherwise, you must use an earlier version of the Colyseus plugin"); } else { Serializer = (ISerializer) new NoneSerializer(); } if (bytes.Length > offset) { try { Serializer.Handshake(bytes, offset); } catch (Exception e) { await Leave(false); OnError?.Invoke(ErrorCode.SCHEMA_MISMATCH, e.Message); return; } } ReconnectionToken = new ReconnectionToken() { RoomId = RoomId, Token = reconnectionToken }; if (JoinedAtTime == 0) { // First time joining JoinedAtTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); OnJoin?.Invoke(); } else { // Successful reconnection ColyseusContext.Logger.Log("[Colyseus reconnection]: Reconnection successful!"); Reconnection.IsReconnecting = false; OnReconnect?.Invoke(); } // Acknowledge JOIN_ROOM await Connection.Send(new[] {Protocol.JOIN_ROOM}); // Flush any enqueued messages that were buffered while disconnected await FlushEnqueuedMessages(); } else if (code == Protocol.ERROR) { Iterator it = new Iterator {Offset = 1}; float errorCode = Decode.DecodeNumber(bytes, it); string errorMessage = Decode.DecodeString(bytes, it); OnError?.Invoke((int) errorCode, errorMessage); } else if (code == Protocol.LEAVE_ROOM) { await Leave(); } else if (code == Protocol.ROOM_STATE) { SetState(bytes, 1); } else if (code == Protocol.ROOM_STATE_PATCH) { Patch(bytes, 1); } else if (code == Protocol.PING) { long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); _pingCallback?.Invoke((int)(now - _lastPingTime)); _pingCallback = null; } else if (code == Protocol.ROOM_DATA || code == Protocol.ROOM_DATA_BYTES) { IMessageHandler handler = null; object type; Iterator it = new Iterator {Offset = 1}; if (Decode.NumberCheck(bytes, it)) { type = Decode.DecodeNumber(bytes, it); OnMessageHandlers.TryGetValue("i" + type, out handler); } else { type = Decode.DecodeString(bytes, it); OnMessageHandlers.TryGetValue(type.ToString(), out handler); } if (handler != null) { object message = null; if ( code == Protocol.ROOM_DATA ) { // // MsgPack deserialization can be optimized: // https://github.com/deniszykov/msgpack-unity3d/issues/23 // message = bytes.Length > it.Offset ? MsgPack.Deserialize(handler.Type, new MemoryStream(bytes, it.Offset, bytes.Length - it.Offset, false)) : null; } else if ( code == Protocol.ROOM_DATA_BYTES ) { message = new byte[bytes.Length - it.Offset]; Buffer.BlockCopy(bytes, it.Offset, (byte[])message, 0, bytes.Length - it.Offset); } handler.Invoke(message); } else if (type != null && !type.ToString().StartsWith("__")) { ColyseusContext.Logger.LogWarning("room.OnMessage() not registered for: '" + type + "'"); } } } /// /// Update the state with just the new changes to the state /// /// Invokes everything subscribed to /// The updates to the state /// Offset to provide the room's protected void Patch(byte[] delta, int offset) { Serializer.Patch(delta, offset); OnStateChange?.Invoke(Serializer.GetState(), false); } /// /// Helper function to display errors with de-serializing messages from server /// /// Exception information /// Additional information to display /// Throws protected void DisplaySerializerErrorHelp(Exception e, string helpMessage) { ColyseusContext.Logger.LogWarning("The serializer from the server is: '" + SerializerId + "'. " + helpMessage); throw e; } private void HandleReconnection(int code) { if (!Reconnection.Enabled) { OnLeave?.Invoke(code); return; } // Check minimum uptime before allowing reconnection long currentTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); if (currentTime - JoinedAtTime < Reconnection.MinUptime) { ColyseusContext.Logger.Log($"[Colyseus reconnection]: Room not up long enough for auto-reconnect (min uptime: {Reconnection.MinUptime}ms)"); OnLeave?.Invoke((int)CloseCode.ABNORMAL_CLOSURE); return; } if (!Reconnection.IsReconnecting) { Reconnection.RetryCount = 0; Reconnection.IsReconnecting = true; } _ = RetryReconnection(); } private async Task RetryReconnection() { if (Reconnection.RetryCount >= Reconnection.MaxRetries) { // No more retries ColyseusContext.Logger.Log($"[Colyseus reconnection]: Reconnection failed after {Reconnection.MaxRetries} attempts."); Reconnection.IsReconnecting = false; OnLeave?.Invoke((int)CloseCode.FAILED_TO_RECONNECT); return; } Reconnection.RetryCount++; int delay = Math.Min(Reconnection.MaxDelay, Math.Max(Reconnection.MinDelay, Reconnection.Backoff(Reconnection.RetryCount, Reconnection.Delay))); ColyseusContext.Logger.Log($"[Colyseus reconnection]: Will retry in {delay / 1000f:F1} seconds..."); await Task.Delay(delay); ColyseusContext.Logger.Log($"[Colyseus reconnection]: Re-establishing sessionId '{SessionId}' with roomId '{RoomId}'... (attempt {Reconnection.RetryCount} of {Reconnection.MaxRetries})"); try { await Connection.Reconnect(ReconnectionToken.Token); } catch (Exception e) { ColyseusContext.Logger.Log($"[Colyseus reconnection]: Reconnect failed - {e.Message}"); _ = RetryReconnection(); } } /// /// Enqueue a message to be sent when the connection is re-established. /// /// The message data to enqueue private void EnqueueMessage(byte[] data) { Reconnection.EnqueuedMessages.Add(data); if (Reconnection.EnqueuedMessages.Count > Reconnection.MaxEnqueuedMessages) { Reconnection.EnqueuedMessages.RemoveAt(0); } } /// /// Flush all enqueued messages after reconnection. /// private async Task FlushEnqueuedMessages() { if (Reconnection.EnqueuedMessages.Count == 0) return; foreach (var message in Reconnection.EnqueuedMessages) { await Connection.Send(message); } Reconnection.EnqueuedMessages.Clear(); } private void Destroy() { Serializer.Teardown(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Room.cs.meta ================================================ fileFormatVersion: 2 guid: b196046f3733f4667a642ce7d71263ae ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/BigEndianBitConverter.cs ================================================ /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ namespace MiscUtil.Conversion { /// /// Implementation of EndianBitConverter which converts to/from big-endian /// byte arrays. /// public sealed class BigEndianBitConverter : EndianBitConverter { /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// /// /// Different computer architectures store data using different byte orders. "Big-endian" /// means the most significant byte is on the left end of a word. "Little-endian" means the /// most significant byte is on the right end of a word. /// /// true if this converter is little-endian, false otherwise. public sealed override bool IsLittleEndian() { return false; } /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// public sealed override Endianness Endianness { get { return Endianness.BigEndian; } } /// /// Copies the specified number of bytes from value to buffer, starting at index. /// /// The value to copy /// The number of bytes to copy /// The buffer to copy the bytes into /// The index to start at protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) { int endOffset = index+bytes-1; for (int i=0; i < bytes; i++) { buffer[endOffset-i] = unchecked((byte)(value&0xff)); value = value >> 8; } } /// /// Returns a value built from the specified number of bytes from the given buffer, /// starting at index. /// /// The data in byte array format /// The first index to use /// The number of bytes to use /// The value built from the given bytes protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) { long ret = 0; for (int i=0; i < bytesToConvert; i++) { ret = unchecked((ret << 8) | buffer[startIndex+i]); } return ret; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/BigEndianBitConverter.cs.meta ================================================ fileFormatVersion: 2 guid: d2db5d6cc28318447a02c83fcba42a1f MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/DoubleConverter.cs ================================================ /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Globalization; namespace MiscUtil.Conversion { /// /// A class to allow the conversion of doubles to string representations of /// their exact decimal values. The implementation aims for readability over /// efficiency. /// public class DoubleConverter { /// /// Converts the given double to a string representation of its /// exact decimal value. /// /// The double to convert. /// A string representation of the double's exact decimal value. public static string ToExactString (double d) { if (double.IsPositiveInfinity(d)) return "+Infinity"; if (double.IsNegativeInfinity(d)) return "-Infinity"; if (double.IsNaN(d)) return "NaN"; // Translate the double into sign, exponent and mantissa. long bits = BitConverter.DoubleToInt64Bits(d); bool negative = (bits < 0); int exponent = (int) ((bits >> 52) & 0x7ffL); long mantissa = bits & 0xfffffffffffffL; // Subnormal numbers; exponent is effectively one higher, // but there's no extra normalisation bit in the mantissa if (exponent==0) { exponent++; } // Normal numbers; leave exponent as it is but add extra // bit to the front of the mantissa else { mantissa = mantissa | (1L<<52); } // Bias the exponent. It's actually biased by 1023, but we're // treating the mantissa as m.0 rather than 0.m, so we need // to subtract another 52 from it. exponent -= 1075; if (mantissa == 0) { return "0"; } /* Normalize */ while((mantissa & 1) == 0) { /* i.e., Mantissa is even */ mantissa >>= 1; exponent++; } // Construct a new decimal expansion with the mantissa ArbitraryDecimal ad = new ArbitraryDecimal (mantissa); // If the exponent is less than 0, we need to repeatedly // divide by 2 - which is the equivalent of multiplying // by 5 and dividing by 10. if (exponent < 0) { for (int i=0; i < -exponent; i++) ad.MultiplyBy(5); ad.Shift(-exponent); } // Otherwise, we need to repeatedly multiply by 2 else { for (int i=0; i < exponent; i++) ad.MultiplyBy(2); } // Finally, return the string with an appropriate sign if (negative) return "-"+ad.ToString(); else return ad.ToString(); } /// /// Private class used for manipulating sequences of decimal digits. /// class ArbitraryDecimal { /// Digits in the decimal expansion, one byte per digit byte[] digits; /// /// How many digits are *after* the decimal point /// int decimalPoint=0; /// /// Constructs an arbitrary decimal expansion from the given long. /// The long must not be negative. /// internal ArbitraryDecimal (long x) { string tmp = x.ToString(CultureInfo.InvariantCulture); digits = new byte[tmp.Length]; for (int i=0; i < tmp.Length; i++) digits[i] = (byte) (tmp[i]-'0'); Normalize(); } /// /// Multiplies the current expansion by the given amount, which should /// only be 2 or 5. /// internal void MultiplyBy(int amount) { byte[] result = new byte[digits.Length+1]; for (int i=digits.Length-1; i >= 0; i--) { int resultDigit = digits[i]*amount+result[i+1]; result[i]=(byte)(resultDigit/10); result[i+1]=(byte)(resultDigit%10); } if (result[0] != 0) { digits=result; } else { Array.Copy (result, 1, digits, 0, digits.Length); } Normalize(); } /// /// Shifts the decimal point; a negative value makes /// the decimal expansion bigger (as fewer digits come after the /// decimal place) and a positive value makes the decimal /// expansion smaller. /// internal void Shift (int amount) { decimalPoint += amount; } /// /// Removes leading/trailing zeroes from the expansion. /// internal void Normalize() { int first; for (first=0; first < digits.Length; first++) if (digits[first]!=0) break; int last; for (last=digits.Length-1; last >= 0; last--) if (digits[last]!=0) break; if (first==0 && last==digits.Length-1) return; byte[] tmp = new byte[last-first+1]; for (int i=0; i < tmp.Length; i++) tmp[i]=digits[i+first]; decimalPoint -= digits.Length-(last+1); digits=tmp; } /// /// Converts the value to a proper decimal string representation. /// public override String ToString() { char[] digitString = new char[digits.Length]; for (int i=0; i < digits.Length; i++) digitString[i] = (char)(digits[i]+'0'); // Simplest case - nothing after the decimal point, // and last real digit is non-zero, eg value=35 if (decimalPoint==0) { return new string (digitString); } // Fairly simple case - nothing after the decimal // point, but some 0s to add, eg value=350 if (decimalPoint < 0) { return new string (digitString)+ new string ('0', -decimalPoint); } // Nothing before the decimal point, eg 0.035 if (decimalPoint >= digitString.Length) { return "0."+ new string ('0',(decimalPoint-digitString.Length))+ new string (digitString); } // Most complicated case - part of the string comes // before the decimal point, part comes after it, // eg 3.5 return new string (digitString, 0, digitString.Length-decimalPoint)+ "."+ new string (digitString, digitString.Length-decimalPoint, decimalPoint); } } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/DoubleConverter.cs.meta ================================================ fileFormatVersion: 2 guid: c7daa1e51f7f668468924517b6446fe9 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/EndianBitConverter.cs ================================================ /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Runtime.InteropServices; namespace MiscUtil.Conversion { /// /// Equivalent of System.BitConverter, but with either endianness. /// public abstract class EndianBitConverter { #region Endianness of this converter /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// /// /// Different computer architectures store data using different byte orders. "Big-endian" /// means the most significant byte is on the left end of a word. "Little-endian" means the /// most significant byte is on the right end of a word. /// /// true if this converter is little-endian, false otherwise. public abstract bool IsLittleEndian(); /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// public abstract Endianness Endianness { get; } #endregion #region Factory properties static LittleEndianBitConverter little = new LittleEndianBitConverter(); /// /// Returns a little-endian bit converter instance. The same instance is /// always returned. /// public static LittleEndianBitConverter Little { get { return little; } } static BigEndianBitConverter big = new BigEndianBitConverter(); /// /// Returns a big-endian bit converter instance. The same instance is /// always returned. /// public static BigEndianBitConverter Big { get { return big; } } #endregion #region Double/primitive conversions /// /// Converts the specified double-precision floating point number to a /// 64-bit signed integer. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A 64-bit signed integer whose value is equivalent to value. public long DoubleToInt64Bits(double value) { return BitConverter.DoubleToInt64Bits(value); } /// /// Converts the specified 64-bit signed integer to a double-precision /// floating point number. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A double-precision floating point number whose value is equivalent to value. public double Int64BitsToDouble (long value) { return BitConverter.Int64BitsToDouble(value); } /// /// Converts the specified single-precision floating point number to a /// 32-bit signed integer. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A 32-bit signed integer whose value is equivalent to value. public int SingleToInt32Bits(float value) { return new Int32SingleUnion(value).AsInt32; } /// /// Converts the specified 32-bit signed integer to a single-precision floating point /// number. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A single-precision floating point number whose value is equivalent to value. public float Int32BitsToSingle (int value) { return new Int32SingleUnion(value).AsSingle; } #endregion #region To(PrimitiveType) conversions /// /// Returns a Boolean value converted from one byte at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// true if the byte at startIndex in value is nonzero; otherwise, false. public bool ToBoolean (byte[] value, int startIndex) { CheckByteArgument(value, startIndex, 1); return BitConverter.ToBoolean(value, startIndex); } /// /// Returns a Unicode character converted from two bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A character formed by two bytes beginning at startIndex. public char ToChar (byte[] value, int startIndex) { return unchecked((char) (CheckedFromBytes(value, startIndex, 2))); } /// /// Returns a double-precision floating point number converted from eight bytes /// at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A double precision floating point number formed by eight bytes beginning at startIndex. public double ToDouble (byte[] value, int startIndex) { return Int64BitsToDouble(ToInt64(value, startIndex)); } /// /// Returns a single-precision floating point number converted from four bytes /// at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A single precision floating point number formed by four bytes beginning at startIndex. public float ToSingle (byte[] value, int startIndex) { return Int32BitsToSingle(ToInt32(value, startIndex)); } /// /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 16-bit signed integer formed by two bytes beginning at startIndex. public short ToInt16 (byte[] value, int startIndex) { return unchecked((short) (CheckedFromBytes(value, startIndex, 2))); } /// /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 32-bit signed integer formed by four bytes beginning at startIndex. public int ToInt32 (byte[] value, int startIndex) { return unchecked((int) (CheckedFromBytes(value, startIndex, 4))); } /// /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 64-bit signed integer formed by eight bytes beginning at startIndex. public long ToInt64 (byte[] value, int startIndex) { return CheckedFromBytes(value, startIndex, 8); } /// /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 16-bit unsigned integer formed by two bytes beginning at startIndex. public ushort ToUInt16 (byte[] value, int startIndex) { return unchecked((ushort) (CheckedFromBytes(value, startIndex, 2))); } /// /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 32-bit unsigned integer formed by four bytes beginning at startIndex. public uint ToUInt32 (byte[] value, int startIndex) { return unchecked((uint) (CheckedFromBytes(value, startIndex, 4))); } /// /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 64-bit unsigned integer formed by eight bytes beginning at startIndex. public ulong ToUInt64 (byte[] value, int startIndex) { return unchecked((ulong) (CheckedFromBytes(value, startIndex, 8))); } /// /// Checks the given argument for validity. /// /// The byte array passed in /// The start index passed in /// The number of bytes required /// value is a null reference /// /// startIndex is less than zero or greater than the length of value minus bytesRequired. /// static void CheckByteArgument(byte[] value, int startIndex, int bytesRequired) { if (value==null) { throw new ArgumentNullException("value"); } if (startIndex < 0 || startIndex > value.Length-bytesRequired) { throw new ArgumentOutOfRangeException("startIndex"); } } /// /// Checks the arguments for validity before calling FromBytes /// (which can therefore assume the arguments are valid). /// /// The bytes to convert after checking /// The index of the first byte to convert /// The number of bytes to convert /// long CheckedFromBytes(byte[] value, int startIndex, int bytesToConvert) { CheckByteArgument(value, startIndex, bytesToConvert); return FromBytes(value, startIndex, bytesToConvert); } /// /// Convert the given number of bytes from the given array, from the given start /// position, into a long, using the bytes as the least significant part of the long. /// By the time this is called, the arguments have been checked for validity. /// /// The bytes to convert /// The index of the first byte to convert /// The number of bytes to use in the conversion /// The converted number protected abstract long FromBytes(byte[] value, int startIndex, int bytesToConvert); #endregion #region ToString conversions /// /// Returns a String converted from the elements of a byte array. /// /// An array of bytes. /// All the elements of value are converted. /// /// A String of hexadecimal pairs separated by hyphens, where each pair /// represents the corresponding element in value; for example, "7F-2C-4A". /// public static string ToString(byte[] value) { return BitConverter.ToString(value); } /// /// Returns a String converted from the elements of a byte array starting at a specified array position. /// /// An array of bytes. /// The starting position within value. /// The elements from array position startIndex to the end of the array are converted. /// /// A String of hexadecimal pairs separated by hyphens, where each pair /// represents the corresponding element in value; for example, "7F-2C-4A". /// public static string ToString(byte[] value, int startIndex) { return BitConverter.ToString(value, startIndex); } /// /// Returns a String converted from a specified number of bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// The number of bytes to convert. /// The length elements from array position startIndex are converted. /// /// A String of hexadecimal pairs separated by hyphens, where each pair /// represents the corresponding element in value; for example, "7F-2C-4A". /// public static string ToString(byte[] value, int startIndex, int length) { return BitConverter.ToString(value, startIndex, length); } #endregion #region Decimal conversions /// /// Returns a decimal value converted from sixteen bytes /// at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A decimal formed by sixteen bytes beginning at startIndex. public decimal ToDecimal (byte[] value, int startIndex) { // HACK: This always assumes four parts, each in their own endianness, // starting with the first part at the start of the byte array. // On the other hand, there's no real format specified... int[] parts = new int[4]; for (int i=0; i < 4; i++) { parts[i] = ToInt32(value, startIndex+i*4); } return new Decimal(parts); } /// /// Returns the specified decimal value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 16. public byte[] GetBytes(decimal value) { byte[] bytes = new byte[16]; int[] parts = decimal.GetBits(value); for (int i=0; i < 4; i++) { CopyBytesImpl(parts[i], 4, bytes, i*4); } return bytes; } /// /// Copies the specified decimal value into the specified byte array, /// beginning at the specified index. /// /// A character to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(decimal value, byte[] buffer, int index) { int[] parts = decimal.GetBits(value); for (int i=0; i < 4; i++) { CopyBytesImpl(parts[i], 4, buffer, i*4+index); } } #endregion #region GetBytes conversions /// /// Returns an array with the given number of bytes formed /// from the least significant bytes of the specified value. /// This is used to implement the other GetBytes methods. /// /// The value to get bytes for /// The number of significant bytes to return byte[] GetBytes(long value, int bytes) { byte[] buffer = new byte[bytes]; CopyBytes(value, bytes, buffer, 0); return buffer; } /// /// Returns the specified Boolean value as an array of bytes. /// /// A Boolean value. /// An array of bytes with length 1. public byte[] GetBytes(bool value) { return BitConverter.GetBytes(value); } /// /// Returns the specified Unicode character value as an array of bytes. /// /// A character to convert. /// An array of bytes with length 2. public byte[] GetBytes(char value) { return GetBytes(value, 2); } /// /// Returns the specified double-precision floating point value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(double value) { return GetBytes(DoubleToInt64Bits(value), 8); } /// /// Returns the specified 16-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 2. public byte[] GetBytes(short value) { return GetBytes(value, 2); } /// /// Returns the specified 32-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(int value) { return GetBytes(value, 4); } /// /// Returns the specified 64-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(long value) { return GetBytes(value, 8); } /// /// Returns the specified single-precision floating point value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(float value) { return GetBytes(SingleToInt32Bits(value), 4); } /// /// Returns the specified 16-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 2. public byte[] GetBytes(ushort value) { return GetBytes(value, 2); } /// /// Returns the specified 32-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(uint value) { return GetBytes(value, 4); } /// /// Returns the specified 64-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(ulong value) { return GetBytes(unchecked((long)value), 8); } #endregion #region CopyBytes conversions /// /// Copies the given number of bytes from the least-specific /// end of the specified value into the specified byte array, beginning /// at the specified index. /// This is used to implement the other CopyBytes methods. /// /// The value to copy bytes for /// The number of significant bytes to copy /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into void CopyBytes(long value, int bytes, byte[] buffer, int index) { if (buffer==null) { throw new ArgumentNullException("buffer", "Byte array must not be null"); } if (buffer.Length < index+bytes) { throw new ArgumentOutOfRangeException("Buffer not big enough for value"); } CopyBytesImpl(value, bytes, buffer, index); } /// /// Copies the given number of bytes from the least-specific /// end of the specified value into the specified byte array, beginning /// at the specified index. /// This must be implemented in concrete derived classes, but the implementation /// may assume that the value will fit into the buffer. /// /// The value to copy bytes for /// The number of significant bytes to copy /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into protected abstract void CopyBytesImpl(long value, int bytes, byte[] buffer, int index); /// /// Copies the specified Boolean value into the specified byte array, /// beginning at the specified index. /// /// A Boolean value. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(bool value, byte[] buffer, int index) { CopyBytes(value ? 1 : 0, 1, buffer, index); } /// /// Copies the specified Unicode character value into the specified byte array, /// beginning at the specified index. /// /// A character to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(char value, byte[] buffer, int index) { CopyBytes(value, 2, buffer, index); } /// /// Copies the specified double-precision floating point value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(double value, byte[] buffer, int index) { CopyBytes(DoubleToInt64Bits(value), 8, buffer, index); } /// /// Copies the specified 16-bit signed integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(short value, byte[] buffer, int index) { CopyBytes(value, 2, buffer, index); } /// /// Copies the specified 32-bit signed integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(int value, byte[] buffer, int index) { CopyBytes(value, 4, buffer, index); } /// /// Copies the specified 64-bit signed integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(long value, byte[] buffer, int index) { CopyBytes(value, 8, buffer, index); } /// /// Copies the specified single-precision floating point value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(float value, byte[] buffer, int index) { CopyBytes(SingleToInt32Bits(value), 4, buffer, index); } /// /// Copies the specified 16-bit unsigned integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(ushort value, byte[] buffer, int index) { CopyBytes(value, 2, buffer, index); } /// /// Copies the specified 32-bit unsigned integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(uint value, byte[] buffer, int index) { CopyBytes(value, 4, buffer, index); } /// /// Copies the specified 64-bit unsigned integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(ulong value, byte[] buffer, int index) { CopyBytes(unchecked((long)value), 8, buffer, index); } #endregion #region Private struct used for Single/Int32 conversions /// /// Union used solely for the equivalent of DoubleToInt64Bits and vice versa. /// [StructLayout(LayoutKind.Explicit)] struct Int32SingleUnion { /// /// Int32 version of the value. /// [FieldOffset(0)] int i; /// /// Single version of the value. /// [FieldOffset(0)] float f; /// /// Creates an instance representing the given integer. /// /// The integer value of the new instance. internal Int32SingleUnion(int i) { this.f = 0; // Just to keep the compiler happy this.i = i; } /// /// Creates an instance representing the given floating point number. /// /// The floating point value of the new instance. internal Int32SingleUnion(float f) { this.i = 0; // Just to keep the compiler happy this.f = f; } /// /// Returns the value of the instance as an integer. /// internal int AsInt32 { get { return i; } } /// /// Returns the value of the instance as a floating point number. /// internal float AsSingle { get { return f; } } } #endregion } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/EndianBitConverter.cs.meta ================================================ fileFormatVersion: 2 guid: aa6dd963847f2e6408c89a530095710a MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/Endianness.cs ================================================ /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ namespace MiscUtil.Conversion { /// /// Endianness of a converter /// public enum Endianness { /// /// Little endian - least significant byte first /// LittleEndian, /// /// Big endian - most significant byte first /// BigEndian } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/Endianness.cs.meta ================================================ fileFormatVersion: 2 guid: 8885dda9cbb1c6246840582b6c637300 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/LittleEndianBitConverter.cs ================================================ /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ namespace MiscUtil.Conversion { /// /// Implementation of EndianBitConverter which converts to/from little-endian /// byte arrays. /// public sealed class LittleEndianBitConverter : EndianBitConverter { /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// /// /// Different computer architectures store data using different byte orders. "Big-endian" /// means the most significant byte is on the left end of a word. "Little-endian" means the /// most significant byte is on the right end of a word. /// /// true if this converter is little-endian, false otherwise. public sealed override bool IsLittleEndian() { return true; } /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// public sealed override Endianness Endianness { get { return Endianness.LittleEndian; } } /// /// Copies the specified number of bytes from value to buffer, starting at index. /// /// The value to copy /// The number of bytes to copy /// The buffer to copy the bytes into /// The index to start at protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) { for (int i=0; i < bytes; i++) { buffer[i+index] = unchecked((byte)(value&0xff)); value = value >> 8; } } /// /// Returns a value built from the specified number of bytes from the given buffer, /// starting at index. /// /// The data in byte array format /// The first index to use /// The number of bytes to use /// The value built from the given bytes protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) { long ret = 0; for (int i=0; i < bytesToConvert; i++) { ret = unchecked((ret << 8) | buffer[startIndex+bytesToConvert-1-i]); } return ret; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion/LittleEndianBitConverter.cs.meta ================================================ fileFormatVersion: 2 guid: dfe60c6da8d01634ab384a42243791ad MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Conversion.meta ================================================ fileFormatVersion: 2 guid: 3825dca9a7fae88429eff0ef4538364d folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/NoneSerializer.cs ================================================ namespace Colyseus { // TODO: remove dummy state dependency from NoneSerializer. public class NoState : Schema.Schema { } /// /// An empty implementation of /// public class NoneSerializer : ISerializer { NoState state = new NoState(); /// public void SetState(byte[] rawEncodedState, int offset) { } /// public NoState GetState() { return state; } /// public void Patch(byte[] bytes, int offset) { } /// public void Teardown() { } /// public void Handshake(byte[] bytes, int offset) { } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/NoneSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: ad9bf8bd4c18f480ca4fe0084bf21d92 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Callbacks/Callbacks.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace Colyseus.Schema { /// /// Helper class to extract property chains from nested member expressions /// internal static class ExpressionHelper { /// /// Extracts the property chain from a nested member expression. /// For example, `state => state.staticEntities.entities` returns ["staticEntities", "entities"] /// public static List GetPropertyChain(Expression expression) { var chain = new List(); var current = expression; while (current is MemberExpression memberExpr) { chain.Insert(0, memberExpr.Member.Name); current = memberExpr.Expression; } return chain; } /// /// Gets the root instance from an expression by walking up to the parameter. /// public static Expression GetRootExpression(Expression expression) { var current = expression; while (current is MemberExpression memberExpr) { current = memberExpr.Expression; } return current; } } /// /// Delegate for handling events given a and a /// /// /// /// public delegate void PropertyChangeEventHandler(T currentValue, T previousValue); /// /// Delegate function when any property on the schema structure has changed. /// public delegate void OnInstanceChangeEventHandler(); /// /// Delegate function for handling removal /// public delegate void OnRemoveEventHandler(); /// /// Delegate for handling events given a and a /// /// The affected value /// The key we're affecting /// The type of we're attempting to access /// The type public delegate void KeyValueEventHandler(K key, T value); public class StateCallbackStrategy where TState : Schema { protected Decoder Decoder; protected HashSet UniqueRefIds = new HashSet(); protected bool isTriggering = false; public StateCallbackStrategy(Decoder decoder) { Decoder = decoder; Decoder.TriggerChanges = TriggerChanges; } protected Action AddCallback(int refId, object operationOrProperty, Delegate handler) { if (!Decoder.Refs.callbacks.TryGetValue(refId, out var handlers)) { handlers = new Dictionary>(); Decoder.Refs.callbacks[refId] = handlers; } if (!handlers.ContainsKey(operationOrProperty)) { handlers[operationOrProperty] = new List(); } handlers[operationOrProperty].Add(handler); return () => handlers[operationOrProperty].Remove(handler); } /// /// Navigates through a nested property chain and invokes a callback when the final property is reached. /// Automatically listens to intermediate properties and re-attaches handlers when they change. /// /// The root instance to start navigation from /// List of property names to navigate through /// Current position in the property chain /// Callback invoked with the final Schema instance and property name /// Whether to trigger immediately if available /// Action to remove all attached listeners protected Action NavigateNestedProperties(Schema instance, List propertyChain, int currentIndex, Func onFinalProperty, bool immediate = true) { if (currentIndex >= propertyChain.Count) { return () => { }; } var propertyName = propertyChain[currentIndex]; var isLastProperty = currentIndex == propertyChain.Count - 1; if (isLastProperty) { // We've reached the final property, invoke the callback return onFinalProperty(instance, propertyName); } // We're at an intermediate property, need to navigate deeper Action removeHandler = () => { }; Action removeListener = () => removeHandler(); var currentValue = instance[propertyName]; if (currentValue == null) { // Intermediate property not available yet, listen for it removeHandler = AddCallback(instance.__refId, propertyName, new PropertyChangeEventHandler((newValue, prevValue) => { // Remove previous deep listener if any removeHandler(); if (newValue != null) { // Navigate to the next level removeHandler = NavigateNestedProperties(newValue, propertyChain, currentIndex + 1, onFinalProperty, immediate); } })); } else if (currentValue is Schema schemaValue) { // Intermediate property is available, navigate deeper removeHandler = NavigateNestedProperties(schemaValue, propertyChain, currentIndex + 1, onFinalProperty, immediate); // Also listen for changes to re-attach handlers var removeChangeListener = AddCallback(instance.__refId, propertyName, new PropertyChangeEventHandler((newValue, prevValue) => { // Remove previous deep listener removeHandler(); if (newValue != null) { // Navigate to the next level with the new value removeHandler = NavigateNestedProperties(newValue, propertyChain, currentIndex + 1, onFinalProperty, immediate); } })); var originalRemoveHandler = removeHandler; removeHandler = () => { originalRemoveHandler(); removeChangeListener(); }; } return removeListener; } protected Action AddCallbackOrWaitCollectionAvailable(TInstance instance, Expression> propertyExpression, OPERATION operation, Delegate handler, bool immediate = true) where TInstance : Schema where TReturn : IRef { var propertyChain = ExpressionHelper.GetPropertyChain(propertyExpression.Body); // If only one property in chain, use the original simple logic if (propertyChain.Count == 1) { return AddCallbackOrWaitCollectionAvailableSimple(instance, propertyChain[0], operation, handler, immediate); } // For nested properties, use the navigation helper Action removeHandler = () => { }; Action removeAll = () => removeHandler(); removeHandler = NavigateNestedProperties(instance, propertyChain, 0, (finalInstance, finalPropertyName) => { return AddCallbackOrWaitCollectionAvailableSimple(finalInstance, finalPropertyName, operation, handler, immediate); }, immediate); return removeAll; } /// /// Original simple implementation for single-level property access /// protected Action AddCallbackOrWaitCollectionAvailableSimple(Schema instance, string propertyName, OPERATION operation, Delegate handler, bool immediate = true) { Action removeHandler = () => { }; Action removeOnAdd = () => removeHandler(); // Collection not available yet. Listen for its availability before attaching the handler. if (instance[propertyName] == null) { Action removePropertyCallback = null; removePropertyCallback = AddCallback(instance.__refId, propertyName, new PropertyChangeEventHandler((IRef collection, IRef _) => { if (collection != null) { // Remove the property listener now that collection is available removePropertyCallback(); removeHandler = AddCallback(collection.__refId, operation, handler); } })); removeHandler = removePropertyCallback; return removeOnAdd; } else { // // Call immediately if collection is already available, if it's an ADD operation. // immediate = immediate && isTriggering == false; if (operation == OPERATION.ADD && immediate) { ((ISchemaCollection)instance[propertyName]).ForEach((key, value) => { handler.DynamicInvoke(key, value); }); } return AddCallback(((IRef)instance[propertyName]).__refId, operation, handler); } } public Action Listen(Expression> propertyExpression, PropertyChangeEventHandler handler, bool immediate = true) { return Listen(Decoder.State, propertyExpression, handler, immediate); } public Action Listen(TInstance instance, Expression> propertyExpression, PropertyChangeEventHandler handler, bool immediate = true) where TInstance : Schema { var propertyChain = ExpressionHelper.GetPropertyChain(propertyExpression.Body); // If only one property in chain, use the original simple logic if (propertyChain.Count == 1) { return ListenSimple(instance, propertyChain[0], handler, immediate); } // For nested properties, use the navigation helper Action removeHandler = () => { }; Action removeAll = () => removeHandler(); removeHandler = NavigateNestedProperties(instance, propertyChain, 0, (finalInstance, finalPropertyName) => { return ListenSimple(finalInstance, finalPropertyName, handler, immediate); }, immediate); return removeAll; } /// /// Original simple implementation for single-level property listening /// protected Action ListenSimple(Schema instance, string propertyName, PropertyChangeEventHandler handler, bool immediate = true) { immediate = immediate && isTriggering == false; // // Call handler immediately if property is already available. // if (immediate && instance[propertyName] != null && !instance[propertyName].Equals(default(TReturn))) { handler((TReturn)instance[propertyName], default(TReturn)); } return AddCallback(instance.__refId, propertyName, handler); } public Action OnChange(T instance, OnInstanceChangeEventHandler handler) where T : Schema { return AddCallback(instance.__refId, OPERATION.REPLACE, handler); } public Action OnAdd(Expression>> propertyExpression, KeyValueEventHandler handler, bool immediate = true) { return OnAdd(Decoder.State, propertyExpression, handler, immediate); } public Action OnAdd(TInstance instance, Expression>> propertyExpression, KeyValueEventHandler handler, bool immediate = true) where TInstance : Schema { return AddCallbackOrWaitCollectionAvailable(instance, propertyExpression, OPERATION.ADD, handler, immediate); } public Action OnAdd(Expression>> propertyExpression, KeyValueEventHandler handler, bool immediate = true) { return OnAdd(Decoder.State, propertyExpression, handler, immediate); } public Action OnAdd(TInstance instance, Expression>> propertyExpression, KeyValueEventHandler handler, bool immediate = true) where TInstance : Schema { return AddCallbackOrWaitCollectionAvailable(instance, propertyExpression, OPERATION.ADD, handler, immediate); } public Action OnChange(Expression>> propertyExpression, KeyValueEventHandler handler) { return OnChange(Decoder.State, propertyExpression, handler); } public Action OnChange(TInstance instance, Expression>> propertyExpression, KeyValueEventHandler handler) where TInstance : Schema { return AddCallbackOrWaitCollectionAvailable(instance, propertyExpression, OPERATION.REPLACE, handler); } public Action OnChange(Expression>> propertyExpression, KeyValueEventHandler handler) { return OnChange(Decoder.State, propertyExpression, handler); } public Action OnChange(TInstance instance, Expression>> propertyExpression, KeyValueEventHandler handler) where TInstance : Schema { return AddCallbackOrWaitCollectionAvailable(instance, propertyExpression, OPERATION.REPLACE, handler); } public Action OnRemove(Expression>> propertyExpression, KeyValueEventHandler handler) { return OnRemove(Decoder.State, propertyExpression, handler); } public Action OnRemove(TInstance instance, Expression>> propertyExpression, KeyValueEventHandler handler) where TInstance : Schema { return AddCallbackOrWaitCollectionAvailable(instance, propertyExpression, OPERATION.DELETE, handler); } public Action OnRemove(Expression>> propertyExpression, KeyValueEventHandler handler) { return OnRemove(Decoder.State, propertyExpression, handler); } public Action OnRemove(TInstance instance, Expression>> propertyExpression, KeyValueEventHandler handler) where TInstance : Schema { return AddCallbackOrWaitCollectionAvailable(instance, propertyExpression, OPERATION.DELETE, handler); } // // String-based overloads (for DynamicSchema / untyped access) // public Action Listen(string propertyName, PropertyChangeEventHandler handler, bool immediate = true) { return ListenSimple(Decoder.State, propertyName, handler, immediate); } public Action Listen(Schema instance, string propertyName, PropertyChangeEventHandler handler, bool immediate = true) { return ListenSimple(instance, propertyName, handler, immediate); } public Action OnAdd(string propertyName, KeyValueEventHandler handler, bool immediate = true) { return AddCallbackOrWaitCollectionAvailableSimple(Decoder.State, propertyName, OPERATION.ADD, handler, immediate); } public Action OnAdd(Schema instance, string propertyName, KeyValueEventHandler handler, bool immediate = true) { return AddCallbackOrWaitCollectionAvailableSimple(instance, propertyName, OPERATION.ADD, handler, immediate); } public Action OnAdd(string propertyName, KeyValueEventHandler handler, bool immediate = true) { return AddCallbackOrWaitCollectionAvailableSimple(Decoder.State, propertyName, OPERATION.ADD, handler, immediate); } public Action OnAdd(Schema instance, string propertyName, KeyValueEventHandler handler, bool immediate = true) { return AddCallbackOrWaitCollectionAvailableSimple(instance, propertyName, OPERATION.ADD, handler, immediate); } public Action OnRemove(string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(Decoder.State, propertyName, OPERATION.DELETE, handler); } public Action OnRemove(Schema instance, string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(instance, propertyName, OPERATION.DELETE, handler); } public Action OnRemove(string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(Decoder.State, propertyName, OPERATION.DELETE, handler); } public Action OnRemove(Schema instance, string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(instance, propertyName, OPERATION.DELETE, handler); } public Action OnChange(string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(Decoder.State, propertyName, OPERATION.REPLACE, handler); } public Action OnChange(Schema instance, string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(instance, propertyName, OPERATION.REPLACE, handler); } public Action OnChange(string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(Decoder.State, propertyName, OPERATION.REPLACE, handler); } public Action OnChange(Schema instance, string propertyName, KeyValueEventHandler handler) { return AddCallbackOrWaitCollectionAvailableSimple(instance, propertyName, OPERATION.REPLACE, handler); } /// /// Binds a schema property to a target object. /// /// /// /// public Action BindTo(Schema from, T to, bool immediate = true) { var action = (Action)(() => { var toType = typeof(T); foreach (var field in from.fieldsByIndex) { var fromValue = from.GetType().GetProperty(field.Value)?.GetValue(from); var toProperty = toType.GetProperty(field.Value); if (toProperty != null && fromValue != null) { if (toProperty.PropertyType.IsAssignableFrom(fromValue.GetType())) { toProperty.SetValue(to, fromValue); } else { // Handle type mismatch, maybe convert or log ColyseusContext.Logger.Log($"BindTo: Type mismatch for property {field.Value}: Cannot assign {fromValue.GetType().Name} to {toProperty.PropertyType.Name}"); } } } }); if (immediate) { action(); } return AddCallback(from.__refId, to, action); } protected void TriggerChanges(ref List allChanges) { UniqueRefIds.Clear(); foreach (DataChange change in allChanges) { var refId = change.RefId; var _ref = Decoder.Refs.Get(refId); // Dictionary> callbacks = Decoder.Refs.callbacks[refId]; Decoder.Refs.callbacks.TryGetValue(refId, out var callbacks); if (callbacks == null) { continue; } // // trigger onRemove on child structure. // if ((change.Op & (byte)OPERATION.DELETE) == (byte)OPERATION.DELETE && change.PreviousValue is Schema) { Decoder.Refs.callbacks.TryGetValue(((Schema)change.PreviousValue).__refId, out var deleteCallbacks); if (deleteCallbacks != null && deleteCallbacks.ContainsKey(OPERATION.DELETE) && deleteCallbacks[OPERATION.DELETE] != null) { foreach (var callback in deleteCallbacks[OPERATION.DELETE]) { callback.DynamicInvoke(); } } } if (_ref is Schema) { // // Handle Schema instance // if (!UniqueRefIds.Contains(refId)) { // trigger onChange callbacks.TryGetValue(OPERATION.REPLACE, out var replaceCallbacks); if (replaceCallbacks != null) { foreach (var callback in replaceCallbacks) { try { callback.DynamicInvoke(); } catch (Exception e) { ColyseusContext.Logger.LogError(e.Message); } } } } callbacks.TryGetValue(change.Field, out var fieldCallbacks); if (fieldCallbacks != null) { // iterate a copy — deferred listeners may remove themselves during iteration foreach (var callback in fieldCallbacks.ToList()) { try { isTriggering = true; callback.DynamicInvoke(change.Value, change.PreviousValue); } catch (Exception e) { ColyseusContext.Logger.LogError(e.Message); } finally { isTriggering = false; } } } } else { // // Handle collection of items // ISchemaCollection container = (ISchemaCollection)_ref; if ((change.Op & (byte)OPERATION.DELETE) == (byte)OPERATION.DELETE) { if (change.PreviousValue != container.GetTypeDefaultValue()) { // trigger onRemove callbacks.TryGetValue(OPERATION.DELETE, out var deleteCallbacks); if (deleteCallbacks != null) { foreach (var callback in deleteCallbacks) { callback.DynamicInvoke(change.DynamicIndex ?? change.Field, change.PreviousValue); } } } // Handle DELETE_AND_ADD operation if ((change.Op & (byte)OPERATION.ADD) == (byte)OPERATION.ADD) { // trigger onRemove callbacks.TryGetValue(OPERATION.ADD, out var addCallbacks); if (addCallbacks != null) { isTriggering = true; foreach (var callback in addCallbacks) { callback.DynamicInvoke(change.DynamicIndex, change.Value); } isTriggering = false; } } } else if ( (change.Op & (byte)OPERATION.ADD) == (byte)OPERATION.ADD && change.PreviousValue != change.Value ) { // trigger onAdd callbacks.TryGetValue(OPERATION.ADD, out var addCallbacks); if (addCallbacks != null) { isTriggering = true; foreach (var callback in addCallbacks) { callback.DynamicInvoke(change.DynamicIndex ?? change.Field, change.Value); } isTriggering = false; } } // trigger onChange if (change.Value != change.PreviousValue) { callbacks.TryGetValue(OPERATION.REPLACE, out var replaceCallbacks); if (replaceCallbacks != null) { foreach (var callback in replaceCallbacks) { callback.DynamicInvoke(change.DynamicIndex ?? change.Field, change.Value); } } } } UniqueRefIds.Add(refId); } } } public class Callbacks // where T : Schema { public static StateCallbackStrategy Get(Room room) where T : Schema { var decoder = (room.Serializer as SchemaSerializer).Decoder; return new StateCallbackStrategy(decoder); } public static StateCallbackStrategy Get(Decoder decoder) where T : Schema { return new StateCallbackStrategy(decoder); } internal static void RemoveChildRefs(ISchemaCollection collection, List changes, ReferenceTracker refs) { if (refs == null) { return; } collection.ForEach((key, value) => { changes.Add(new DataChange { RefId = collection.__refId, Op = (byte)OPERATION.DELETE, //Field = item.Key, DynamicIndex = key, Value = null, PreviousValue = value }); if (collection.HasSchemaChild) { refs.Remove((value as IRef).__refId); } }); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Callbacks/Callbacks.cs.meta ================================================ fileFormatVersion: 2 guid: cdc421113be204ebf86bc7061decb2a5 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Callbacks.meta ================================================ fileFormatVersion: 2 guid: 20021fffe9f50415790a724749f52d37 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Decoder.cs ================================================ using System; using System.Collections.Generic; namespace Colyseus.Schema { public delegate void TriggerChangesDelegate(ref List changes); public class Decoder where T : Schema { public ReferenceTracker Refs = new ReferenceTracker(); public TypeContext Context = new TypeContext(); public T State; public TriggerChangesDelegate TriggerChanges; protected List AllChanges = new List(); /// /// DynamicTypeDefinitions keyed by typeId, populated during Handshake when T is DynamicSchema. /// internal Dictionary DynamicDefinitions; /// /// Maps collection refIds to their child element typeId (for DynamicSchema support). /// private Dictionary _collectionChildTypeId; public Decoder() { State = Activator.CreateInstance(); Refs.Add(0, State); } /// /// Decode incoming data /// /// The incoming data /// used to If null, will create a new one /// /// for all refs found through the decoding process. If null, will /// create a new one /// /// If no decoding fails public void Decode(byte[] bytes, Iterator it = null) { if (it == null) { it = new Iterator(); } int refId = 0; IRef _ref = State; AllChanges.Clear(); int totalBytes = bytes.Length; while (it.Offset < totalBytes) { if (bytes[it.Offset] == (byte)SPEC.SWITCH_TO_STRUCTURE) { it.Offset++; refId = Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); if (_ref is IArraySchema) { ((IArraySchema)_ref).OnDecodeEnd(); } _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); } continue; } bool isSchemaDefinitionMismatch; if (_ref is Schema) { isSchemaDefinitionMismatch = !DecodeSchema(bytes, it, (Schema)_ref); } else if (_ref is IMapSchema) { isSchemaDefinitionMismatch = !DecodeMapSchema(bytes, it, (IMapSchema)_ref); } else { isSchemaDefinitionMismatch = !DecodeArraySchema(bytes, it, (IArraySchema)_ref); } if (isSchemaDefinitionMismatch) { // // keep skipping next bytes until reaches a known structure // by local decoder. // Iterator nextIterator = new Iterator { Offset = it.Offset }; while (it.Offset < totalBytes) { if (Utils.Decode.SwitchStructureCheck(bytes, it)) { nextIterator.Offset = it.Offset + 1; if (Refs.Has(Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, nextIterator)))) { break; } } it.Offset++; } continue; } } if (_ref is IArraySchema) { ((IArraySchema)_ref).OnDecodeEnd(); } TriggerChanges?.Invoke(ref AllChanges); Refs.GarbageCollection(); } protected void DecodeValue(byte[] bytes, Iterator it, IRef _ref, int fieldIndex, string fieldType, System.Type childType, byte operation, out object value, out object previousValue) { previousValue = _ref.GetByIndex(fieldIndex); // // Delete operations // if ((operation & (byte)OPERATION.DELETE) == (byte)OPERATION.DELETE) { // Flag `refId` for garbage collection. if (previousValue != null && previousValue is IRef) { Refs.Remove(((IRef)previousValue).__refId); } if (operation != (byte)OPERATION.DELETE_AND_ADD) { _ref.DeleteByIndex(fieldIndex); } value = null; } if (operation == (byte)OPERATION.DELETE) { // // FIXME: refactor me. // Don't do anything. // value = null; } else if (fieldType == "ref") { var __refId = Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); value = Refs.Get(__refId); if ((operation & (byte)OPERATION.ADD) == (byte)OPERATION.ADD) { float resolvedTypeId; System.Type concreteChildType = GetSchemaType(bytes, it, childType, out resolvedTypeId); if (value == null) { value = CreateTypeInstance(concreteChildType); ((IRef)value).__refId = __refId; } // Assign DynamicTypeDefinition for DynamicSchema instances if (DynamicDefinitions != null && value is DynamicSchema dynamicValue && dynamicValue.Definition == null) { if (resolvedTypeId < 0) { // No TYPE_ID in stream, determine from parent context if (_ref is DynamicSchema parentDs && parentDs.Definition != null && parentDs.Definition.FieldsByIndex.TryGetValue(fieldIndex, out var fn) && parentDs.Definition.FieldReferencedTypes.TryGetValue(fn, out var parentTypeId)) { resolvedTypeId = parentTypeId; } else if (_ref is ISchemaCollection && _collectionChildTypeId != null && _collectionChildTypeId.TryGetValue(_ref.__refId, out var colTypeId)) { resolvedTypeId = colTypeId; } } if (resolvedTypeId >= 0 && DynamicDefinitions.TryGetValue(resolvedTypeId, out var def)) { dynamicValue.Definition = def; } } Refs.Add(__refId, (IRef)value, ( value != previousValue || // increment ref count if value has changed (operation == (byte)OPERATION.DELETE_AND_ADD && value == previousValue) // increment ref count if the same instance is being added again )); } } else if (childType == null) { // primitive values value = Utils.Decode.DecodePrimitiveType(fieldType, bytes, it); } else { var __refId = Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); ISchemaCollection valueRef = Refs.Has(__refId) ? (ISchemaCollection)previousValue ?? (ISchemaCollection)Refs.Get(__refId) : (ISchemaCollection)Activator.CreateInstance(childType); value = valueRef.Clone(); ((ISchemaCollection)value).__refId = __refId; // keep reference to nested childPrimitiveType. string childPrimitiveType; ((Schema)_ref).fieldsByIndex.TryGetValue(fieldIndex, out var fieldName); ((Schema)_ref).fieldChildPrimitiveTypes.TryGetValue(fieldName, out childPrimitiveType); ((ISchemaCollection)value).ChildPrimitiveType = childPrimitiveType; // Track child typeId for DynamicSchema collections if (DynamicDefinitions != null && _ref is DynamicSchema parentDs2 && parentDs2.Definition != null && fieldName != null && parentDs2.Definition.FieldReferencedTypes.TryGetValue(fieldName, out var collChildTypeId)) { if (_collectionChildTypeId == null) _collectionChildTypeId = new Dictionary(); _collectionChildTypeId[__refId] = collChildTypeId; } if (previousValue != null) { if ( ((IRef)previousValue).__refId > 0 && __refId != ((IRef)previousValue).__refId ) { ((ISchemaCollection)previousValue).ForEach((key, value) => { if (value is IRef) { Refs.Remove(((IRef)value).__refId); } AllChanges.Add(new DataChange { RefId = __refId, DynamicIndex = key, Op = (byte)OPERATION.DELETE, Value = null, PreviousValue = value }); }); } } Refs.Add(__refId, (IRef)value, ( valueRef != previousValue || // increment ref count if value has changed (operation == (byte)OPERATION.DELETE_AND_ADD && valueRef == previousValue) // increment ref count if the same instance is being added again )); } } protected bool DecodeSchema(byte[] bytes, Iterator it, Schema refSchema) { byte firstByte = bytes[it.Offset++]; byte operation = (byte) ((firstByte >> 6) << 6); // "compressed" index + operation int fieldIndex = firstByte % (operation == 0 ? 255 : operation); // FIXME: JS allows (0 || 255); refSchema.fieldsByIndex.TryGetValue(fieldIndex, out var fieldName); refSchema.fieldTypes.TryGetValue(fieldName ?? "", out var fieldType); refSchema.fieldChildTypes.TryGetValue(fieldName ?? "", out var childType); if (fieldName == null) { return false; } DecodeValue( bytes, it, refSchema, fieldIndex, fieldType, childType, operation, out var value, out var previousValue ); if (value != null) { refSchema[fieldName] = value; } if (previousValue != value) { AllChanges.Add(new DataChange { RefId = refSchema.__refId, Op = operation, Field = fieldName, Value = value, PreviousValue = previousValue }); } return true; } protected bool DecodeMapSchema (byte[] bytes, Iterator it, IMapSchema refMap) { byte operation = bytes[it.Offset++]; if (operation == (byte)OPERATION.CLEAR) { refMap.Clear(AllChanges, Refs); return true; } int fieldIndex = Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); string fieldType; System.Type childType = null; if (refMap.HasSchemaChild) { fieldType = "ref"; childType = refMap.GetChildType(); } else { fieldType = refMap.ChildPrimitiveType; } string dynamicIndex; if ((operation & (byte)OPERATION.ADD) == (byte)OPERATION.ADD) { // MapSchema dynamic index. dynamicIndex = Utils.Decode.DecodeString(bytes, it); refMap.SetIndex(fieldIndex, dynamicIndex); } else { dynamicIndex = (string)refMap.GetIndex(fieldIndex); } DecodeValue( bytes, it, refMap, fieldIndex, fieldType, childType, operation, out var value, out var previousValue ); if (value != null) { refMap.SetByIndex(fieldIndex, dynamicIndex, value); } if (previousValue != value) { AllChanges.Add(new DataChange { RefId = refMap.__refId, Op = operation, Field = null, DynamicIndex = dynamicIndex, Value = value, PreviousValue = previousValue }); } return true; } protected bool DecodeArraySchema(byte[] bytes, Iterator it, IArraySchema refArray) { byte operation = bytes[it.Offset++]; int index; if (operation == (byte)OPERATION.CLEAR) { refArray.Clear(AllChanges, Refs); return true; } else if (operation == (byte)OPERATION.REVERSE) { refArray.Reverse(); return true; } else if (operation == (byte)OPERATION.DELETE_BY_REFID) { // TODO: refactor here, try to follow same flow as below int refId = Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); object itemByRefId = Refs.Get(refId); int i = 0; index = -1; foreach (var item in refArray.GetItems()) { if (item == itemByRefId) { index = i; break; } i++; } refArray.DeleteByIndex(index); AllChanges.Add(new DataChange { RefId = refArray.__refId, Op = (byte) OPERATION.DELETE, Field = "", DynamicIndex = index, Value = null, PreviousValue = itemByRefId }); return true; } else if (operation == (byte)OPERATION.ADD_BY_REFID) { int refId = Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); IRef itemByRefId = Refs.Get(refId); index = -1; if (itemByRefId != null) { int i = 0; foreach (var item in refArray.GetItems()) { if (item == itemByRefId) { index = i; break; } i++; } } if (index == -1) { index = refArray.Count; } } else { index = Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); } string fieldType; System.Type childType = null; if (refArray.HasSchemaChild) { fieldType = "ref"; childType = refArray.GetChildType(); } else { fieldType = refArray.ChildPrimitiveType; } DecodeValue( bytes, it, refArray, index, fieldType, childType, operation, out var value, out var previousValue ); if (value != null && value != previousValue) { refArray.SetByIndex(index, value, operation); } if (previousValue != value) { AllChanges.Add(new DataChange { RefId = refArray.__refId, Op = operation, Field = null, DynamicIndex = index, Value = value, PreviousValue = previousValue }); } return true; } /// /// Determine what type of this is /// /// Incoming data /// /// The used to the /// /// /// The default type, if one cant be determined from the /// /// /// The parsed if found, if not protected System.Type GetSchemaType(byte[] bytes, Iterator it, System.Type defaultType, out float resolvedTypeId) { System.Type type = defaultType; resolvedTypeId = -1; if (it.Offset < bytes.Length && bytes[it.Offset] == (byte)SPEC.TYPE_ID) { it.Offset++; resolvedTypeId = (float)Convert.ToInt32(Utils.Decode.DecodeNumber(bytes, it)); type = Context.Get(resolvedTypeId); } return type; } /// /// Create an instance of the provided /// /// The to create an instance of /// protected object CreateTypeInstance(System.Type type) { return Activator.CreateInstance(type); } internal void Teardown() { Refs.Clear(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Decoder.cs.meta ================================================ fileFormatVersion: 2 guid: bf9ddf343365d470598eca60f7ccbeb0 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/DynamicSchema.cs ================================================ using System; using System.Collections.Generic; namespace Colyseus.Schema { /// /// Per-type metadata built from server handshake Reflection data. /// Analogous to a generated Schema subclass's [Type] attributes, /// but constructed at runtime. /// public class DynamicTypeDefinition { public float TypeId; public Dictionary FieldsByIndex = new Dictionary(); public Dictionary FieldTypes = new Dictionary(); public Dictionary FieldChildTypes = new Dictionary(); public Dictionary FieldChildPrimitiveTypes = new Dictionary(); public Dictionary FieldReferencedTypes = new Dictionary(); /// /// Parses a ReflectionField into the appropriate dictionaries. /// /// The field index from the reflection data /// The field name /// The field type string (e.g. "ref", "map", "array", "string", "int32") /// The referenced type id, or -1 for primitives public void ParseFieldType(int fieldIndex, string fieldName, string fieldType, float referencedType) { FieldsByIndex[fieldIndex] = fieldName; if (referencedType >= 0) { FieldReferencedTypes[fieldName] = referencedType; } if (fieldType == "ref") { FieldTypes[fieldName] = "ref"; if (referencedType >= 0) { FieldChildTypes[fieldName] = typeof(DynamicSchema); } } else if (fieldType == "map") { FieldTypes[fieldName] = "map"; if (referencedType >= 0) { FieldChildTypes[fieldName] = typeof(MapSchema); } } else if (fieldType == "array") { FieldTypes[fieldName] = "array"; if (referencedType >= 0) { FieldChildTypes[fieldName] = typeof(ArraySchema); } } else if (fieldType.Contains(":")) { // Collection of primitives: "map:string", "array:number", etc. var parts = fieldType.Split(':'); var collectionType = parts[0]; var primitiveType = parts[1]; FieldTypes[fieldName] = collectionType; FieldChildPrimitiveTypes[fieldName] = primitiveType; if (collectionType == "map") { FieldChildTypes[fieldName] = typeof(MapSchema); } else if (collectionType == "array") { FieldChildTypes[fieldName] = typeof(ArraySchema); } } else { // Primitive types: "string", "int32", "float32", "number", "boolean", etc. FieldTypes[fieldName] = fieldType; } } } /// /// A Schema subclass that stores values in a dictionary rather than /// requiring compile-time generated fields. Use with Room<DynamicSchema> /// to skip code generation entirely. /// public class DynamicSchema : Schema { internal DynamicTypeDefinition Definition; private Dictionary _values = new Dictionary(); /// /// Parameterless constructor required for Activator.CreateInstance /// public DynamicSchema() { } public override object this[string propertyName] { get { _values.TryGetValue(propertyName, out var value); return value; } set { _values[propertyName] = value; } } internal override Dictionary fieldsByIndex => Definition?.FieldsByIndex ?? _emptyFieldsByIndex; internal override Dictionary fieldTypes => Definition?.FieldTypes ?? _emptyFieldTypes; internal override Dictionary fieldChildTypes => Definition?.FieldChildTypes ?? _emptyFieldChildTypes; internal override Dictionary fieldChildPrimitiveTypes => Definition?.FieldChildPrimitiveTypes ?? _emptyFieldChildPrimitiveTypes; /// /// Typed convenience accessor for field values. /// public T Get(string fieldName) { _values.TryGetValue(fieldName, out var value); if (value == null) { return default(T); } if (value is T typedValue) { return typedValue; } return (T)Convert.ChangeType(value, typeof(T)); } // Shared empty dictionaries to avoid allocations when Definition is null private static readonly Dictionary _emptyFieldsByIndex = new Dictionary(); private static readonly Dictionary _emptyFieldTypes = new Dictionary(); private static readonly Dictionary _emptyFieldChildTypes = new Dictionary(); private static readonly Dictionary _emptyFieldChildPrimitiveTypes = new Dictionary(); } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/DynamicSchema.cs.meta ================================================ fileFormatVersion: 2 guid: 0c5bf7be5839b48abb887366463f49e5 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/ReferenceTracker.cs ================================================ using System; using System.Collections.Generic; namespace Colyseus.Schema { /// /// Keep track of and maintain multiple objects /// public class ReferenceTracker { /// /// Local list of s we have scheduled for removal in the next /// /// public List deletedRefs = new List(); /// /// Map of how many s we're currently tracking for each ID /// public Dictionary refCounts = new Dictionary(); /// /// Map of s we're tracking /// public Dictionary refs = new Dictionary(); /// /// List of callbacks by refId /// public Dictionary>> callbacks = new Dictionary>>(); /// /// Add a new reference to be tracked /// /// The ID of the reference /// The object we're tracking /// If true, we increment the at this ID public void Add(int refId, IRef _ref, bool incrementCount = true) { refs[refId] = _ref; if (incrementCount) { int previousCount = (!refCounts.ContainsKey(refId)) ? 0 : refCounts[refId]; refCounts[refId] = previousCount + 1; } if (deletedRefs.Contains(refId)) { deletedRefs.Remove(refId); } } /// /// Get a reference by it's ID /// /// The ID of the reference requested /// The reference with that in , if it exists public IRef Get(int refId) { refs.TryGetValue(refId, out var _ref); return _ref; } /// /// Check if contains /// /// The ID to check for /// True if contains , false otherwise public bool Has(int refId) { return refs.ContainsKey(refId); } /// /// Remove a reference by ID /// /// The ID of the reference to remove public bool Remove(int refId) { if (!refCounts.ContainsKey(refId)) { ColyseusContext.Logger.Log("trying to remove refId that doesn't exist: " + refId); return false; } refCounts[refId] = refCounts[refId] - 1; if (refCounts[refId] <= 0) { deletedRefs.Add(refId); return true; } return false; } /// /// Remove all references contained in from the tracking maps ( and /// ). Clears afterwards /// public void GarbageCollection() { int totalDeletedRefs = deletedRefs.Count; for (int i = 0; i < totalDeletedRefs; i++) { int refId = deletedRefs[i]; if (refCounts[refId] <= 0) { IRef _ref = refs[refId]; if (_ref is Schema) { foreach (KeyValuePair field in ((Schema)_ref).GetFieldChildTypes()) { object fieldValue = ((Schema)_ref)[field.Key]; if (fieldValue is IRef) { int childRefId = ((IRef)fieldValue).__refId; if (!deletedRefs.Contains(childRefId) && Remove(childRefId)) { totalDeletedRefs++; } } } } else if (_ref is ISchemaCollection && ((ISchemaCollection)_ref).HasSchemaChild) { ((ISchemaCollection)_ref).ForEach((key, value) => { int childRefId = ((IRef)value).__refId; if (!deletedRefs.Contains(childRefId) && Remove(childRefId)) { totalDeletedRefs++; } }); } refs.Remove(refId); refCounts.Remove(refId); callbacks.Remove(refId); } } deletedRefs.Clear(); } /// /// Clear all maps and lists in this tracker /// public void Clear() { refs.Clear(); refCounts.Clear(); callbacks.Clear(); deletedRefs.Clear(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/ReferenceTracker.cs.meta ================================================ fileFormatVersion: 2 guid: 8d1f13b23a23742dda739d463a4568e2 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Schema.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Reflection; // ReSharper disable InconsistentNaming /*** //Allowed primitive types: // "string" // "number" // "boolean" // "int8" // "uint8" // "int16" // "uint16" // "int32" // "uint32" // "int64" // "uint64" // "float32" // "float64" //Allowed reference types: // "ref" // "array" // "map" ***/ namespace Colyseus.Schema { /// /// wrapper class /// Allowed primitive types: /// /// /// "string", "number", "boolean", "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", /// "float32", "float64" /// /// /// Allowed reference types: /// /// "ref", "array", "map" /// /// [AttributeUsage(AttributeTargets.Field)] public class Type : Attribute { /// /// The of the /// public string ChildPrimitiveType; /// /// What type of this attribute is (can be null) /// public System.Type ChildType; /// /// The field type this represents /// public string FieldType; /// /// The index of where this will be stored in the /// public int Index; public Type(int index, string type, System.Type childType = null, string childPrimitiveType = null) { Index = index; // GetType().GetFields() doesn't guarantee order of fields, need to manually track them here! FieldType = type; ChildType = childType; ChildPrimitiveType = childPrimitiveType; } } /// /// Wrapper class containing an offset value /// public class Iterator { /// /// The value used to offset when we encode/decode data /// public int Offset; } /// /// Byte flags used to signal specific operations to be performed on data /// public enum SPEC : byte { /// /// A decode can be done, begin that process /// SWITCH_TO_STRUCTURE = 255, /// /// The following bytes will indicate the type /// TYPE_ID = 213 } /// /// Byte flags for operations that can be done /// [SuppressMessage("ReSharper", "MissingXmlDoc")] public enum OPERATION : byte { ADD = 128, REPLACE = 0, DELETE = 64, DELETE_AND_MOVE = 96, // ArraySchema DELETE_AND_ADD = 192, CLEAR = 10, /** * ArraySchema operations */ REVERSE = 15, DELETE_BY_REFID = 33, ADD_BY_REFID = 129, } /// /// Wrapper class for a change /// public class DataChange { /// /// The reference id of the data change /// public int RefId; /// /// The field index of the data change /// public object DynamicIndex; /// /// The field name of the data /// public string Field; /// /// An flag for this DataChange /// public byte Op; /// /// The value of the old data /// public object PreviousValue; /// /// The value of the new data /// public object Value; } /// /// Interface for a collection of multiple s /// [SuppressMessage("ReSharper", "MissingXmlDoc")] public interface ISchemaCollection : IRef { bool HasSchemaChild { get; } string ChildPrimitiveType { get; set; } int Count { get; } object this[object key] { get; set; } IEnumerable GetItems(); void ForEach(Action action); void SetItems(object items); void Clear(List changes, ReferenceTracker refs); System.Type GetChildType(); object GetTypeDefaultValue(); ISchemaCollection Clone(); } [SuppressMessage("ReSharper", "MissingXmlDoc")] public interface IArraySchema : ISchemaCollection { void OnDecodeEnd(); void SetByIndex(int index, object value, byte operation); void Reverse(); } [SuppressMessage("ReSharper", "MissingXmlDoc")] public interface IMapSchema : ISchemaCollection { void SetIndex(int index, object dynamicIndex); object GetIndex(int index); void SetByIndex(int index, object dynamicIndex, object value); } /// /// Interface for an object that can be tracked by a /// [SuppressMessage("ReSharper", "MissingXmlDoc")] public interface IRef { /// /// The ID with which this instance will be tracked /// int __refId { get; set; } object GetByIndex(int index); void DeleteByIndex(int index); } /// /// Data structure representing a 's state (synchronizeable data) /// public class Schema : IRef { private static readonly Dictionary _cachedMetadata = new Dictionary(); private readonly Metadata _metadata; /// /// Map of the s that this schema uses /// internal virtual Dictionary fieldChildPrimitiveTypes => _metadata.FieldChildPrimitiveTypes; /// /// Map of the s that this schema uses /// internal virtual Dictionary fieldChildTypes => _metadata.FieldChildTypes; /// /// Map of the fields in this schema using {, /// internal virtual Dictionary fieldsByIndex => _metadata.FieldsByIndex; /// /// Map of the field types in this schema /// internal virtual Dictionary fieldTypes => _metadata.FieldTypes; public Schema() { var type = GetType(); if (!_cachedMetadata.TryGetValue(type, out _metadata)) { _metadata = CreateMetadata(type); _cachedMetadata.Add(type, _metadata); } } private static Metadata CreateMetadata(System.Type type) { var metadata = new Metadata(); FieldInfo[] fields = type.GetFields(); foreach (FieldInfo field in fields) { object[] typeAttributes = field.GetCustomAttributes(typeof(Type), true); for (int i = 0; i < typeAttributes.Length; i++) { Type t = (Type)typeAttributes[i]; metadata.FieldsByIndex.Add(t.Index, field.Name); metadata.FieldTypes.Add(field.Name, t.FieldType); if (t.ChildPrimitiveType != null) { metadata.FieldChildPrimitiveTypes.Add(field.Name, t.ChildPrimitiveType); } if (t.ChildType != null) { metadata.FieldChildTypes.Add(field.Name, t.ChildType); } } } return metadata; } /// /// Allow get and set of property values by its /// /// The object's field name public virtual object this[string propertyName] { get { return GetType().GetField(propertyName).GetValue(this); } set { FieldInfo field = GetType().GetField(propertyName); field.SetValue(this, value); } } /// /// implementation - ID with which to reference this /// public int __refId { get; set; } /// /// Get a field by it's index /// /// Index of the field to get /// The at that index (if it exists) public object GetByIndex(int index) { fieldsByIndex.TryGetValue(index, out var fieldName); return this[fieldName]; } /// /// Remove the field by it's index /// /// Index of the field to remove public void DeleteByIndex(int index) { fieldsByIndex.TryGetValue(index, out var fieldName); this[fieldName] = null; } /// /// Getter function, required for /// /// /// /// internal Dictionary GetFieldChildTypes() { // This is required for "garbage collection" inside ReferenceTracker. return fieldChildTypes; } /// /// Check if this has a child /// /// type to check for /// True if found, false otherwise public static bool CheckSchemaChild(System.Type toCheck) { System.Type generic = typeof(Schema); while (toCheck != null && toCheck != typeof(object)) { System.Type cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; if (generic == cur) { return true; } toCheck = toCheck.BaseType; } return false; } /// /// Metadata for Schema type /// private class Metadata { /// /// Map of the s that this schema uses /// public Dictionary FieldChildPrimitiveTypes = new Dictionary(); /// /// Map of the s that this schema uses /// public Dictionary FieldChildTypes = new Dictionary(); /// /// Map of the fields in this schema using {, /// public Dictionary FieldsByIndex = new Dictionary(); /// /// Map of the field types in this schema /// public Dictionary FieldTypes = new Dictionary(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Schema.cs.meta ================================================ fileFormatVersion: 2 guid: 7e50bf707193ce547b13afb98abb3b48 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/TypeContext.cs ================================================ using System.Collections.Generic; namespace Colyseus.Schema { /// /// Internal container class for tracking and maintaining the context of the current application /// public class TypeContext { /// /// Schema types and IDs we're aware of /// protected Dictionary typeIds = new Dictionary(); protected List types = new List(); //TODO: This does not appear to be used ever /// /// Add a new Schema type by id /// /// The incoming schema type /// The schema type ID we received via server Handshake public void SetTypeId(System.Type type, float typeid) { typeIds[typeid] = type; } /// /// Get the with a given /// /// The schema type ID /// The schema type we previous have set with public System.Type Get(float typeid) { return typeIds[typeid]; //TODO: Confirm if this can ever fail and if so we should handle it gracefully } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/TypeContext.cs.meta ================================================ fileFormatVersion: 2 guid: a1b0877a82fcf4867af631924169de16 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/ArraySchema.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using Colyseus; namespace Colyseus.Schema { /// /// A array of type objects /// /// The type of object in this array public class ArraySchema : IArraySchema { /// /// The contents of the /// public List items; internal HashSet deletedKeys = new HashSet(); [Preserve] public ArraySchema() { items = new List(); } public ArraySchema(List items = null) { this.items = items ?? new List(); } /// /// Accessor to get/set by index /// public T this[int index] { get { return items[index]; } set { items[index] = value; } } /// public int __refId { get; set; } public void SetByIndex(int index, object value, byte operation) { deletedKeys.Remove(index); if ( index == 0 && operation == (byte)OPERATION.ADD && items.Count > 0 ) { // handle decoding unshift items.Insert(0, (T)value); } else if (operation == (byte)OPERATION.DELETE_AND_MOVE) { items.RemoveAt(index); for (int i = items.Count; i <= index; i++) { items.Add(default(T)); } items[index] = (T)value; // items.Insert(index, (T) value); } else { for (int i = items.Count; i <= index; i++) { items.Add(default(T)); } items[index] = (T)value; // items.Insert(index, (T) value); } } /// /// Get an item out of the by it's index /// /// The index of the item /// An object of type if it exists public object GetByIndex(int index) { try { return items[index]; } catch (ArgumentOutOfRangeException) { return null; } } /// /// Remove an item and it's dynamic index reference /// /// The index of the item public void DeleteByIndex(int index) { deletedKeys.Add(index); // skip if index is out of range if (index >= items.Count) { return; } items[index] = default(T); } /// /// Clear all items and indices /// /// Passed in for garbage collection, if needed public void Clear(List changes, ReferenceTracker refs) { Callbacks.RemoveChildRefs(this, changes, refs); items.Clear(); } public void Reverse() { items.Reverse(); } /// /// Clone this /// /// A copy of this public ISchemaCollection Clone() { ArraySchema clone = new ArraySchema(items); return clone; } /// /// Determine what type of item this contains /// /// /// typeof(); /// public System.Type GetChildType() { return typeof(T); } /// /// Get the default value of /// /// /// default(); /// public object GetTypeDefaultValue() { return default(T); } /// /// Getter for /// This calls: Schema.CheckSchemaChild(typeof(T)) /// public bool HasSchemaChild { get; } = Schema.CheckSchemaChild(typeof(T)); /// /// Getter/Setter of the that this /// contains /// public string ChildPrimitiveType { get; set; } /// /// Getter for the amount of in this /// public int Count { get { return items.Count; } } /// /// Accessor to get/set with a /// /// public object this[object key] { get { return this[(int)key]; } set { items[(int)key] = HasSchemaChild ? (T)value : (T)Convert.ChangeType(value, typeof(T)); } } /// /// Getter function to get all the in this /// /// /// /// public IEnumerable GetItems() { return items; } public int IndexOf(T value) { int i = 0; foreach (var item in items) { if (item.Equals(value)) { return i; } i++; } return -1; } /// /// Setter function to cast and set /// /// /// The items to pass to the . Will be cast to Dictionary{int, /// } /// public void SetItems(object items) { this.items = (List)items; } /// /// Function to iterate over and perform an upon each entry /// /// The to perform public void ForEach(Action action) { int i = 0; items.ForEach((value) => { action(i, value); i++; }); } public void ForEach(Action action) { int i = 0; items.ForEach((value) => { action(i, value); i++; }); } public void OnDecodeEnd() { if (deletedKeys.Count == 0) { return; } var newItems = new List(); for (int i = 0; i < items.Count; i++) { if (deletedKeys.Contains(i)) { continue; } newItems.Add(items[i]); } items = newItems; deletedKeys.Clear(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/ArraySchema.cs.meta ================================================ fileFormatVersion: 2 guid: 3ab256d5bbbf81a4591a87d62c22a878 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/CustomType.cs ================================================ using System; using System.Collections.Generic; namespace Colyseus.Schema { class CustomType { protected static CustomType instance = new CustomType(); protected Dictionary types = new Dictionary(); //{ // ["array"] = ArraySchema //}; public static CustomType GetInstance() { return instance; } public void Add(string id, System.Type type) { if (!types.ContainsKey(id)) { types.Add(id, type); } } public System.Type Get(string id) { System.Type type; types.TryGetValue(id, out type); return type; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/CustomType.cs.meta ================================================ fileFormatVersion: 2 guid: 8797edbee9b8f41e0ad98a6c35a02c43 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/MapSchema.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; namespace Colyseus.Schema { /// /// A dictionary of type objects /// /// The type of object in this map public class MapSchema : IMapSchema { protected Dictionary Indexes = new Dictionary(); /// /// The contents of the /// public OrderedDictionary items = new OrderedDictionary(); [Preserve] public MapSchema() { items = new OrderedDictionary(); } public MapSchema(OrderedDictionary items = null) { this.items = items ?? new OrderedDictionary(); } /// /// Accessor to get/set with a /// /// public T this[string key] { get { T value; TryGetValue(key, out value); return value; } set { items[key] = value; } } /// /// Getter for all of the keys in /// public ICollection Keys { get { return items.Keys; } } /// /// Getter for all of the values in /// public ICollection Values { get { return items.Values; } } public int __refId { get; set; } /// /// Set the value /// /// The field index /// The new dynamic Index value, cast to public void SetIndex(int index, object dynamicIndex) { Indexes[index] = (string) dynamicIndex; } /// /// Set an Item by it's /// /// /// Sets value at to /// /// /// The index, cast to , in that will be set to /// /// The new object to put into public void SetByIndex(int index, object dynamicIndex, object value) { Indexes[index] = (string) dynamicIndex; items[dynamicIndex] = (T) value; } /// /// Get the dynamic index value from /// /// The location of the dynamic index to return /// The dynamic index from , if it exists. if it does not public object GetIndex(int index) { string dynamicIndex; Indexes.TryGetValue(index, out dynamicIndex); return dynamicIndex; } /// /// Get an item out of the by it's index /// /// The index of the item /// An object of type if it exists public object GetByIndex(int index) { string dynamicIndex = (string) GetIndex(index); return dynamicIndex != null && items.Contains(dynamicIndex) ? items[dynamicIndex] : GetTypeDefaultValue(); } /// /// Remove an item and it's dynamic index reference /// /// The index of the item public void DeleteByIndex(int index) { string dynamicIndex = (string) GetIndex(index); if ( // // FIXME: // The schema encoder should not encode a DELETE operation when using ADD + DELETE in the same key. (in the same patch) // dynamicIndex != null && items.Contains(dynamicIndex) ) { items.Remove(dynamicIndex); Indexes.Remove(index); } } /// /// Clone this /// /// A copy of this public ISchemaCollection Clone() { MapSchema clone = new MapSchema(items) { Indexes = Indexes }; return clone; } /// /// Determine what type of item this contains /// /// /// typeof(); /// public System.Type GetChildType() { return typeof(T); } /// /// Get the default value of /// /// /// default(); /// public object GetTypeDefaultValue() { return default(T); } /// /// Determine if this contains /// /// The key in that will be checked for /// True if contains the , false if not public bool ContainsKey(object key) { return items.Contains(key); } /// /// Getter for /// This calls: Schema.CheckSchemaChild(typeof(T)) /// public bool HasSchemaChild { get; } = Schema.CheckSchemaChild(typeof(T)); /// /// Getter/Setter of the that this /// contains /// public string ChildPrimitiveType { get; set; } /// /// Accessor to get/set with a /// /// public object this[object key] { get { return this[(string) key]; } set { items[(string) key] = HasSchemaChild ? (T) value : (T) Convert.ChangeType(value, typeof(T)); } } /// /// Getter function to get all the in this /// /// /// /// public IEnumerable GetItems() { return items; } /// /// Clear all items and indices /// /// Passed in for garbage collection, if needed public void Clear(List changes, ReferenceTracker refs) { Callbacks.RemoveChildRefs(this, changes, refs); Indexes.Clear(); items.Clear(); } public void Reverse() { throw new NotImplementedException(); } /// /// Getter for the amount of in this /// public int Count { get { return items.Count; } } /// /// UNIMPLEMENTED setter function to set /// /// /// The items to pass to the /// public void SetItems(object items) //TODO: Is it ok if this is unimplemented? { throw new NotImplementedException(); } /// /// Add a new item into /// /// The new entry for public void Add(KeyValuePair item) { items[item.Key] = item.Value; } /// /// Check if an exists in /// /// The value to check for /// True if is found in , false otherwise public bool Contains(KeyValuePair item) { return items.Contains(item.Key); } /// /// Remove an item from this 's /// /// The item to remove /// /// True if was found and removed, false if it was not in the to /// begin with /// public bool Remove(KeyValuePair item) { T value; if (TryGetValue(item.Key, out value) && Equals(value, item.Value)) { Remove(item.Key); return true; } return false; } /// /// Add a new item to /// /// The field name /// The data to add public void Add(string key, T value) { items.Add(key, value); } public bool Remove(string key) { bool result = items.Contains(key); if (result) { items.Remove(key); } return result; } public bool TryGetValue(string key, out T value) { object foundValue; if ((foundValue = items[key]) != null || items.Contains(key)) { // Either found with a non-null value, or contained value is null. value = (T) foundValue; return true; } value = default; return false; } /// /// Function to iterate over and perform an upon each entry /// /// The to perform public void ForEach(Action action) { foreach (DictionaryEntry item in items) { action((string) item.Key, (T) item.Value); } } public void ForEach(Action action) { foreach (DictionaryEntry item in items) { action(item.Key, item.Value); } } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/MapSchema.cs.meta ================================================ fileFormatVersion: 2 guid: ef32bb4c29e82a54d8c9ae61fbba9b56 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/Reflection.cs ================================================ using Colyseus; namespace Colyseus.Schema { /// /// used for the purposes of reflection /// [Preserve] public class ReflectionField : Schema { /// /// The field name /// [Type(0, "string")] public string name; /// /// The type of the field /// [Type(1, "string")] public string type; [Type(2, "number")] public float referencedType = -1; } /// /// Mid level reflection container of an /// [Preserve] public class ReflectionType : Schema { /// /// The ID of this /// [Type(0, "number")] public float id; /// /// The ID of which structure this extends /// [Type(1, "number")] public float extendsId = -1; /// /// An of /// [Preserve] [Type(2, "array", typeof(ArraySchema))] public ArraySchema fields; [Preserve] public ReflectionType() {} } /// /// Top level reflection container for an /// [Preserve] public class Reflection : Schema { /// /// An of /// [Preserve] [Type(0, "array", typeof(ArraySchema))] public ArraySchema types; [Type(1, "number")] public float rootType = -1; } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types/Reflection.cs.meta ================================================ fileFormatVersion: 2 guid: 65dac8d9cc95b40899c31429819d6949 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Types.meta ================================================ fileFormatVersion: 2 guid: 917dd28b10759a045b773955c84601bf folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Utils/Decode.cs ================================================ using System; using System.Text; using MiscUtil.Conversion; namespace Colyseus.Schema.Utils { public class Decode { public static LittleEndianBitConverter bitConverter = new LittleEndianBitConverter(); /// /// Decodes incoming data into an based off of the provided /// /// What type of we expect this data to be. /// Will determine the Decode method used /// /// The incoming data /// The iterator who's will be used to Decode the data /// A decoded that has been decoded with a specified method public static object DecodePrimitiveType(string type, byte[] bytes, Iterator it) { if (type == "string") { return DecodeString(bytes, it); } if (type == "number") { return DecodeNumber(bytes, it); } if (type == "int8") { return DecodeInt8(bytes, it); } if (type == "uint8") { return DecodeUint8(bytes, it); } if (type == "int16") { return DecodeInt16(bytes, it); } if (type == "uint16") { return DecodeUint16(bytes, it); } if (type == "int32") { return DecodeInt32(bytes, it); } if (type == "uint32") { return DecodeUint32(bytes, it); } if (type == "int64") { return DecodeInt64(bytes, it); } if (type == "uint64") { return DecodeUint64(bytes, it); } if (type == "float32") { return DecodeFloat32(bytes, it); } if (type == "float64") { return DecodeFloat64(bytes, it); } if (type == "boolean") { return DecodeBoolean(bytes, it); } return null; } /// /// Decode method to decode into a /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a public static float DecodeNumber(byte[] bytes, Iterator it) { byte prefix = bytes[it.Offset++]; if (prefix < 0x80) { // positive fixint return prefix; } if (prefix == 0xca) { // float 32 return DecodeFloat32(bytes, it); } if (prefix == 0xcb) { // float 64 return (float)DecodeFloat64(bytes, it); } if (prefix == 0xcc) { // uint 8 return DecodeUint8(bytes, it); } if (prefix == 0xcd) { // uint 16 return DecodeUint16(bytes, it); } if (prefix == 0xce) { // uint 32 return DecodeUint32(bytes, it); } if (prefix == 0xcf) { // uint 64 return DecodeUint64(bytes, it); } if (prefix == 0xd0) { // int 8 return DecodeInt8(bytes, it); } if (prefix == 0xd1) { // int 16 return DecodeInt16(bytes, it); } if (prefix == 0xd2) { // int 32 return DecodeInt32(bytes, it); } if (prefix == 0xd3) { // int 64 return DecodeInt64(bytes, it); } if (prefix > 0xdf) { // negative fixint return (0xff - prefix + 1) * -1; } return float.NaN; } /// /// Decode method to decode into an 8-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into an 8-bit public static sbyte DecodeInt8(byte[] bytes, Iterator it) { return Convert.ToSByte((DecodeUint8(bytes, it) << 24) >> 24); } /// /// Decode method to decode into an 8-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into an 8-bit public static byte DecodeUint8(byte[] bytes, Iterator it) { return bytes[it.Offset++]; } /// /// Decode method to decode into a 16-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 16-bit public static short DecodeInt16(byte[] bytes, Iterator it) { short value = bitConverter.ToInt16(bytes, it.Offset); it.Offset += 2; return value; } /// /// Decode method to decode into a 16-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 16-bit public static ushort DecodeUint16(byte[] bytes, Iterator it) { ushort value = bitConverter.ToUInt16(bytes, it.Offset); it.Offset += 2; return value; } /// /// Decode method to decode into a 32-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 32-bit public static int DecodeInt32(byte[] bytes, Iterator it) { int value = bitConverter.ToInt32(bytes, it.Offset); it.Offset += 4; return value; } /// /// Decode method to decode into a 32-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 32-bit public static uint DecodeUint32(byte[] bytes, Iterator it) { uint value = bitConverter.ToUInt32(bytes, it.Offset); it.Offset += 4; return value; } /// /// Decode method to decode into a 32-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 32-bit public static float DecodeFloat32(byte[] bytes, Iterator it) { float value = bitConverter.ToSingle(bytes, it.Offset); it.Offset += 4; return value; } /// /// Decode method to decode into a 64-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 64-bit public static double DecodeFloat64(byte[] bytes, Iterator it) { double value = bitConverter.ToDouble(bytes, it.Offset); it.Offset += 8; return value; } /// /// Decode method to decode into a 64-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 64-bit public static long DecodeInt64(byte[] bytes, Iterator it) { long value = bitConverter.ToInt64(bytes, it.Offset); it.Offset += 8; return value; } /// /// Decode method to decode into a 64-bit /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a 64-bit public static ulong DecodeUint64(byte[] bytes, Iterator it) { ulong value = bitConverter.ToUInt64(bytes, it.Offset); it.Offset += 8; return value; } /// /// Decode method to decode into a /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a public static bool DecodeBoolean(byte[] bytes, Iterator it) { return DecodeUint8(bytes, it) > 0; } /// /// Decode method to decode into a /// /// The incoming data /// The iterator who's will be used to Decode the data /// decoded into a public static string DecodeString(byte[] bytes, Iterator it) { int prefix = bytes[it.Offset++]; int length; if (prefix < 0xc0) { // fixstr length = prefix & 0x1f; } else if (prefix == 0xd9) { length = (int)DecodeUint8(bytes, it); } else if (prefix == 0xda) { length = DecodeUint16(bytes, it); } else if (prefix == 0xdb) { length = (int)DecodeUint32(bytes, it); } else { length = 0; } string str = Encoding.UTF8.GetString(bytes, it.Offset, length); it.Offset += length; return str; } /// /// Checks if /// bytes[it.Offset] == (byte)SPEC.SWITCH_TO_STRUCTURE /// /// The incoming data /// The iterator who's will be used to Decode the data /// /// True if the current works with this array of , false /// otherwise /// public static bool SwitchStructureCheck(byte[] bytes, Iterator it) { return bytes[it.Offset] == (byte)SPEC.SWITCH_TO_STRUCTURE; } /// /// Checks if the incoming is a number /// /// The incoming data /// The iterator who's will be used to Decode the data /// True if can be resolved into a number, false otherwise public static bool NumberCheck(byte[] bytes, Iterator it) { byte prefix = bytes[it.Offset]; return prefix < 0x80 || prefix >= 0xca && prefix <= 0xd3; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Utils/Decode.cs.meta ================================================ fileFormatVersion: 2 guid: dd65c771c1d0b44399010708ca01d75d ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Utils/Encode.cs ================================================ using System; namespace Colyseus.Schema.Utils { public class Encode { /// /// Retrieves the initial bytes from based on it's length /// /// The incoming "type" encoded to a [] /// The important bytes we need based upon the incoming type /// public static byte[] getInitialBytesFromEncodedType(byte[] encodedType, byte protocol) { byte[] initialBytes = { protocol }; if (encodedType.Length < 0x20) { initialBytes = addByteToArray(initialBytes, new[] { (byte)(encodedType.Length | 0xa0) }); } else if (encodedType.Length < 0x100) { initialBytes = addByteToArray(initialBytes, new byte[] { 0xd9 }); initialBytes = uint8(initialBytes, encodedType.Length); } else if (encodedType.Length < 0x10000) { initialBytes = addByteToArray(initialBytes, new byte[] { 0xda }); initialBytes = uint16(initialBytes, encodedType.Length); } else if (encodedType.Length < 0x7fffffff) { initialBytes = addByteToArray(initialBytes, new byte[] { 0xdb }); initialBytes = uint32(initialBytes, encodedType.Length); } else { throw new Exception("String too long"); } return initialBytes; } private static byte[] addByteToArray(byte[] byteArray, byte[] newBytes) { byte[] bytes = new byte[byteArray.Length + newBytes.Length]; Buffer.BlockCopy(byteArray, 0, bytes, 0, byteArray.Length); Buffer.BlockCopy(newBytes, 0, bytes, byteArray.Length, newBytes.Length); return bytes; } private static byte[] uint8(byte[] bytes, int value) { return addByteToArray(bytes, new[] { (byte)(value & 255) }); } private static byte[] uint16(byte[] bytes, int value) { byte[] a1 = addByteToArray(bytes, new[] { (byte)(value & 255) }); return addByteToArray(a1, new[] { (byte)((value >> 8) & 255) }); } private static byte[] uint32(byte[] bytes, int value) { int b4 = value >> 24; int b3 = value >> 16; int b2 = value >> 8; int b1 = value; byte[] a1 = addByteToArray(bytes, new[] { (byte)(b1 & 255) }); byte[] a2 = addByteToArray(a1, new[] { (byte)(b2 & 255) }); byte[] a3 = addByteToArray(a2, new[] { (byte)(b3 & 255) }); return addByteToArray(a3, new[] { (byte)(b4 & 255) }); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Utils/Encode.cs.meta ================================================ fileFormatVersion: 2 guid: 8b9a0de6846c54eec8dcc45f10ed3b1c ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema/Utils.meta ================================================ fileFormatVersion: 2 guid: bb2c980af108447b0ae4b3af69848342 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Schema.meta ================================================ fileFormatVersion: 2 guid: 0bddc8a03e686dc43bea0c9ceec6d774 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/SchemaSerializer.cs ================================================ using System; using System.Reflection; using System.Collections.Generic; using Colyseus.Schema; using Type = Colyseus.Schema.Type; namespace Colyseus { /// /// An instance of ISerializer specifically for based serialization /// /// A child of public class SchemaSerializer : ISerializer where T : Schema.Schema { public Decoder Decoder = new Decoder(); /// /// A reference to the /// protected Iterator It = new Iterator(); /// public void SetState(byte[] data, int offset = 0) { It.Offset = offset; Decoder.Decode(data, It); } /// public T GetState() { return Decoder.State; } /// public void Patch(byte[] data, int offset = 0) { It.Offset = offset; Decoder.Decode(data, It); } /// public void Teardown() { // Clear all stored references. Decoder.Teardown(); } /// public void Handshake(byte[] bytes, int offset) { Iterator it = new Iterator { Offset = offset }; var reflectionDecoder = new Decoder(); reflectionDecoder.Decode(bytes, it); var reflection = reflectionDecoder.State; var types = reflection.types.items.ToArray(); if (typeof(T) == typeof(DynamicSchema)) { HandshakeDynamic(reflection, types); return; } System.Type targetType = typeof(T); System.Type[] allTypes = targetType.Assembly.GetTypes(); List namespaceSchemaTypes = new List(Array.FindAll(allTypes, t => t.Namespace == targetType.Namespace && typeof(Schema.Schema).IsAssignableFrom( targetType))); for (int i = 0; i < reflection.types.Count; i++) { var reflectionType = reflection.types[i]; var reflectionFields = GetFieldsFromType(reflectionType, types); var schemaType = namespaceSchemaTypes.Find(t => CompareTypes(t, reflectionFields)); if (schemaType != null) { Decoder.Context.SetTypeId(schemaType, reflection.types[i].id); // Remove from list to avoid duplicate checks namespaceSchemaTypes.Remove(schemaType); } else { ColyseusContext.Logger.LogWarning( "Local schema mismatch from server. Use \"schema-codegen\" to generate up-to-date local definitions."); } } } private void HandshakeDynamic(Reflection reflection, ReflectionType[] types) { Decoder.DynamicDefinitions = new Dictionary(); for (int i = 0; i < reflection.types.Count; i++) { var reflectionType = reflection.types[i]; var reflectionFields = GetFieldsFromType(reflectionType, types); var definition = new DynamicTypeDefinition(); definition.TypeId = reflectionType.id; for (int j = 0; j < reflectionFields.Count; j++) { var field = reflectionFields[j]; definition.ParseFieldType(j, field.name, field.type, field.referencedType); } Decoder.DynamicDefinitions[reflectionType.id] = definition; Decoder.Context.SetTypeId(typeof(DynamicSchema), reflectionType.id); } // Assign root definition var rootTypeId = reflection.rootType >= 0 ? reflection.rootType : (reflection.types.Count > 0 ? reflection.types[0].id : -1); if (rootTypeId >= 0) { var rootState = Decoder.State as DynamicSchema; if (rootState != null && Decoder.DynamicDefinitions.TryGetValue(rootTypeId, out var rootDef)) { rootState.Definition = rootDef; } } } private static string DebugReflectionType(ReflectionType reflectionType, List reflectionFields) { List fieldNames = new List(); for (int i = 0; i < reflectionFields.Count; i++) { fieldNames.Add(reflectionFields[i].name); } return $"TypeId: {reflectionType.id} (extendsId: {reflectionType.extendsId}), Fields: {string.Join(", ", fieldNames)}"; } private static bool CompareTypes(System.Type schemaType, List reflectionFields) { FieldInfo[] fields = schemaType.GetFields(); int typedFieldCount = 0; foreach (FieldInfo field in fields) { object[] typeAttributes = field.GetCustomAttributes(typeof(Type), true); if (typeAttributes.Length != 1) { continue; } Type typedField = (Type)typeAttributes[0]; // Skip if reflectionType doesn't have the field if (typedField.Index >= reflectionFields.Count) { return false; } ReflectionField reflectionField = reflectionFields[typedField.Index]; if ( reflectionField.type.IndexOf(typedField.FieldType) != 0 || reflectionField.name != field.Name ) { return false; } typedFieldCount++; } // skip if number of Type'd fields doesn't match if (typedFieldCount != reflectionFields.Count) { return false; } return true; } private List GetFieldsFromType(ReflectionType reflectionType, ReflectionType[] types) { var reflectionFields = new List(); // Find all types in the inheritance chain from child to root List inheritanceChain = new List(); var extendsId = reflectionType.id; while (extendsId != -1) { var currentType = Array.Find(types, t => t.id == extendsId); inheritanceChain.Insert(0, currentType); // Insert at the beginning to reverse order extendsId = currentType.extendsId; } // Collect fields from each type in the chain, from root to child foreach (var type in inheritanceChain) { type.fields.ForEach((_, field) => reflectionFields.Add(field)); } return reflectionFields; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/SchemaSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: c2a698bbb71fc4545a17aed07000db46 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Serializer.cs ================================================ namespace Colyseus { /// /// Serializer Interface /// /// The type of state this serializer will be for public interface ISerializer { /// /// Set the serializer's state /// /// The incoming state data /// Offset for reading the incoming data void SetState(byte[] data, int offset); /// /// Get the current state /// /// An object of type representing the current state T GetState(); /// /// Apply a patch to this state /// /// The incoming state data /// Offset for reading the incoming data void Patch(byte[] data, int offset); /// /// Clean-up functionality /// void Teardown(); /// /// Confirms connection and serialization is working properly /// /// The handshake data to serialize /// Offset for reading the incoming data void Handshake(byte[] bytes, int offset); } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer/Serializer.cs.meta ================================================ fileFormatVersion: 2 guid: 4fcf3a67477874e14b7685c3d289d252 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Serializer.meta ================================================ fileFormatVersion: 2 guid: 6a0c82fa6f9124a47a4f2b068619c7d3 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Settings/Settings.cs ================================================ using System; using System.Collections.Generic; #if UNITY_5_3_OR_NEWER using UnityEngine; #endif namespace Colyseus { /// /// Contains relevant Colyseus settings /// #if UNITY_5_3_OR_NEWER [CreateAssetMenu(fileName = "MyServerSettings", menuName = "Colyseus/Server Settings Scriptable Object", order = 1)] [Serializable] public class Settings : ScriptableObject #else [Serializable] public class Settings #endif { /// /// The server address /// public string colyseusServerAddress = "localhost"; /// /// The port to connect to /// public string colyseusServerPort = "2567"; /// /// If true, we use secure protocols (wss, https) otherwise we use ws, http (based on ) /// public bool useSecureProtocol = false; /// /// Internal wrapper class for a Request header /// [Serializable] public class RequestHeader { public string name; public string value; } #if UNITY_5_3_OR_NEWER [SerializeField] #endif private RequestHeader[] _requestHeaders; private Dictionary _headersDictionary; public void SetRequestHeaders(RequestHeader[] headers) { _requestHeaders = new RequestHeader[headers.Length]; for (int i = 0; i < headers.Length; i++) { _requestHeaders[i] = headers[i]; } } public RequestHeader[] GetRequestHeaders() { return _requestHeaders; } /// /// Convert the user-defined into a dictionary /// public Dictionary Headers { get { if (_headersDictionary == null) { _headersDictionary = new Dictionary(); for (int i = 0; _requestHeaders != null && i < _requestHeaders.Length; ++i) { _headersDictionary.Add(_requestHeaders[i].name, _requestHeaders[i].value); } } return _headersDictionary; } } /// /// Centralized location to build and return the WebSocketEndpoint /// public string WebSocketEndpoint { get { return BuildWebSocketEndpoint(); } } /// /// Centralized location to build and return an WebRequestEndpoint /// public string WebRequestEndpoint { get { return BuildWebRequestEndpoint(); } } public static Settings Create() { #if UNITY_5_3_OR_NEWER return CreateInstance(); #else return new Settings(); #endif } /// /// Create a copy of the provided object /// /// The settings to copy /// A new instance of with values copied from the provided object public static Settings Clone(Settings orig) { Settings clone = Create(); clone.colyseusServerAddress = orig.colyseusServerAddress; clone.colyseusServerPort = orig.colyseusServerPort; clone.useSecureProtocol = orig.useSecureProtocol; clone.SetRequestHeaders(orig.GetRequestHeaders()); return clone; } private string BuildWebRequestEndpoint() { UriBuilder webRequestEndpointBuilder = new UriBuilder(GetBaseEndpoint(GetWebRequestEndpointScheme())); webRequestEndpointBuilder.Port = GetPort(); return webRequestEndpointBuilder.ToString(); } private string BuildWebSocketEndpoint() { UriBuilder webSocketEndpointBuilder = new UriBuilder(GetBaseEndpoint(GetWebSocketEndpointScheme())); webSocketEndpointBuilder.Port = GetPort(); return webSocketEndpointBuilder.ToString(); } private string GetBaseEndpoint(string scheme) { return $"{scheme}://{colyseusServerAddress}"; } public string GetWebSocketEndpointScheme() { return (useSecureProtocol ? "wss" : "ws"); } public string GetWebRequestEndpointScheme() { return (useSecureProtocol ? "https" : "http"); } public int GetPort() { if (ShouldIncludeServerPort() && int.TryParse(colyseusServerPort, out int serverPort)) { return serverPort; } else { return -1; } } private bool ShouldIncludeServerPort() { return !string.IsNullOrEmpty(colyseusServerPort) && !string.Equals(colyseusServerPort, "80") && !string.Equals(colyseusServerPort, "443"); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Settings/Settings.cs.meta ================================================ fileFormatVersion: 2 guid: 6ed729989615d4b4db072e1e8c41076c MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Settings.meta ================================================ fileFormatVersion: 2 guid: 8fbeaff66f46edb4998d9d07492e454c folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Transport/WebSocket.cs ================================================ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Colyseus { public class WebSocketTransport { public event WebSocketOpenEventHandler OnOpen; public event WebSocketMessageEventHandler OnMessage; public event WebSocketErrorEventHandler OnError; public event WebSocketCloseEventHandler OnClose; private readonly object _dispatchLock = new object(); private NativeWebSocket.WebSocket _ws; private bool _usesSharedDispatchLoop; internal static bool ShouldUseSharedDispatchLoop(SynchronizationContext synchronizationContext) { return ColyseusContext.RegisterWebSocketForDispatch == null && synchronizationContext == null; } public async Task Connect(string url, Dictionary headers) { var synchronizationContext = SynchronizationContext.Current; var socket = new NativeWebSocket.WebSocket(url, headers); _ws = socket; _usesSharedDispatchLoop = false; socket.OnOpen += () => OnOpen?.Invoke(); socket.OnMessage += (data) => OnMessage?.Invoke(data); socket.OnError += (msg) => OnError?.Invoke(msg); socket.OnClose += (code) => { ColyseusContext.UnregisterWebSocketForDispatch?.Invoke(socket); #if !UNITY_WEBGL || UNITY_EDITOR lock (_dispatchLock) { _usesSharedDispatchLoop = false; } WebSocketDispatchLoop.Unregister(this); #endif OnClose?.Invoke((int)code); }; if (ColyseusContext.RegisterWebSocketForDispatch != null) { // External dispatcher (MonoGame, custom engine loop, etc.) handles DispatchMessageQueue ColyseusContext.RegisterWebSocketForDispatch(socket); } #if !UNITY_WEBGL || UNITY_EDITOR else if (ShouldUseSharedDispatchLoop(synchronizationContext)) { _usesSharedDispatchLoop = true; WebSocketDispatchLoop.Register(this); } #endif await socket.Connect(); } public Task Send(byte[] data) => _ws.Send(data); public Task Close() => _ws.Close(); public void CancelConnection() => _ws.CancelConnection(); #if !UNITY_WEBGL || UNITY_EDITOR public void DispatchMessageQueue() { lock (_dispatchLock) { if (_usesSharedDispatchLoop) { _usesSharedDispatchLoop = false; WebSocketDispatchLoop.Unregister(this); } _ws?.DispatchMessageQueue(); } } internal void DispatchMessageQueueFromSharedLoop() { lock (_dispatchLock) { if (!_usesSharedDispatchLoop) { return; } _ws?.DispatchMessageQueue(); } } #endif } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Transport/WebSocket.cs.meta ================================================ fileFormatVersion: 2 guid: fc5122bd7006f4974b636c76af201f44 ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Transport/WebSocketDispatchLoop.cs ================================================ #if !UNITY_WEBGL || UNITY_EDITOR using System; using System.Collections.Generic; using System.Threading; namespace Colyseus { internal static class WebSocketDispatchLoop { private const int PollIntervalMilliseconds = 15; private static readonly object SyncRoot = new object(); private static readonly HashSet Sockets = new HashSet(); private static readonly AutoResetEvent WakeUp = new AutoResetEvent(false); private static Thread _dispatchThread; public static void Register(WebSocketTransport socket) { if (socket == null) { return; } lock (SyncRoot) { if (!Sockets.Add(socket)) { return; } EnsureThread(); } WakeUp.Set(); } public static void Unregister(WebSocketTransport socket) { if (socket == null) { return; } lock (SyncRoot) { Sockets.Remove(socket); } WakeUp.Set(); } private static void EnsureThread() { if (_dispatchThread != null) { return; } _dispatchThread = new Thread(DispatchLoop) { IsBackground = true, Name = "Colyseus.WebSocketDispatch" }; _dispatchThread.Start(); } private static void DispatchLoop() { while (true) { WebSocketTransport[] sockets; lock (SyncRoot) { if (Sockets.Count == 0) { sockets = null; } else { sockets = new WebSocketTransport[Sockets.Count]; Sockets.CopyTo(sockets); } } if (sockets == null) { WakeUp.WaitOne(); continue; } for (var i = 0; i < sockets.Length; i++) { try { sockets[i].DispatchMessageQueueFromSharedLoop(); } catch (Exception e) { ColyseusContext.Logger?.LogError($"DispatchMessageQueue error: {e.Message}"); } } WakeUp.WaitOne(PollIntervalMilliseconds); } } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Transport/WebSocketDispatchLoop.cs.meta ================================================ fileFormatVersion: 2 guid: 368242dd6ccd74f16a7436c1c53f73ff ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Transport.meta ================================================ fileFormatVersion: 2 guid: 2fc81b6cb1f8d4749a81f71f7b5a8d03 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/Exceptions.cs ================================================ using System; namespace Colyseus { /// /// Custom exception thrown when there is an issue with Match Making /// public class MatchMakeException : Exception { /// /// The error code the server returned /// public int Code; public MatchMakeException(int code, string message) : base(message) { Code = code; } } public class HttpException : Exception { /// /// The error code the server returned /// public int StatusCode; public HttpException(int statusCode, string message) : base(message) { StatusCode = statusCode; } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/Exceptions.cs.meta ================================================ fileFormatVersion: 2 guid: ca772085cc5044b368171964c8140999 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/ExtensionMethods.cs ================================================ using System; using System.Runtime.CompilerServices; using UnityEngine; using UnityEngine.Networking; namespace Colyseus { /// /// Provides extension for Unity specific methods /// public static class ColyseusExtensionMethods { /// /// Returns our custom instead of a standard /// /// /// An instance of public static UnityWebRequestAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOp) { return new UnityWebRequestAwaiter(asyncOp); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/ExtensionMethods.cs.meta ================================================ fileFormatVersion: 2 guid: 49ca7a47d6d774afa802f5b302cd2f9f MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/ObjectExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Colyseus { /// /// Extension methods for handling and functionality /// public static class ColyseusObjectExtensions { /// /// Convert an to an of type /// /// The dictionary to be converted /// The type of we will convert this into /// An of type public static T ToObject(this IDictionary source) where T : class, new() { T someObject = new T(); Type someObjectType = someObject.GetType(); foreach (KeyValuePair item in source) { someObjectType .GetProperty(item.Key) ?.SetValue(someObject, item.Value, null); } return someObject; } /// /// Convert an into a /// /// The to be converted /// The to use on this /// An version of the public static IDictionary AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) { return source.GetType().GetProperties(bindingAttr).ToDictionary ( propInfo => propInfo.Name, propInfo => propInfo.GetValue(source, null) ); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/ObjectExtensions.cs.meta ================================================ fileFormatVersion: 2 guid: 04fc7c8d440e04bc6afbdd8e8ceaab85 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/UnityWebRequestAwaiter.cs ================================================ using System; using System.Runtime.CompilerServices; using UnityEngine; using UnityEngine.Networking; namespace Colyseus { /// /// A custom class that awaits the completion of a and then performs an /// upon completion /// public class UnityWebRequestAwaiter : INotifyCompletion { private readonly UnityWebRequestAsyncOperation _asyncOp; private Action _continuation; public UnityWebRequestAwaiter(UnityWebRequestAsyncOperation asyncOp) { _asyncOp = asyncOp; asyncOp.completed += OnRequestCompleted; } /// /// Public getter to determine if the is completed /// public bool IsCompleted { get { return _asyncOp.isDone; } } /// /// Provide an action to be invoked when the s completed /// /// The action to perform when the request is completed public void OnCompleted(Action continuation) { _continuation = continuation; } /// /// Satifies the requirement, but currently unused /// public void GetResult() { } private void OnRequestCompleted(AsyncOperation obj) { _continuation?.Invoke(); } } } ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils/UnityWebRequestAwaiter.cs.meta ================================================ fileFormatVersion: 2 guid: eeaf5a5ece0b541c5af42f9c73275e1c MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus/Utils.meta ================================================ fileFormatVersion: 2 guid: 9542be54dba5d9244aed15299f49540c folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Colyseus.meta ================================================ fileFormatVersion: 2 guid: 1a26a17010d5aea49b8a4f09f5801b05 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/ColyseusSDK.asmdef ================================================ { "name": "ColyseusSDK", "references": [ "colyseus.nativewebsocket" ] } ================================================ FILE: Assets/Colyseus/Runtime/ColyseusSDK.asmdef.meta ================================================ fileFormatVersion: 2 guid: 7d8788162c0190c4c8b801543ada1607 AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Editor Default Resources/.gitkeep ================================================ ================================================ FILE: Assets/Colyseus/Runtime/Editor Default Resources/Icons/ColyseusSettings.png.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://c6t3i2w508an0" path="res://.godot/imported/ColyseusSettings.png-b62bac3a69932db7348b37a35909416f.ctex" metadata={ "vram_texture": false } [deps] source_file="res://ColyseusSDK/Editor Default Resources/Icons/ColyseusSettings.png" dest_files=["res://.godot/imported/ColyseusSettings.png-b62bac3a69932db7348b37a35909416f.ctex"] [params] compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/channel_remap/red=0 process/channel_remap/green=1 process/channel_remap/blue=2 process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 ================================================ FILE: Assets/Colyseus/Runtime/Editor Default Resources/Icons/ColyseusSettings.png.import.meta ================================================ fileFormatVersion: 2 guid: 40daac54efc2241099dc969333714022 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Editor Default Resources/Icons/ColyseusSettings.png.meta ================================================ fileFormatVersion: 2 guid: 0d890485a241f1c478e0dd5844695a6f TextureImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 12 mipmaps: mipMapMode: 0 enableMipMap: 0 sRGBTexture: 1 linearTexture: 0 fadeOut: 0 borderMipMap: 0 mipMapsPreserveCoverage: 0 alphaTestReferenceValue: 0.5 mipMapFadeDistanceStart: 1 mipMapFadeDistanceEnd: 3 bumpmap: convertToNormalMap: 0 externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 ignoreMasterTextureLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 seamlessCubemap: 0 textureFormat: 1 maxTextureSize: 2048 textureSettings: serializedVersion: 2 filterMode: 1 aniso: 1 mipBias: 0 wrapU: 1 wrapV: 1 wrapW: 0 nPOTScale: 0 lightmap: 0 compressionQuality: 50 spriteMode: 1 spriteExtrude: 1 spriteMeshType: 1 alignment: 0 spritePivot: {x: 0.5, y: 0.5} spritePixelsToUnits: 100 spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 0 alphaUsage: 1 alphaIsTransparency: 1 spriteTessellationDetail: -1 textureType: 2 textureShape: 1 singleChannelComponent: 0 flipbookRows: 1 flipbookColumns: 1 maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 cookieLightType: 1 platformSettings: - serializedVersion: 3 buildTarget: DefaultTexturePlatform maxTextureSize: 128 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 - serializedVersion: 3 buildTarget: Standalone maxTextureSize: 128 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 - serializedVersion: 3 buildTarget: Android maxTextureSize: 128 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 - serializedVersion: 3 buildTarget: WebGL maxTextureSize: 128 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 - serializedVersion: 3 buildTarget: Server maxTextureSize: 128 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] physicsShape: [] bones: [] spriteID: internalID: 0 vertices: [] indices: edges: [] weights: [] secondaryTextures: [] nameFileIdTable: {} spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Editor Default Resources/Icons.meta ================================================ fileFormatVersion: 2 guid: 1af14f53b56f41e4b95a1e6089cea2e6 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Editor Default Resources.meta ================================================ fileFormatVersion: 2 guid: 78d7e3c75054b9b4abf1814a70fe3695 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Example.meta ================================================ fileFormatVersion: 2 guid: ae74ab3446648452aa052bfb9ad976e8 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Example~/ColyseusNetworkManager.cs ================================================ using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using Colyseus; public class WeatherMessage { public string weather; } public class ColyseusNetworkManager : MonoBehaviour { protected Client client; protected Room room; protected Room lobbyRoom; protected Room queueRoom; // Start is called before the first frame update void Start() { client = new Client("ws://localhost:2567"); // // join "my_room" and bind callbacks // ------------------------------------------------------------ // this is the main game room demonstration, with messages and state callbacks // joinMyRoom(); // // join "lobby" room and bind callbacks // ------------------------------------------------------------ // when creating or destroying "my_room" rooms, the "lobby" room will receive these events // joinLobbyRoom(); // // join "queue" room and bind callbacks // ------------------------------------------------------------ // the "queue" room requires 4 clients to join the game room // you can open the playground at http://localhost:2567, and join the "queue" room 3 times to test it // joinQueueRoom(); } // Update is called once per frame void Update() { } async void OnDestroy() { // Close all room connections when the MonoBehaviour is destroyed // (e.g., when stopping play mode in the Editor) if (room != null) await room.Leave(); if (lobbyRoom != null) await lobbyRoom.Leave(); if (queueRoom != null) await queueRoom.Leave(); } protected async void joinMyRoom() { var options = new Dictionary(); room = await client.JoinOrCreate("my_room", options); // Allow to reconnect immediately room.Reconnection.MinUptime = 0; room.OnLeave += (int code) => { Debug.Log($"[MyRoom] Left room with code: {code}"); }; // // messages from server // room.OnMessage("weather", (WeatherMessage message) => { Debug.Log($"[MyRoom] Weather changed: {message.weather}"); }); // // state callbacks // var callbacks = Colyseus.Schema.Callbacks.Get(room); callbacks.OnAdd(state => state.players, (key, player) => { Debug.Log($"[MyRoom] Player {key} joined the room"); callbacks.OnChange(player, () => { Debug.Log($"[MyRoom] Player {key} changed!"); }); callbacks.Listen(player, player => player.x, (x, _) => { Debug.Log($"[MyRoom] Player {key} moved to {x}"); }); callbacks.Listen(player, player => player.y, (y, _) => { Debug.Log($"[MyRoom] Player {key} moved to {y}"); }); }); callbacks.OnRemove(state => state.players, (key, player) => { Debug.Log($"[MyRoom] Player {key} left the room"); }); } protected async void joinLobbyRoom() { lobbyRoom = await client.JoinOrCreate("lobby"); lobbyRoom.OnMessage("rooms", (RoomAvailable[] message) => { Debug.Log($"[Lobby] Rooms: {message}"); }); lobbyRoom.OnMessage("+", (object[] message) => { string roomId = (string)message[0]; var roomData = (IDictionary)message[1]; Debug.Log($"[Lobby] Add Room: {roomId} - {roomData["name"]} ({roomData["clients"]}/{roomData["maxClients"]})"); }); lobbyRoom.OnMessage("-", (string message) => { Debug.Log($"[Lobby] Remove Room: {message}"); }); } protected async void joinQueueRoom() { queueRoom = await client.JoinOrCreate("queue"); queueRoom.OnMessage("clients", (int clients) => { Debug.Log($"[Queue] Clients: {clients}"); }); queueRoom.OnMessage("seat", async (SeatReservation seat) => { Debug.Log($"[Queue] Seat: {seat.roomId} - {seat.sessionId} - {seat.publicAddress} - {seat.processId} - {seat.reconnectionToken} - {seat.devMode} - {seat.protocol}"); var room = await client.ConsumeSeatReservation(seat); Debug.Log($"[Queue] Joined game room: {room.RoomId}"); room.OnLeave += (int code) => { Debug.Log($"[Queue] Left game room: {code}"); }; // confirm successfully joined the room! / leave the queue room _ = queueRoom.Send("confirm"); // delay 1 second _ = Task.Delay(500); // // queue demo is over, leave the room // (you should actually bind the callbacks for the room and not leave it here) // _ = room.Leave(); }); queueRoom.OnLeave += (int code) => { Debug.Log($"[Queue] Left queue room: {code}"); }; } } ================================================ FILE: Assets/Colyseus/Runtime/Example~/ColyseusNetworkManager.cs.meta ================================================ fileFormatVersion: 2 guid: 9ca8e2dc39ba1406a8d548cecca16bde MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Example~/ConnectionManager.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; public class ConnectionManager : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } } ================================================ FILE: Assets/Colyseus/Runtime/Example~/ConnectionManager.cs.meta ================================================ fileFormatVersion: 2 guid: 3ca02083085144590ae17a25ce46df36 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Example~/ExampleScene.unity.meta ================================================ fileFormatVersion: 2 guid: c6a63a8be1d1044869c7ef0d1a91dde4 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Example~/MyServerSettings.asset.meta ================================================ fileFormatVersion: 2 guid: c63116ed08d6d405985d628d4aeb2820 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/Example~/Schema/Item.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 4.0.7 // using Colyseus.Schema; #if UNITY_5_3_OR_NEWER using UnityEngine.Scripting; #endif public partial class Item : Schema { #if UNITY_5_3_OR_NEWER [Preserve] #endif public Item() { } [Type(0, "string")] public string name = default(string); [Type(1, "number")] public float value = default(float); } ================================================ FILE: Assets/Colyseus/Runtime/Example~/Schema/Item.cs.meta ================================================ fileFormatVersion: 2 guid: 1add4259764d34d94bf8be13e98a69ca ================================================ FILE: Assets/Colyseus/Runtime/Example~/Schema/MyRoomState.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 4.0.7 // using Colyseus.Schema; #if UNITY_5_3_OR_NEWER using UnityEngine.Scripting; #endif public partial class MyRoomState : Schema { #if UNITY_5_3_OR_NEWER [Preserve] #endif public MyRoomState() { } [Type(0, "map", typeof(MapSchema))] public MapSchema players = null; [Type(1, "ref", typeof(Player))] public Player host = null; [Type(2, "string")] public string currentTurn = default(string); } ================================================ FILE: Assets/Colyseus/Runtime/Example~/Schema/MyRoomState.cs.meta ================================================ fileFormatVersion: 2 guid: 8ae9c4a2670084d28843c4b84c3dd530 ================================================ FILE: Assets/Colyseus/Runtime/Example~/Schema/Player.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 4.0.7 // using Colyseus.Schema; #if UNITY_5_3_OR_NEWER using UnityEngine.Scripting; #endif public partial class Player : Schema { #if UNITY_5_3_OR_NEWER [Preserve] #endif public Player() { } [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); [Type(2, "boolean")] public bool isBot = default(bool); [Type(3, "boolean")] public bool disconnected = default(bool); [Type(4, "array", typeof(ArraySchema))] public ArraySchema items = null; } ================================================ FILE: Assets/Colyseus/Runtime/Example~/Schema/Player.cs.meta ================================================ fileFormatVersion: 2 guid: 74cbd8724747e4d2092e580e14725b78 ================================================ FILE: Assets/Colyseus/Runtime/Example~/Schema.meta ================================================ fileFormatVersion: 2 guid: a80dec46085bf4ea4b523a69ff3f2405 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/ArrayExtensions.cs ================================================ using System; namespace GameDevWare.Serialization { internal static class ArrayExtensions { public static OutputT[] ConvertAll(this T[] array, Func converter) { if (array == null) throw new ArgumentNullException("array"); if (converter == null) throw new ArgumentNullException("converter"); var newList = new OutputT[array.Length]; var i = 0; foreach (var item in array) { newList[i++] = converter(item); } return newList; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/ArrayExtensions.cs.meta ================================================ fileFormatVersion: 2 guid: b7ce8c517c054bd5bec836e71944c782 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/GenerateTypeSerializerAttribute.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] public class GenerateTypeSerializerAttribute : Attribute { } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/GenerateTypeSerializerAttribute.cs.meta ================================================ fileFormatVersion: 2 guid: 8420b011fc234497996f0dd94bd8b466 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IJsonReader.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public interface IJsonReader { SerializationContext Context { get; } JsonToken Token { get; } object RawValue { get; } IValueInfo Value { get; } bool NextToken(); bool IsEndOfStream(); /// /// Resets Line/Column numbers, CharactersRead and Token information of reader /// void Reset(); } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IJsonReader.cs.meta ================================================ fileFormatVersion: 2 guid: 9e6240f54909465fb6d01ab74fa0061d timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IJsonWriter.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public interface IJsonWriter { SerializationContext Context { get; } void Flush(); void Write(string value); void Write(JsonMember value); void Write(int number); void Write(uint number); void Write(long number); void Write(ulong number); void Write(float number); void Write(double number); void Write(decimal number); void Write(bool value); void Write(DateTime dateTime); void Write(DateTimeOffset dateTimeOffset); void WriteObjectBegin(int numberOfMembers); void WriteObjectEnd(); void WriteArrayBegin(int numberOfMembers); void WriteArrayEnd(); void WriteNull(); void WriteJson(string jsonString); void WriteJson(char[] jsonString, int index, int charCount); void Reset(); } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IJsonWriter.cs.meta ================================================ fileFormatVersion: 2 guid: 633fb2c48abf4347a8ffe33fafa2ce00 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IValueInfo.cs ================================================ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public interface IValueInfo { bool HasValue { get; } object Raw { get; } Type Type { get; } bool AsBoolean { get; } byte AsByte { get; } short AsInt16 { get; } int AsInt32 { get; } long AsInt64 { get; } sbyte AsSByte { get; } ushort AsUInt16 { get; } uint AsUInt32 { get; } ulong AsUInt64 { get; } float AsSingle { get; } double AsDouble { get; } decimal AsDecimal { get; } string AsString { get; } DateTime AsDateTime { get; } int LineNumber { get; } int ColumnNumber { get; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IValueInfo.cs.meta ================================================ fileFormatVersion: 2 guid: 08a9e5333be7414aa0de623e9026075e timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IndexedDictionary.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { [Serializable, DebuggerDisplay("IndexedDictionary, Count: {Count}")] public class IndexedDictionary : IDictionary, IDictionary { [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] private readonly Dictionary dictionary; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly List keys; [DebuggerBrowsable(DebuggerBrowsableState.Never), NonSerialized] private ReadOnlyCollection keysReadOnly; /// public int Count { get { return this.dictionary.Count; } } /// public ValueT this[KeyT key] { get { return this.dictionary[key]; } set { if (this.dictionary.ContainsKey(key) == false) this.keys.Add(key); this.dictionary[key] = value; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] object IDictionary.this[object key] { get { var value = default(ValueT); return this.TryGetValue((KeyT)key, out value) ? value : default(object); } set { this[(KeyT)key] = (ValueT)value; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] object ICollection.SyncRoot { get { return this.dictionary; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] bool ICollection.IsSynchronized { get { return false; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] public ReadOnlyCollection Keys { get { return this.keysReadOnly ?? (this.keysReadOnly = new ReadOnlyCollection(this.keys)); } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] public ReadOnlyCollection Values { get { return this.GetValues(); } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection IDictionary.Values { get { return this.GetValues(); } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection IDictionary.Keys { get { return this.keys; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] bool IDictionary.IsReadOnly { get { return false; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] bool IDictionary.IsFixedSize { get { return false; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] bool ICollection>.IsReadOnly { get { return false; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection IDictionary.Keys { get { return this.keysReadOnly; } } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection IDictionary.Values { get { return this.GetValues(); } } public IndexedDictionary() { this.dictionary = new Dictionary(); this.keys = new List(); this.keysReadOnly = new ReadOnlyCollection(this.keys); } public IndexedDictionary(int count) { if (count < 0) throw new ArgumentOutOfRangeException("count"); if (count == 0) count = 30; this.dictionary = new Dictionary(count); this.keys = new List(count); this.keysReadOnly = new ReadOnlyCollection(this.keys); } public IndexedDictionary(IDictionary dictionary) { if (dictionary == null) throw new ArgumentNullException("dictionary"); this.dictionary = new Dictionary(dictionary); this.keys = new List(dictionary.Keys); this.keysReadOnly = new ReadOnlyCollection(this.keys); } public IndexedDictionary(IEnumerable> pairs) { if (pairs == null) throw new ArgumentNullException("pairs"); this.dictionary = new Dictionary(); this.keys = new List(); this.keysReadOnly = new ReadOnlyCollection(this.keys); foreach (var pair in pairs) this.Add(pair.Key, pair.Value); } public IndexedDictionary(IDictionary dictionary, ICollection keys) { if (dictionary == null) throw new ArgumentNullException("dictionary"); if (keys == null) throw new ArgumentNullException("keys"); this.dictionary = new Dictionary(dictionary); this.keys = new List(keys); this.keysReadOnly = new ReadOnlyCollection(this.keys); } /// public void Add(KeyT key, ValueT value) { this.dictionary.Add(key, value); this.keys.Add(key); } /// public void Add(IndexedDictionary other) { if (other == null) throw new ArgumentNullException("other"); if (this.Count == 0) { this.keys.AddRange(other.keys); foreach (var kv in other.dictionary) this.dictionary.Add(kv.Key, kv.Value); } else { foreach (var kv in other.dictionary) { this.dictionary.Add(kv.Key, kv.Value); this.keys.Add(kv.Key); } } } /// public void Insert(int index, KeyT key, ValueT value) { // Dictionary operation first, so exception thrown if key already exists. this.dictionary.Add(key, value); this.keys.Insert(index, key); } /// public bool ContainsKey(KeyT key) { return this.dictionary.ContainsKey(key); } /// public bool ContainsKey(KeyT key, IEqualityComparer keyComparer) { foreach (var k in this.keys) if (keyComparer.Equals(k, key)) return true; return false; } /// public bool ContainsValue(ValueT value) { foreach (var kv in this.dictionary) if (Equals(value, kv.Value)) return true; return false; } /// public bool ContainsValue(ValueT value, IEqualityComparer comparer) { if (comparer == null) throw new ArgumentNullException("comparer"); foreach (var kv in this.dictionary) if (comparer.Equals(value, kv.Value)) return true; return false; } /// public bool Remove(KeyT key) { var wasInDictionary = this.dictionary.Remove(key); this.keys.Remove(key); return wasInDictionary; } /// public bool TryGetValue(KeyT key, out ValueT value) { return this.dictionary.TryGetValue(key, out value); } /// public int IndexOf(KeyT key) { return this.keys.IndexOf(key); } /// public void RemoveAt(int index) { if (index >= this.Count || index < 0) throw new ArgumentOutOfRangeException("index"); var key = this.keys[index]; this.dictionary.Remove(key); this.keys.RemoveAt(index); } public void SortKeys(IComparer comparer) { if (comparer == null) throw new ArgumentNullException("comparer"); this.keys.Sort(comparer); } /// public void Clear() { this.dictionary.Clear(); this.keys.Clear(); } private ReadOnlyCollection GetValues() { var values = new ValueT[this.Count]; var index = 0; foreach (var key in this.keys) values[index++] = this.dictionary[key]; return new ReadOnlyCollection(values); } /// bool IDictionary.Contains(object key) { return this.ContainsKey((KeyT)key); } /// void IDictionary.Add(object key, object value) { this.Add((KeyT)key, (ValueT)value); } /// IDictionaryEnumerator IDictionary.GetEnumerator() { return this.GetEnumerator(); } /// void IDictionary.Remove(object key) { this.Remove((KeyT)key); } /// void ICollection.CopyTo(Array array, int index) { if (array == null) throw new ArgumentNullException("array"); if (index >= array.Length) throw new ArgumentOutOfRangeException("index"); if (index + this.Count > array.Length) throw new ArgumentOutOfRangeException("index"); var end = index + this.Count; for (var i = 0; i < end; i++) array.SetValue(new DictionaryEntry(this.keys[i], this.dictionary[this.keys[i]]), index + i); } /// void ICollection>.Add(KeyValuePair item) { this.Add(item.Key, item.Value); } /// bool ICollection>.Contains(KeyValuePair item) { var value = default(ValueT); return this.dictionary.TryGetValue(item.Key, out value) && Equals(value, item.Value); } /// void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { foreach (var pair in this) { array[arrayIndex] = pair; arrayIndex++; } } /// bool ICollection>.Remove(KeyValuePair item) { if (!this.Contains(item)) return false; return this.Remove(item.Key); } /// IEnumerator> IEnumerable>.GetEnumerator() { return this.GetEnumerator(); } /// IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } public Enumerator GetEnumerator() { return new Enumerator(this); } /// public override string ToString() { return "Count: " + this.Count.ToString(); } public struct Enumerator : IEnumerator>, IDictionaryEnumerator { private List.Enumerator innerEnumerator; private readonly IndexedDictionary owner; private KeyValuePair current; public Enumerator(IndexedDictionary owner) { this.owner = owner; this.innerEnumerator = owner.keys.GetEnumerator(); this.current = new KeyValuePair(); } /// public object Key { get { return this.current.Key; } } /// public object Value { get { return this.current.Value; } } /// public DictionaryEntry Entry { get { return new DictionaryEntry(this.current.Key, this.current.Value); } } /// public KeyValuePair Current { get { return this.current; } } /// object IEnumerator.Current { get { return this.Entry; } } /// public bool MoveNext() { if (!this.innerEnumerator.MoveNext()) return false; var key = this.innerEnumerator.Current; this.current = new KeyValuePair(key, this.owner.dictionary[key]); return true; } /// public void Reset() { this.innerEnumerator = this.owner.keys.GetEnumerator(); } /// public void Dispose() { this.innerEnumerator.Dispose(); } } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/IndexedDictionary.cs.meta ================================================ fileFormatVersion: 2 guid: 37db542680ca413d96e9d3920576ea8a timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Json.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using GameDevWare.Serialization.MessagePack; using GameDevWare.Serialization.Serializers; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public static class Json { private static IFormatProvider _DefaultFormat = CultureInfo.InvariantCulture; private static Encoding _DefaultEncoding = new UTF8Encoding(false, true); private static string[] _DefaultDateTimeFormats; public static string[] DefaultDateTimeFormats { get { return _DefaultDateTimeFormats; } set { if (value == null) throw new ArgumentNullException("value"); if (value.Length == 0) throw new ArgumentException(); _DefaultDateTimeFormats = value; } } public static IFormatProvider DefaultFormat { get { return _DefaultFormat; } set { if (value == null) throw new ArgumentNullException("value"); _DefaultFormat = value; } } public static Encoding DefaultEncoding { get { return _DefaultEncoding; } set { if (value == null) throw new ArgumentNullException("value"); _DefaultEncoding = value; } } public static List DefaultSerializers { get; private set; } static Json() { // ReSharper disable StringLiteralTypo _DefaultDateTimeFormats = new[] { "yyyy-MM-ddTHH:mm:ss.fffzzz", // ISO 8601, with timezone "yyyy-MM-ddTHH:mm:ss.ffzzz", // ISO 8601, with timezone "yyyy-MM-ddTHH:mm:ss.fzzz", // ISO 8601, with timezone "yyyy-MM-ddTHH:mm:ssZ", // also ISO 8601, without timezone and without microseconds "yyyy-MM-ddTHH:mm:ss.fZ", // also ISO 8601, without timezone "yyyy-MM-ddTHH:mm:ss.ffZ", // also ISO 8601, without timezone "yyyy-MM-ddTHH:mm:ss.fffZ", // also ISO 8601, without timezone "yyyy-MM-ddTHH:mm:ss.ffffZ", // also ISO 8601, without timezone "yyyy-MM-ddTHH:mm:ss.fffffZ", // also ISO 8601, without timezone "yyyy-MM-ddTHH:mm:ss.ffffffZ", // also ISO 8601, without timezone "yyyy-MM-ddTHH:mm:ss.fffffffZ" // also ISO 8601, without timezone }; // ReSharper restore StringLiteralTypo DefaultSerializers = new List { new BinarySerializer(), new DateTimeOffsetSerializer(), new DateTimeSerializer(), new GuidSerializer(), new StreamSerializer(), new UriSerializer(), new VersionSerializer(), new TimeSpanSerializer(), new DictionaryEntrySerializer(), #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER new BoundsSerializer(), new Matrix4x4Serializer(), new QuaternionSerializer(), new RectSerializer(), new Vector2Serializer(), new Vector3Serializer(), new Vector4Serializer(), #endif new PrimitiveSerializer(typeof (bool)), new PrimitiveSerializer(typeof (byte)), new PrimitiveSerializer(typeof (decimal)), new PrimitiveSerializer(typeof (double)), new PrimitiveSerializer(typeof (short)), new PrimitiveSerializer(typeof (int)), new PrimitiveSerializer(typeof (long)), new PrimitiveSerializer(typeof (sbyte)), new PrimitiveSerializer(typeof (float)), new PrimitiveSerializer(typeof (ushort)), new PrimitiveSerializer(typeof (uint)), new PrimitiveSerializer(typeof (ulong)), new PrimitiveSerializer(typeof (string)), }; } public static void Serialize(T objectToSerialize, Stream jsonOutput) { Serialize(objectToSerialize, jsonOutput, CreateDefaultContext(SerializationOptions.None)); } public static void Serialize(T objectToSerialize, Stream jsonOutput, Encoding encoding) { Serialize(objectToSerialize, jsonOutput, CreateDefaultContext(SerializationOptions.None, encoding)); } public static void Serialize(T objectToSerialize, Stream jsonOutput, SerializationOptions options) { Serialize(objectToSerialize, jsonOutput, CreateDefaultContext(options)); } public static void Serialize(T objectToSerialize, Stream jsonOutput, SerializationOptions options, Encoding encoding) { Serialize(objectToSerialize, jsonOutput, CreateDefaultContext(options, encoding)); } public static void Serialize(T objectToSerialize, Stream jsonOutput, SerializationContext context) { if (jsonOutput == null) throw new ArgumentNullException("jsonOutput"); if (context == null) throw new ArgumentNullException("context"); if (!jsonOutput.CanWrite) throw JsonSerializationException.StreamIsNotWriteable(); if (objectToSerialize == null) { var bytes = context.Encoding.GetBytes("null"); jsonOutput.Write(bytes, 0, bytes.Length); return; } var writer = new JsonStreamWriter(jsonOutput, context); writer.WriteValue(objectToSerialize, typeof(T)); writer.Flush(); } public static void Serialize(T objectToSerialize, TextWriter textWriter) { Serialize(objectToSerialize, textWriter, CreateDefaultContext(SerializationOptions.None)); } public static void Serialize(T objectToSerialize, TextWriter textWriter, SerializationOptions options) { Serialize(objectToSerialize, textWriter, CreateDefaultContext(options)); } public static void Serialize(T objectToSerialize, TextWriter textWriter, SerializationContext context) { if (textWriter == null) throw new ArgumentNullException("textWriter"); if (context == null) throw new ArgumentNullException("context"); if (objectToSerialize == null) { textWriter.Write("null"); textWriter.Flush(); return; } var writer = new JsonTextWriter(textWriter, context); writer.WriteValue(objectToSerialize, typeof(T)); writer.Flush(); } public static void Serialize(T objectToSerialize, IJsonWriter writer, SerializationContext context) { if (writer == null) throw new ArgumentNullException("writer"); if (context == null) throw new ArgumentNullException("context"); if (objectToSerialize == null) { writer.WriteNull(); writer.Flush(); return; } writer.WriteValue(objectToSerialize, typeof(T)); writer.Flush(); } public static string SerializeToString(T objectToSerialize) { return SerializeToString(objectToSerialize, CreateDefaultContext(SerializationOptions.None)); } public static string SerializeToString(T objectToSerialize, SerializationOptions options) { return SerializeToString(objectToSerialize, CreateDefaultContext(options)); } public static string SerializeToString(T objectToSerialize, SerializationContext context) { if (context == null) throw new ArgumentNullException("context"); if (objectToSerialize == null) return "null"; var writer = new JsonStringBuilderWriter(new StringBuilder(), context); writer.WriteValue(objectToSerialize, typeof(T)); writer.Flush(); return writer.ToString(); } public static object Deserialize(Type objectType, byte[] jsonBytes, int offset, int length) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(objectType, new MemoryStream(jsonBytes, offset, length)); } public static object Deserialize(Type objectType, byte[] jsonBytes, int offset, int length, Encoding encoding) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(objectType, new MemoryStream(jsonBytes, offset, length), encoding); } public static object Deserialize(Type objectType, byte[] jsonBytes, int offset, int length, SerializationOptions options) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(objectType, new MemoryStream(jsonBytes, offset, length), options); } public static object Deserialize(Type objectType, byte[] jsonBytes, int offset, int length, SerializationOptions options, Encoding encoding) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(objectType, new MemoryStream(jsonBytes, offset, length), options, encoding); } public static object Deserialize(Type objectType, byte[] jsonBytes, int offset, int length, SerializationContext context) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(objectType, new MemoryStream(jsonBytes, offset, length), context); } public static object Deserialize(Type objectType, Stream jsonStream) { return Deserialize(objectType, jsonStream, CreateDefaultContext(SerializationOptions.None)); } public static object Deserialize(Type objectType, Stream jsonStream, Encoding encoding) { return Deserialize(objectType, jsonStream, CreateDefaultContext(SerializationOptions.None, encoding)); } public static object Deserialize(Type objectType, Stream jsonStream, SerializationOptions options) { return Deserialize(objectType, jsonStream, CreateDefaultContext(options)); } public static object Deserialize(Type objectType, Stream jsonStream, SerializationOptions options, Encoding encoding) { return Deserialize(objectType, jsonStream, CreateDefaultContext(options, encoding)); } public static object Deserialize(Type objectType, Stream jsonStream, SerializationContext context) { if (objectType == null) throw new ArgumentNullException("objectType"); if (jsonStream == null) throw new ArgumentNullException("jsonStream"); if (context == null) throw new ArgumentNullException("context"); if (!jsonStream.CanRead) throw JsonSerializationException.StreamIsNotReadable(); var reader = new JsonStreamReader(jsonStream, context); return reader.ReadValue(objectType, false); } public static object Deserialize(Type objectType, TextReader textReader) { return Deserialize(objectType, textReader, CreateDefaultContext(SerializationOptions.None)); } public static object Deserialize(Type objectType, TextReader textReader, SerializationOptions options) { return Deserialize(objectType, textReader, CreateDefaultContext(options)); } public static object Deserialize(Type objectType, TextReader textReader, SerializationContext context) { if (objectType == null) throw new ArgumentNullException("objectType"); if (textReader == null) throw new ArgumentNullException("textReader"); if (context == null) throw new ArgumentNullException("context"); var reader = new JsonTextReader(textReader, context); return reader.ReadValue(objectType, false); } public static object Deserialize(Type objectType, string jsonString) { return Deserialize(objectType, jsonString, CreateDefaultContext(SerializationOptions.None)); } public static object Deserialize(Type objectType, string jsonString, SerializationOptions options) { return Deserialize(objectType, jsonString, CreateDefaultContext(options)); } public static object Deserialize(Type objectType, string jsonString, SerializationContext context) { if (objectType == null) throw new ArgumentNullException("objectType"); if (jsonString == null) throw new ArgumentNullException("jsonString"); if (context == null) throw new ArgumentNullException("context"); var reader = new JsonStringReader(jsonString, context); return reader.ReadValue(objectType, false); } public static object Deserialize(Type objectType, IJsonReader reader) { if (objectType == null) throw new ArgumentNullException("objectType"); if (reader == null) throw new ArgumentNullException("reader"); return reader.ReadValue(objectType, false); } public static T Deserialize(byte[] jsonBytes, int offset, int length) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(new MemoryStream(jsonBytes, offset, length)); } public static T Deserialize(byte[] jsonBytes, int offset, int length, Encoding encoding) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(new MemoryStream(jsonBytes, offset, length), encoding); } public static T Deserialize(byte[] jsonBytes, int offset, int length, SerializationOptions options) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(new MemoryStream(jsonBytes, offset, length), options); } public static T Deserialize(byte[] jsonBytes, int offset, int length, SerializationOptions options, Encoding encoding) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize(new MemoryStream(jsonBytes, offset, length), options, encoding); } public static T Deserialize(byte[] jsonBytes, int offset, int length, SerializationContext context) { if (jsonBytes == null) throw new ArgumentNullException("jsonBytes"); return Deserialize( new MemoryStream(jsonBytes, offset, length), context); } public static T Deserialize(Stream jsonStream) { return (T)Deserialize(typeof(T), jsonStream, CreateDefaultContext(SerializationOptions.None)); } public static T Deserialize(Stream jsonStream, Encoding encoding) { return (T)Deserialize(typeof(T), jsonStream, CreateDefaultContext(SerializationOptions.None, encoding)); } public static T Deserialize(Stream jsonStream, SerializationOptions options) { return (T)Deserialize(typeof(T), jsonStream, CreateDefaultContext(options)); } public static T Deserialize(Stream jsonStream, SerializationOptions options, Encoding encoding) { return (T)Deserialize(typeof(T), jsonStream, CreateDefaultContext(options, encoding)); } public static T Deserialize(Stream jsonStream, SerializationContext context) { return (T)Deserialize(typeof(T), jsonStream, context); } public static T Deserialize(TextReader textReader) { return (T)Deserialize(typeof(T), textReader, CreateDefaultContext(SerializationOptions.None)); } public static T Deserialize(TextReader textReader, SerializationOptions options) { return (T)Deserialize(typeof(T), textReader, CreateDefaultContext(options)); } public static T Deserialize(TextReader textReader, SerializationContext context) { return (T)Deserialize(typeof(T), textReader, context); } public static T Deserialize(string jsonString) { return (T)Deserialize(typeof(T), jsonString, CreateDefaultContext(SerializationOptions.None)); } public static T Deserialize(string jsonString, SerializationOptions options) { return (T)Deserialize(typeof(T), jsonString, CreateDefaultContext(options)); } public static T Deserialize(string jsonString, SerializationContext context) { return (T)Deserialize(typeof(T), jsonString, context); } public static T Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); var serializer = reader.Context.GetSerializerForType(typeof(T)); return (T)serializer.Deserialize(reader); } private static SerializationContext CreateDefaultContext(SerializationOptions options, Encoding encoding = null) { return new SerializationContext { Encoding = encoding ?? DefaultEncoding, Options = options }; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Json.cs.meta ================================================ fileFormatVersion: 2 guid: ae711c9fe19244fab91b2218cc02ae50 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonMember.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Linq; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public struct JsonMember : IEquatable, IEquatable { internal string NameString; internal char[] NameChars; internal bool IsEscapedAndQuoted; public int Length { get { return this.NameString != null ? this.NameString.Length : this.NameChars.Length; } } public JsonMember(string name) : this(name, false) { } public JsonMember(string name, bool escapedAndQuoted) { if (name == null) throw new ArgumentNullException("name"); this.NameString = name; this.IsEscapedAndQuoted = escapedAndQuoted; this.NameChars = null; } public JsonMember(char[] name) : this(name, false) { } public JsonMember(char[] name, bool escapedAndQuoted) { if (name == null) throw new ArgumentNullException("name"); this.NameChars = name; this.IsEscapedAndQuoted = escapedAndQuoted; this.NameString = null; } public override int GetHashCode() { return this.NameString != null ? this.NameString.GetHashCode() : this.NameChars.Aggregate(0, (a, c) => a ^ (int) c); } public override bool Equals(object obj) { if (obj is JsonMember) return this.Equals((JsonMember) obj); else if (obj is string) return this.Equals((string) obj); else return false; } public bool Equals(JsonMember other) { return this.ToString().Equals(other.ToString(), StringComparison.Ordinal); } public bool Equals(string other) { return this.ToString().Equals(other, StringComparison.Ordinal); } public static explicit operator string(JsonMember member) { return member.ToString(); } public static explicit operator JsonMember(string memberName) { return new JsonMember(memberName); } public override string ToString() { var name = NameString; if (this.NameChars != null) name = new string(NameChars, 0, NameChars.Length); // this is used in tests, so perf is not primary if (this.IsEscapedAndQuoted) { if (name.EndsWith(":")) name = name.Substring(0, name.Length - 1); name = JsonUtils.UnescapeAndUnquote(name); } return name; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonMember.cs.meta ================================================ fileFormatVersion: 2 guid: d9e3f4d0576d4f3d9e6f9e93a0833f07 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonReader.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections; using System.Collections.Generic; using System.Globalization; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public abstract class JsonReader : IJsonReader { protected const int DEFAULT_BUFFER_SIZE = 1024; private sealed class Buffer : IList { private const float SHIFT_THRESHOLD = 0.5f; // position over 50% of buffer private const float GROW_THRESHOLD = 0.1f; // when less that 10% of space is free private readonly JsonReader reader; private char[] buffer; private int? lazyFixation; private int end; private readonly int initialSize; private bool isLast; private int lineNumber; private long lineStartIndex; private long charsReaded; private int Capacity { get { return this.buffer.Length; } set { if (value <= 0) throw new ArgumentOutOfRangeException("value"); var newBuffer = new char[value]; BlockCopy(this.buffer, 0, newBuffer, 0, Math.Min(newBuffer.Length, this.buffer.Length)); this.buffer = newBuffer; } } public int Offset { get; private set; } public long CharactersReaded { get { return this.charsReaded + this.Offset; } } public int LineNumber { get { return this.lineNumber + 1; } } public int ColumnNumber { get { return (int)(this.CharactersReaded - this.lineStartIndex + 1); } } public char this[int index] { get { if (index < 0) throw new ArgumentOutOfRangeException("index"); if (this.IsBeyondOfStream(index)) return (char)0; return this.buffer[this.Offset + index]; } } public Buffer(JsonReader reader, char[] buffer) { if (reader == null) new ArgumentNullException("reader"); this.reader = reader; this.buffer = buffer ?? new char[DEFAULT_BUFFER_SIZE]; this.initialSize = this.buffer.Length; this.end = 0; this.Offset = 0; } public void FixateNow() { if (this.lazyFixation == null) return; this.Fixate(this.lazyFixation.Value); this.lazyFixation = null; } public void Fixate(int atIndex) { if (atIndex < 0) throw new ArgumentOutOfRangeException("atIndex"); for (var i = 0; i < atIndex; i++) { if (this[i] != '\n') continue; this.lineNumber++; this.lineStartIndex = this.CharactersReaded + i; } // ensure that fixation point in loaded range this.IsBeyondOfStream(atIndex); this.Offset += atIndex; // when we are at second half of buffer - we need to shift back if ((this.Offset / (float)this.buffer.Length) > SHIFT_THRESHOLD) this.ShiftToZero(); } public void FixateLater(int atIndex) { if (atIndex < 0) throw new ArgumentOutOfRangeException("atIndex"); if (this.lazyFixation != null) this.lazyFixation += atIndex; else this.lazyFixation = atIndex; } public bool IsBeyondOfStream(int index) { if (!this.isLast && this.Offset + index >= this.end) this.ReadNextBlock(); if (this.isLast && this.Offset + index >= this.end) return true; return false; } public char[] GetChars() { return this.buffer; } public void Reset() { this.FixateNow(); this.ShiftToZero(); this.charsReaded = 0; this.lineNumber = 0; this.lineStartIndex = 0; } private void ReadNextBlock() { // when we are at second half of buffer - we need to shift back if ((this.Offset / (float)this.buffer.Length) > SHIFT_THRESHOLD) this.ShiftToZero(); // check for free space var free = this.buffer.Length - this.end; if ((free / (float)this.initialSize) < GROW_THRESHOLD) this.Capacity += this.initialSize; var newEnd = this.reader.FillBuffer(this.buffer, this.end); this.isLast = newEnd == this.end; this.end = newEnd; } private void ShiftToZero() { this.charsReaded += this.Offset; var block = Math.Min(this.Offset, this.end - this.Offset); var start = this.Offset; var lastBlock = 0; while (start < this.end) { BlockCopy(this.buffer, start, this.buffer, lastBlock, Math.Min(block, this.end - start)); lastBlock += block; start += block; } this.end = this.end - this.Offset; this.Offset = 0; #if DEBUG if (this.end < this.buffer.Length) // zero unused space(just for debug) Array.Clear(this.buffer, this.end, this.buffer.Length - this.end); #endif } private static void BlockCopy(char[] from, int fromIdx, char[] to, int toIdx, int len) { const int CHAR_SIZE = sizeof(char); System.Buffer.BlockCopy(from, fromIdx * CHAR_SIZE, to, toIdx * CHAR_SIZE, len * CHAR_SIZE); } public override string ToString() { return new string(this.buffer, this.Offset, this.end - this.Offset); } #region IList Members int IList.IndexOf(char item) { throw new NotSupportedException(); } void IList.Insert(int index, char item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } char IList.this[int index] { get { return this[index]; } set { throw new NotImplementedException(); } } #endregion #region ICollection Members void ICollection.Add(char item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Contains(char item) { throw new NotSupportedException(); } void ICollection.CopyTo(char[] array, int arrayIndex) { throw new NotSupportedException(); } int ICollection.Count { get { return this.buffer.Length; } } bool ICollection.IsReadOnly { get { return true; } } bool ICollection.Remove(char item) { throw new NotSupportedException(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return (this.buffer as IList).GetEnumerator(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return this.buffer.GetEnumerator(); } #endregion } private sealed class LazyValueInfo : IValueInfo { private enum Kind : byte { Explicit = 0, QuotedString, String }; private readonly JsonReader reader; private int jsonStart; private int jsonLen; private object value; private Kind valueKind; public bool HasValue { get; private set; } public object Raw { get { // eval lazy value if (this.valueKind == Kind.String) this.Raw = new string(this.reader.buffer.GetChars(), this.reader.buffer.Offset + this.jsonStart, this.jsonLen); else if (this.valueKind == Kind.QuotedString) this.Raw = JsonUtils.UnescapeBuffer(this.reader.buffer.GetChars(), this.reader.buffer.Offset + this.jsonStart, this.jsonLen); return this.value; } set { this.valueKind = Kind.Explicit; this.value = value; this.HasValue = true; } } public Type Type { get { if (this.valueKind != Kind.Explicit) { switch (this.reader.token) { case JsonToken.BeginArray: return typeof(List); case JsonToken.Number: return typeof(double); case JsonToken.Member: case JsonToken.StringLiteral: return typeof(string); case JsonToken.DateTime: return typeof(DateTime); case JsonToken.Boolean: return typeof(bool); } } if (this.value != null) return this.value.GetType(); else return typeof(object); } } public int LineNumber { get; private set; } public int ColumnNumber { get; private set; } public bool AsBoolean { get { return Convert.ToBoolean(this.Raw, this.reader.Context.Format); } } public byte AsByte { get { return Convert.ToByte(this.Raw, this.reader.Context.Format); } } public short AsInt16 { get { return Convert.ToInt16(this.Raw, this.reader.Context.Format); } } public int AsInt32 { get { return Convert.ToInt32(this.Raw, this.reader.Context.Format); } } public long AsInt64 { get { return Convert.ToInt64(this.Raw, this.reader.Context.Format); } } public sbyte AsSByte { get { return Convert.ToSByte(this.Raw, this.reader.Context.Format); } } public ushort AsUInt16 { get { return Convert.ToUInt16(this.Raw, this.reader.Context.Format); } } public uint AsUInt32 { get { return Convert.ToUInt32(this.Raw, this.reader.Context.Format); } } public ulong AsUInt64 { get { return Convert.ToUInt64(this.Raw, this.reader.Context.Format); } } public float AsSingle { get { return Convert.ToSingle(this.Raw, this.reader.Context.Format); } } public double AsDouble { get { return Convert.ToDouble(this.Raw, this.reader.Context.Format); } } public decimal AsDecimal { get { return Convert.ToDecimal(this.Raw, this.reader.Context.Format); } } public DateTime AsDateTime { get { if (this.Raw is DateTime) return (DateTime)this.Raw; else return DateTime.ParseExact(this.AsString, this.reader.Context.DateTimeFormats, this.reader.Context.Format, DateTimeStyles.AssumeUniversal); } } public string AsString { get { var raw = this.Raw; if (raw is string) return (string)raw; else if (raw is byte[]) return Convert.ToBase64String((byte[])raw); else return Convert.ToString(raw, this.reader.Context.Format); } } public LazyValueInfo(JsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); this.reader = reader; } public void ClearValue() { this.value = null; this.HasValue = false; this.valueKind = Kind.String; } public void SetBufferBounds(int start, int len) { if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); this.LineNumber = this.reader.buffer.LineNumber; this.ColumnNumber = this.reader.buffer.ColumnNumber + start; this.jsonStart = start; this.jsonLen = len; } public void SetAsLazyString(bool quoted) { this.valueKind = quoted ? Kind.QuotedString : Kind.String; this.HasValue = true; } } private const char INSIGNIFICANT_TAB = '\t'; private const char INSIGNIFICANT_SPACE = ' '; private const char INSIGNIFICANT_NEWLINE = '\n'; private const char INSIGNIFICANT_RETURN = '\r'; private const char INSIGNIFICANT_NAME_SEPARATOR = ':'; private const char INSIGNIFICANT_VALUE_SEPARATOR = ','; private const char SIGNIFICANT_BEGIN_ARRAY = '['; private const char SIGNIFICANT_END_ARRAY = ']'; private const char SIGNIFICANT_BEGIN_OBJECT = '{'; private const char SIGNIFICANT_END_OBJECT = '}'; private const char SIGNIFICANT_QUOTE = '\"'; private const char SIGNIFICANT_QUOTE_ALT = '\''; private LazyValueInfo lazyValue; private JsonToken token; private readonly Buffer buffer; protected JsonReader(SerializationContext context, char[] buffer = null) { if (context == null) throw new ArgumentNullException("context"); if (buffer != null && buffer.Length < 1024) throw new ArgumentOutOfRangeException("buffer", "Buffer should be at least 1024 chars long."); this.Context = context; this.lazyValue = new LazyValueInfo(this); this.buffer = new Buffer(this, buffer); } #region IJsonReader Members public SerializationContext Context { get; private set; } public IValueInfo Value { get { if (this.Token == JsonToken.None) this.NextToken(); return this.lazyValue; } } public JsonToken Token { get { if (this.token == JsonToken.None) this.NextToken(); return this.token; } } public object RawValue { get { if (this.token == JsonToken.None) this.NextToken(); return this.Value.Raw; } } public long CharactersReaded { get { return this.buffer.CharactersReaded; } } public bool NextToken() { var start = 0; var len = 0; var quoted = false; var isMember = false; if (!this.NextLexeme(ref start, ref len, ref quoted, ref isMember)) // end of stream { this.token = JsonToken.EndOfStream; this.lazyValue.Raw = null; return false; } this.lazyValue.ClearValue(); this.lazyValue.SetBufferBounds(start, len); if (len == 1 && !quoted && this.buffer[start] == SIGNIFICANT_BEGIN_ARRAY) { this.token = JsonToken.BeginArray; } else if (len == 1 && !quoted && this.buffer[start] == SIGNIFICANT_BEGIN_OBJECT) { this.token = JsonToken.BeginObject; } else if (len == 1 && !quoted && this.buffer[start] == SIGNIFICANT_END_ARRAY) { this.token = JsonToken.EndOfArray; } else if (len == 1 && !quoted && this.buffer[start] == SIGNIFICANT_END_OBJECT) { this.token = JsonToken.EndOfObject; } else if (len == 4 && !quoted && this.LookupAt(this.buffer, start, len, "null")) { this.token = JsonToken.Null; } else if (len == 4 && !quoted && this.LookupAt(this.buffer, start, len, "true")) { this.token = JsonToken.Boolean; this.lazyValue.Raw = true; } else if (len == 5 && !quoted && this.LookupAt(this.buffer, start, len, "false")) { this.token = JsonToken.Boolean; this.lazyValue.Raw = false; } else if (quoted && this.LookupAt(this.buffer, start, 6, "/Date(") && this.LookupAt(this.buffer, start + len - 2, 2, ")/")) { var ticks = JsonUtils.StringToInt64(this.buffer.GetChars(), this.buffer.Offset + start + 6, len - 8); this.token = JsonToken.DateTime; var dateTime = new DateTime(ticks * 0x2710L + JsonUtils.UnixEpochTicks, DateTimeKind.Utc); this.lazyValue.Raw = dateTime; } else if (!quoted && IsNumber(this.buffer, start, len)) { this.token = JsonToken.Number; this.lazyValue.SetAsLazyString(false); } else { this.token = isMember ? JsonToken.Member : JsonToken.StringLiteral; this.lazyValue.SetAsLazyString(quoted); } this.buffer.FixateLater(start + len + (quoted ? 1 : 0)); return true; } public bool IsEndOfStream() { return this.token == JsonToken.EndOfStream; } public void Reset() { this.buffer.Reset(); if (this.token != JsonToken.EndOfStream) { this.lazyValue = new LazyValueInfo(this); this.token = JsonToken.None; } } #endregion private static bool IsNumber(Buffer buffer, int start, int len) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); const int INT_PART = 0; const int FRAC_PART = 1; const int EXP_PART = 2; const char POINT = '.'; const char EXP = 'E'; const char PLUS = '+'; const char MINUS = '-'; len = start + len; var part = INT_PART; for (var i = start; i < len; i++) { var ch = buffer[i]; switch (part) { case INT_PART: if (ch == MINUS) { if (i != start) return false; } #if !STRICT else if (ch == PLUS) { if (i != start) return false; } #endif else if (ch == POINT) { if (i == start) return false; // decimal point as first character else part = FRAC_PART; } else if (char.ToUpper(ch) == EXP) { if (i == start) return false; // exp at first character else part = EXP_PART; } else if (!char.IsDigit(ch)) return false; // non digit character in int part break; case FRAC_PART: if (char.ToUpper(ch) == EXP) { if (i == start) return false; // exp at first character else part = EXP_PART; } else if (!char.IsDigit(ch)) return false; // non digit character in frac part break; case EXP_PART: if ((ch == PLUS || ch == MINUS)) { if (char.ToUpper(buffer[i - 1]) != EXP) return false; // sign not at start of exp part } else if (!char.IsDigit(ch)) return false; // non digit character in exp part break; } } return true; } private static bool IsInsignificantWhitespace(char symbol) { return symbol == INSIGNIFICANT_NEWLINE || symbol == INSIGNIFICANT_RETURN || symbol == INSIGNIFICANT_SPACE || symbol == INSIGNIFICANT_TAB; } private static bool IsInsignificant(char symbol) { return symbol == INSIGNIFICANT_NEWLINE || symbol == INSIGNIFICANT_RETURN || symbol == INSIGNIFICANT_SPACE || symbol == INSIGNIFICANT_TAB || symbol == INSIGNIFICANT_NAME_SEPARATOR || symbol == INSIGNIFICANT_VALUE_SEPARATOR; } private static bool IsLiteralTerminator(char ch, bool quoted, char quoteCh, bool escaped, bool eos, IJsonReader reader) { if (!escaped && quoted && ch == quoteCh) return true; else if (quoted && (ch == INSIGNIFICANT_NEWLINE || ch == INSIGNIFICANT_RETURN)) throw JsonSerializationException.UnterminatedStringLiteral(reader); else if (eos) { if (quoted) throw JsonSerializationException.UnexpectedEndOfStream(reader); else return true; } else if (!quoted && (ch == SIGNIFICANT_BEGIN_ARRAY || ch == SIGNIFICANT_BEGIN_OBJECT || ch == SIGNIFICANT_END_ARRAY || ch == SIGNIFICANT_END_OBJECT || ch == INSIGNIFICANT_VALUE_SEPARATOR || ch == INSIGNIFICANT_NAME_SEPARATOR)) return true; else if (!quoted && IsInsignificantWhitespace(ch)) return true; return false; } private bool LookupAt(Buffer buffer, int start, int len, string matchString) { for (var i = 0; i < len; i++) { if (buffer[start + i] != matchString[i]) return false; } return true; } private bool LookupAtSkipWhitespace(Buffer buffer, int start, int len, string matchString) { while (IsInsignificantWhitespace(buffer[start])) start++; for (var i = 0; i < len; i++) { if (buffer[start + i] != matchString[i]) return false; } return true; } /// /// Get next lexeme from current buffer /// /// return position of returned lexeme in buffer /// return size of returned lexeme /// return true when string literal was quoted /// is lexeme is object's member /// Null in case of "end of stream", or character buffer with result private bool NextLexeme(ref int start, ref int len, ref bool quoted, ref bool isMember) { // apply 'lazy' fixation this.buffer.FixateNow(); if (this.buffer.IsBeyondOfStream(0)) return false; var position = 0; var ch = this.buffer[position]; // skip insignificant characters while (!this.buffer.IsBeyondOfStream(position) && IsInsignificant(this.buffer[position])) position++; // we reached end of stream if (this.buffer.IsBeyondOfStream(position)) return false; // tell buffer that significant characters starts here // this prevents buffer overgrow this.buffer.Fixate(position); position = 0; var literalStart = position; // ch = this.buffer[position]; // // check for quote character var quoteCh = '\0'; if (ch == SIGNIFICANT_QUOTE) { quoteCh = ch; quoted = true; position++; literalStart++; } #if !STRICT else if (ch == SIGNIFICANT_QUOTE_ALT) { quoteCh = ch; quoted = true; position++; literalStart++; } #endif var escaped = false; // is character is escaped var eos = false; // is end of stream do { eos = this.buffer.IsBeyondOfStream(position); escaped = ch == '\\'; ch = this.buffer[position]; position++; } while (!IsLiteralTerminator(ch, quoted, quoteCh, escaped, eos, this)); var literalEnd = position - 1; // minus terminator character start = literalStart; len = literalEnd - literalStart; // special case - self terminated lexeme if (literalStart == literalEnd && !quoted) len = 1; isMember = this.LookupAtSkipWhitespace(this.buffer, literalEnd + (quoted ? 1 : 0), 1, ":"); return true; } /// /// Fills buffer with new characters, staring from /// /// Character buffer to fill /// index from which to start /// new buffer size protected abstract int FillBuffer(char[] buffer, int index); } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonReader.cs.meta ================================================ fileFormatVersion: 2 guid: 8ce453655ad14674adcc6bc3f730bf87 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonReaderExtentions.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Reflection; using System.Text; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public static class JsonReaderExtentions { public static void ReadArrayBegin(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token != JsonToken.BeginArray) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.BeginArray); if (reader.IsEndOfStream()) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.EndOfArray); if (nextToken) reader.NextToken(); } public static void ReadArrayEnd(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token != JsonToken.EndOfArray) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.EndOfArray); if (nextToken) reader.NextToken(); } public static void ReadObjectBegin(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token != JsonToken.BeginObject) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.BeginObject); if (reader.IsEndOfStream()) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.EndOfObject); if (nextToken) reader.NextToken(); } public static void ReadObjectEnd(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token != JsonToken.EndOfObject) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.EndOfObject); if (nextToken) reader.NextToken(); } public static string ReadMember(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token != JsonToken.Member) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.Member); var memberName = (string)reader.RawValue; if (nextToken) reader.NextToken(); return memberName; } public static byte ReadByte(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(byte); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsByte; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static byte? ReadByteOrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(byte?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsByte; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static sbyte ReadSByte(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(sbyte); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsSByte; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static sbyte? ReadSByteOrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(sbyte?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsSByte; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static short ReadInt16(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(short); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsInt16; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static short? ReadInt16OrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(short?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsInt16; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static int ReadInt32(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(int); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsInt32; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static int? ReadInt32OrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(int?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsInt32; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static long ReadInt64(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(long); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsInt64; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static long? ReadInt64OrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(long?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsInt64; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static ushort ReadUInt16(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(ushort); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsUInt16; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static ushort? ReadUInt16OrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(ushort?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsUInt16; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static uint ReadUInt32(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(uint); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsUInt32; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static uint? ReadUInt32OrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(uint?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsUInt32; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static ulong ReadUInt64(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(ulong); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsUInt64; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static ulong? ReadUInt64OrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(ulong?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsUInt64; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static float ReadSingle(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(float); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsSingle; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static float? ReadSingleOrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(float?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsSingle; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static double ReadDouble(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(double); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsDouble; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static double? ReadDoubleOrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(double?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsDouble; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static decimal ReadDecimal(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(decimal); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number) value = reader.Value.AsDecimal; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); if (nextToken) reader.NextToken(); return value; } public static decimal? ReadDecimalOrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(decimal?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: value = reader.Value.AsDecimal; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number); } if (nextToken) reader.NextToken(); return value; } public static bool ReadBoolean(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(bool); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Boolean) value = reader.Value.AsBoolean; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Boolean); if (nextToken) reader.NextToken(); return value; } public static bool? ReadBooleanOrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(bool?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Boolean: value = reader.Value.AsBoolean; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Boolean); } if (nextToken) reader.NextToken(); return value; } public static DateTime ReadDateTime(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(DateTime); if (reader.Token == JsonToken.Member || reader.Token == JsonToken.StringLiteral || reader.Token == JsonToken.Number || reader.Token == JsonToken.DateTime) value = reader.Value.AsDateTime; else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number, JsonToken.DateTime); if (nextToken) reader.NextToken(); return value; } public static DateTime? ReadDateTimeOrNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var value = default(DateTime?); switch (reader.Token) { case JsonToken.Null: value = null; break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: case JsonToken.DateTime: value = reader.Value.AsDateTime; break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number, JsonToken.DateTime); } if (nextToken) reader.NextToken(); return value; } public static string ReadString(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); var stringValue = default(string); switch (reader.Token) { case JsonToken.Null: break; case JsonToken.Member: case JsonToken.StringLiteral: case JsonToken.Number: case JsonToken.DateTime: case JsonToken.Boolean: stringValue = Convert.ToString(reader.RawValue, reader.Context.Format); break; default: throw JsonSerializationException.UnexpectedToken(reader, JsonToken.StringLiteral, JsonToken.Number, JsonToken.DateTime, JsonToken.Boolean); } if (nextToken) reader.NextToken(); return stringValue; } public static void ReadNull(this IJsonReader reader, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token != JsonToken.Null) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.Null); if (nextToken) reader.NextToken(); } public static object ReadValue(this IJsonReader reader, Type valueType, bool nextToken = true) { if (reader == null) throw new ArgumentNullException("reader"); // try guess type if (valueType == typeof(object)) valueType = reader.Value.Type; var value = default(object); var isNullable = valueType.IsValueType == false || valueType.IsInstantiationOf(typeof(Nullable<>)); if (reader.Token == JsonToken.Null && isNullable) { value = null; } else { if (isNullable && valueType.IsValueType) valueType = valueType.GetGenericArguments()[0]; // get subtype of Nullable var serializer = reader.Context.GetSerializerForType(valueType); value = serializer.Deserialize(reader); } if (nextToken) reader.NextToken(); return value; } public static string DebugPrintTokens(this IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); var output = new StringBuilder(); var stack = new Stack(); stack.Push(JsonToken.None); while (reader.NextToken()) { var strValue = reader.Token + (reader.Value.HasValue && reader.Value != null ? "[<" + reader.Value.Type.Name + "> " + JsonUtils.EscapeAndQuote(reader.Value.AsString).Trim('"') + "]" : ""); if (stack.Peek() != JsonToken.Member) { var endingTokenIndent = (reader.Token == JsonToken.EndOfObject || reader.Token == JsonToken.EndOfArray ? -1 : 0); output.Append(Environment.NewLine); for (var i = 0; i < System.Linq.Enumerable.Count(stack, t => t != JsonToken.Member && t != JsonToken.None) + endingTokenIndent; i++) output.Append("\t"); } else { output.Append(" "); } output.Append(strValue); if (reader.Token == JsonToken.EndOfObject || reader.Token == JsonToken.EndOfArray || stack.Peek() == JsonToken.Member) stack.Pop(); if (reader.Token == JsonToken.BeginObject || reader.Token == JsonToken.BeginArray || reader.Token == JsonToken.Member) stack.Push(reader.Token); } return output.ToString(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonReaderExtentions.cs.meta ================================================ fileFormatVersion: 2 guid: 91c78aa24ac24020bfde9485103e5a5b timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonSerializationException.cs ================================================ using System; using System.Reflection; using System.Runtime.Serialization; using System.Security; using GameDevWare.Serialization.Serializers; #pragma warning disable 1591 // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { [Serializable] public class JsonSerializationException : SerializationException { public enum ErrorCode { SerializationException = 1, EmptyMemberName, DiscriminatorNotFirstMemberOfObject, CantCreateInstanceOfType, SerializationGraphIsTooBig, SerializationGraphIsTooDeep, TypeIsNotValid, SerializingUnknownType, SerializingSpecialSystemType, UnexpectedEndOfStream, UnexpectedMemberName, UnexpectedToken, UnknownEscapeSequence, UnknownDiscriminatorValue, SerializationFramesCorruption, StreamIsNotReadable, StreamIsNotWriteable, UnterminatedStringLiteral, UnknownNotation, MemberNameIsNotSpecified, TypeRequiresCustomSerializer } public ErrorCode Code { get; set; } public int LineNumber { get; set; } public int ColumnNumber { get; set; } public ulong CharactersWritten { get; set; } internal JsonSerializationException(string message, ErrorCode errorCode, IJsonReader reader = null) : base(message) { this.Code = errorCode; if (reader != null) this.Update(reader); } internal JsonSerializationException(string message, ErrorCode errorCode, IJsonReader reader, Exception innerException) : base(message, innerException) { this.Code = errorCode; if (reader != null) this.Update(reader); } internal JsonSerializationException(string message, Exception innerException) : base(message, innerException) { this.Code = ErrorCode.SerializationException; } protected JsonSerializationException(SerializationInfo info, StreamingContext context) : base(info, context) { this.Code = (ErrorCode)info.GetInt32("Code"); this.LineNumber = info.GetInt32("LineNumber"); this.ColumnNumber = info.GetInt32("ColumnNumber"); this.CharactersWritten = info.GetUInt64("CharactersWritten"); } private void Update(IJsonReader reader) { this.LineNumber = reader.Value.LineNumber; this.ColumnNumber = reader.Value.ColumnNumber; //this.Path = reader.Value.Path; } [SecurityCritical] public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Code", (int)this.Code); info.AddValue("LineNumber", this.LineNumber); info.AddValue("ColumnNumber", this.ColumnNumber); info.AddValue("CharactersWritten", this.CharactersWritten); base.GetObjectData(info, context); } public static Exception MemberNameIsEmpty(IJsonReader reader) { return new JsonSerializationException ( string.Format("An empty member name was deserialized. Path: '{0}'", reader.Context.GetPath()), ErrorCode.EmptyMemberName, reader ); } public static Exception MemberNameIsNotSet() { return new JsonSerializationException ( "A member name is not set before writing value to object.", ErrorCode.MemberNameIsNotSpecified ); } public static Exception DiscriminatorIsNotFirstMember(IJsonReader reader) { return new JsonSerializationException ( string.Format("Discriminator member '{0}' should be first member of object. Path: '{1}'.", ObjectSerializer.TYPE_MEMBER_NAME, reader.Context.GetPath()), ErrorCode.DiscriminatorNotFirstMemberOfObject, reader ); } public static Exception CantCreateInstanceOfType(Type type) { return new JsonSerializationException ( string.Format("Unable to deserialize instance of '{0}' because ", type.FullName) + (type.GetTypeInfo().IsAbstract ? "it is an abstract type." : "there is no parameterless constructor is defined on type."), ErrorCode.CantCreateInstanceOfType ); } public static Exception SerializationGraphIsTooBig(IJsonReader reader, ulong maxObjects) { return new JsonSerializationException ( string.Format("Serialization graph is too big. Maximum serialized objects is {0}. Path: '{1}'", maxObjects, reader.Context.GetPath()), ErrorCode.SerializationGraphIsTooBig ); } public static Exception SerializationGraphIsTooDeep(IJsonReader reader, ulong maxDepth) { return new JsonSerializationException ( string.Format("Serialization graph is too deep. Maximum depth is {0}. Path: '{1}'", maxDepth, reader.Context.GetPath()), ErrorCode.SerializationGraphIsTooDeep) ; } public static Exception TypeIsNotValid(Type type, string problem) { problem = problem.TrimEnd('.'); return new JsonSerializationException ( string.Format("Type '{0}' is not valid for serialization: {1}.", type.Name, problem), ErrorCode.TypeIsNotValid ); } public static Exception SerializingUnknownType(Type type) { return new JsonSerializationException ( string.Format("Attempt to serialize unknown type '{0}' failed.", type.FullName), ErrorCode.SerializingUnknownType ); } public static Exception SerializingSpecialSystemType(Type type) { return new JsonSerializationException ( string.Format("Attempt to serialize special system type '{0}' failed. This type is could be serialized.", type.FullName), ErrorCode.SerializingSpecialSystemType ); } public static Exception UnexpectedEndOfStream(IJsonReader reader) { return new JsonSerializationException ( string.Format("Unexpected end of stream while more data is expected. Path: '{0}'.", reader.Context.GetPath()), ErrorCode.UnexpectedEndOfStream, reader ); } public static Exception UnexpectedMemberName(string memberName, string expected, IJsonReader reader) { return new JsonSerializationException ( string.Format("Unexpected member '{0}' is read while '{1}' is expected. Path: '{2}'.", memberName, expected, reader.Context.GetPath()), ErrorCode.UnexpectedMemberName, reader ); } public static Exception UnexpectedToken(IJsonReader reader, params JsonToken[] expectedTokens) { var tokensStr = default(string); if (expectedTokens.Length == 0) { tokensStr = ""; } else { #if NET40 tokensStr = String.Join(", ", expectedTokens); #else var tokens = expectedTokens.ConvertAll(c => c.ToString()); tokensStr = String.Join(", ", tokens); #endif } if (reader.Token == JsonToken.EndOfStream) { return UnexpectedEndOfStream(reader); } else if (expectedTokens.Length > 1) { return new JsonSerializationException ( string.Format("Unexpected token read '{0}' while any of '{1}' are expected. Path: '{2}'.", reader.Token, tokensStr, reader.Context.GetPath()), ErrorCode.UnexpectedToken, reader ); } else { return new JsonSerializationException ( string.Format("Unexpected token read '{0}' while '{1}' is expected. Path: '{2}'.", reader.Token, tokensStr, reader.Context.GetPath()), ErrorCode.UnexpectedToken, reader ); } } public static Exception UnknownEscapeSequence(string escape, IJsonReader reader) { return new JsonSerializationException ( string.Format("An unknown escape sequence '{0}' is read. Path: '{1}'.", escape, reader.Context.GetPath()), ErrorCode.UnknownEscapeSequence, reader ); } public static Exception SerializationFramesCorruption() { return new JsonSerializationException ( "Serialization frames are corrupted. Probably invalid Push/Pop sequence in TypeSerializers implementation.", ErrorCode.SerializationFramesCorruption ); } public static Exception StreamIsNotReadable() { return new JsonSerializationException ( "Can\'t perform deserialization from stream which doesn't support reading.", ErrorCode.StreamIsNotReadable ); } public static Exception StreamIsNotWriteable() { return new JsonSerializationException ( "Can\'t perform serialization to stream which doesn't support writing.", ErrorCode.StreamIsNotWriteable ); } public static Exception UnterminatedStringLiteral(IJsonReader reader) { return new JsonSerializationException ( string.Format("An unterminated string literal. Path: '{0}'.", reader.Context.GetPath()), ErrorCode.UnterminatedStringLiteral, reader ); } public static Exception UnknownNotation(IJsonReader reader, string notation) { return new JsonSerializationException ( string.Format("An unknown notation '{0}'. Path: '{1}'.", notation, reader.Context.GetPath()), ErrorCode.UnknownNotation, reader ); } public static Exception TypeRequiresCustomSerializer(Type type, Type typeSerializer) { return new JsonSerializationException ( string.Format("Type '{0}' can't be serialized by '{1}' and requires custom {2} registered in 'Json.DefaultSerializers'.", type.FullName, typeSerializer.Name, typeof(TypeSerializer).Name), ErrorCode.TypeRequiresCustomSerializer ); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonSerializationException.cs.meta ================================================ fileFormatVersion: 2 guid: 9216e69600364fd8a766d27f04f18701 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStreamReader.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.IO; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class JsonStreamReader : JsonReader { private readonly StreamReader reader; public JsonStreamReader(Stream stream, SerializationContext context, char[] buffer = null) : base(context, buffer) { if (stream == null) throw new ArgumentNullException("stream"); if (!stream.CanRead) throw JsonSerializationException.StreamIsNotReadable(); this.reader = new StreamReader(stream, context.Encoding); } protected override int FillBuffer(char[] buffer, int index) { if (buffer == null) throw new ArgumentNullException("buffer"); if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException("index"); var count = buffer.Length - index; if (count <= 0) return index; var read = this.reader.Read(buffer, index, count); return index + read; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStreamReader.cs.meta ================================================ fileFormatVersion: 2 guid: dba4bb2b43a34afe956f2fe58a1d435d timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStreamWriter.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.IO; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class JsonStreamWriter : JsonWriter { private readonly StreamWriter writer; public Stream Stream { get { return writer.BaseStream; } } public JsonStreamWriter(Stream stream, SerializationContext context, char[] buffer = null) : base(context, buffer) { if (stream == null) throw new ArgumentNullException("stream"); if (!stream.CanWrite) throw JsonSerializationException.StreamIsNotWriteable(); writer = new StreamWriter(stream, context.Encoding); } public override void Flush() { writer.Flush(); } public override void WriteJson(string jsonString) { if (jsonString == null) throw new ArgumentNullException("jsonString"); writer.Write(jsonString); this.CharactersWritten += jsonString.Length; } public override void WriteJson(char[] jsonString, int index, int charactersToWrite) { if (jsonString == null) throw new ArgumentNullException("jsonString"); if (index < 0 || index >= jsonString.Length) throw new ArgumentOutOfRangeException("index"); if (charactersToWrite < 0 || index + charactersToWrite > jsonString.Length) throw new ArgumentOutOfRangeException("charactersToWrite"); if (charactersToWrite == 0) return; writer.Write(jsonString, index, charactersToWrite); this.CharactersWritten += charactersToWrite; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStreamWriter.cs.meta ================================================ fileFormatVersion: 2 guid: ea2e81bf26c44034887aeb8f01489170 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStringBuilderReader.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Text; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class JsonStringBuilderReader : JsonReader { private readonly StringBuilder jsonString; private int position; public JsonStringBuilderReader(StringBuilder stringBuilder, SerializationContext context, char[] buffer = null) : base(context, buffer) { if (stringBuilder == null) throw new ArgumentNullException("str"); this.jsonString = stringBuilder; this.position = 0; } protected override int FillBuffer(char[] buffer, int index) { if (buffer == null) throw new ArgumentNullException("buffer"); if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException("index"); var block = Math.Min(this.jsonString.Length - position, buffer.Length - index); if (block <= 0) return index; jsonString.CopyTo(position, buffer, index, block); position += block; return index + block; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStringBuilderReader.cs.meta ================================================ fileFormatVersion: 2 guid: a7c8908a12104d4080e9a2979109faa3 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStringBuilderWriter.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Text; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class JsonStringBuilderWriter : JsonWriter { private readonly StringBuilder stringBuilder; public StringBuilder Builder { get { return stringBuilder; } } public JsonStringBuilderWriter(StringBuilder stringBuilder, SerializationContext context, char[] buffer = null) : base(context, buffer) { if (stringBuilder == null) throw new ArgumentNullException("builder"); this.stringBuilder = stringBuilder; } public override void Flush() { } public override void WriteJson(string jsonString) { if (jsonString == null) throw new ArgumentNullException("jsonString"); stringBuilder.Append(jsonString); this.CharactersWritten += jsonString.Length; } public override void WriteJson(char[] jsonString, int offset, int charactersToWrite) { if (jsonString == null) throw new ArgumentNullException("jsonString"); if (offset < 0 || offset >= jsonString.Length) throw new ArgumentOutOfRangeException("offset"); if (charactersToWrite < 0 || offset + charactersToWrite > jsonString.Length) throw new ArgumentOutOfRangeException("charactersToWrite"); if (charactersToWrite == 0) return; stringBuilder.Append(jsonString, offset, charactersToWrite); this.CharactersWritten += charactersToWrite; } public override string ToString() { return stringBuilder.ToString(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStringBuilderWriter.cs.meta ================================================ fileFormatVersion: 2 guid: ba0b2700618d44678074988497f6811f timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStringReader.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class JsonStringReader : JsonReader { private readonly string jsonString; private int position; public JsonStringReader(string jsonString, SerializationContext context, char[] buffer = null) : base(context, buffer) { if (jsonString == null) throw new ArgumentNullException("jsonString"); this.jsonString = jsonString; this.position = 0; } protected override int FillBuffer(char[] buffer, int index) { if (buffer == null) throw new ArgumentNullException("buffer"); if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException("index"); var block = Math.Min(this.jsonString.Length - this.position, buffer.Length - index); if (block <= 0) return index; this.jsonString.CopyTo(this.position, buffer, index, block); this.position += block; return index + block; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonStringReader.cs.meta ================================================ fileFormatVersion: 2 guid: 87d7e0b24d7741298d295c21eb4bdb01 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonTextReader.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.IO; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class JsonTextReader : JsonReader { private readonly TextReader reader; public JsonTextReader(TextReader reader, SerializationContext context, char[] buffer = null) : base(context, buffer) { if (reader == null) throw new ArgumentNullException("reader"); this.reader = reader; } protected override int FillBuffer(char[] buffer, int index) { if (buffer == null) throw new ArgumentNullException("buffer"); if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException("index"); var count = buffer.Length - index; if (count <= 0) return index; var read = this.reader.Read(buffer, index, count); return index + read; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonTextReader.cs.meta ================================================ fileFormatVersion: 2 guid: 17d87f5a6e50464b9a12adaeaeaf64aa timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonTextWriter.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.IO; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class JsonTextWriter : JsonWriter { private readonly TextWriter writer; private TextWriter Writer { get { return writer; } } public JsonTextWriter(TextWriter writer, SerializationContext context, char[] buffer = null) : base(context, buffer) { if (writer == null) throw new ArgumentNullException("writer"); this.writer = writer; } public override void Flush() { writer.Flush(); } public override void WriteJson(string jsonString) { if (jsonString == null) throw new ArgumentNullException("jsonString"); writer.Write(jsonString); this.CharactersWritten += jsonString.Length; } public override void WriteJson(char[] jsonString, int offset, int charactersToWrite) { if (jsonString == null) throw new ArgumentNullException("jsonString"); if (offset < 0 || offset >= jsonString.Length) throw new ArgumentOutOfRangeException("offset"); if (charactersToWrite < 0 || offset + charactersToWrite > jsonString.Length) throw new ArgumentOutOfRangeException("charactersToWrite"); if (charactersToWrite == 0) return; writer.Write(jsonString, offset, charactersToWrite); this.CharactersWritten += charactersToWrite; } public override string ToString() { return writer.ToString(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonTextWriter.cs.meta ================================================ fileFormatVersion: 2 guid: 68aac15450ed45c78a7b616ff202c96d timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonToken.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public enum JsonToken { None = 0, BeginArray, EndOfArray, BeginObject, EndOfObject, Member, Number, StringLiteral, DateTime, Null, Boolean, EndOfStream } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonToken.cs.meta ================================================ fileFormatVersion: 2 guid: db426eb135514e37ab199c140e9de479 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonUtils.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Text; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { internal static class JsonUtils { internal static readonly long UnixEpochTicks = new DateTime(0x7b2, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks; private static readonly char[] ZerBuff = new char[] {'0', '0', '0', '0', '0', '0', '0', '0',}; private static readonly char[] HexChar = "0123456789ABCDEF".ToCharArray(); public static string UnescapeAndUnquote(string stringToUnescape) { if (stringToUnescape == null) throw new ArgumentNullException("stringToUnescape"); var start = 0; var len = stringToUnescape.Length; if (stringToUnescape.Length > 0 && stringToUnescape[0] == '"') { start += 1; len -= 2; } return UnescapeBuffer(stringToUnescape.ToCharArray(), start, len); } public static string EscapeAndQuote(string stringToEscape) { if (stringToEscape == null) throw new ArgumentNullException("stringToEscape"); var stringHasNonLatinCharacters = false; var newSize = stringToEscape.Length + 2; for (var i = 0; i < stringToEscape.Length; i++) { var charToCheck = stringToEscape[i]; var isNonLatinOrSpecial = ((int) charToCheck < 32 || charToCheck == '\\' || charToCheck == '"'); if (isNonLatinOrSpecial) newSize += 5; // encoded characters add 4 hex symbols and "u" stringHasNonLatinCharacters = stringHasNonLatinCharacters || isNonLatinOrSpecial; } // if it"s a latin - write as is if (!stringHasNonLatinCharacters) return string.Concat("\"", stringToEscape, "\""); // else tranform and write var sb = new StringBuilder(newSize); var hexBuff = new char[12]; // 4 for zeroes and 8 for number sb.Append('"'); for (var i = 0; i < stringToEscape.Length; i++) { var charToCheck = stringToEscape[i]; if ((int) charToCheck < 32 || charToCheck == '\\' || charToCheck == '"') { sb.Append("\\u"); Buffer.BlockCopy(ZerBuff, 0, hexBuff, 0, sizeof (char)*8); // clear buffer with "0" var hexlen = UInt32ToHexBuffer((uint) charToCheck, hexBuff, 4); sb.Append(hexBuff, hexlen, 4); } else sb.Append(charToCheck); } sb.Append('"'); return sb.ToString(); } public static int EscapeBuffer(string value, ref int offset, char[] outputBuffer, int outputBufferOffset) { if (value == null) throw new ArgumentNullException("value"); if (offset < 0 || offset >= value.Length) throw new ArgumentOutOfRangeException("offset"); if (outputBuffer == null) throw new ArgumentNullException("outputBuffer"); if (outputBufferOffset < 0 || outputBufferOffset >= outputBuffer.Length) throw new ArgumentOutOfRangeException("outputBufferOffset"); const ushort LOWER_BOUND_CHAR = 32; const ushort QUOTE_CHAR = '\\'; const ushort DOUBLE_QUOTE_CHAR = '"'; var written = 0; for (; offset < value.Length; offset++) { var charCode = (ushort) value[offset]; if (charCode < LOWER_BOUND_CHAR || charCode == QUOTE_CHAR || charCode == DOUBLE_QUOTE_CHAR) { if (outputBuffer.Length - outputBufferOffset < 6) return written; outputBuffer[outputBufferOffset++] = '\\'; outputBuffer[outputBufferOffset++] = 'u'; outputBufferOffset += UInt16ToPaddedHexBuffer(charCode, outputBuffer, outputBufferOffset); written += 6; } else { if (outputBuffer.Length - outputBufferOffset == 0) return written; // dont escape outputBuffer[outputBufferOffset++] = (char) charCode; written++; } } return written; } public static string UnescapeBuffer(char[] charsToUnescape, int start, int length) { if (charsToUnescape == null) throw new ArgumentNullException("charsToUnescape"); if (start < 0 || start + length > charsToUnescape.Length) throw new ArgumentOutOfRangeException("start"); var sb = new StringBuilder(length); var plainStart = start; var plainLen = 0; var end = start + length; for (var i = start; i < end; i++) { var ch = charsToUnescape[i]; if (ch == '\\') { var seqLength = 1; // append unencoded chunk if (plainLen != 0) { sb.Append(charsToUnescape, plainStart, plainLen); plainLen = 0; } var seqKind = charsToUnescape[i + 1]; switch (seqKind) { case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 'b': sb.Append('\b'); break; case 'f': sb.Append('\f'); break; case 't': sb.Append('\t'); break; case '\\': sb.Append('\\'); break; case '\'': sb.Append('\''); break; case '\"': sb.Append('\"'); break; // unicode symbol case 'u': sb.Append((char) HexStringToUInt32(charsToUnescape, i + 2, 4)); seqLength = 5; break; // latin hex encoded symbol case 'x': sb.Append((char) HexStringToUInt32(charsToUnescape, i + 2, 2)); seqLength = 3; break; // latin dec encoded symbol case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': sb.Append((char) StringToInt32(charsToUnescape, i + 1, 3)); seqLength = 3; break; default: #if STRICT throw new Exceptions.UnknownEscapeSequence("\\" + seqKind.ToString(), null); #else sb.Append(charsToUnescape[i + 1]); break; #endif } // set next chunk start right after this escape plainStart = i + seqLength + 1; i += seqLength; } else plainLen++; } // append last unencoded chunk if (plainLen != 0) sb.Append(charsToUnescape, plainStart, plainLen); return sb.ToString(); } public static uint HexStringToUInt32(char[] buffer, int start, int len) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); const uint ZERO = (ushort) '0'; const uint a = (ushort) 'a'; const uint A = (ushort) 'A'; var result = 0u; for (var i = 0; i < len; i++) { var c = buffer[start + i]; var d = 0u; if (c >= '0' && c <= '9') d = (c - ZERO); else if (c >= 'a' && c <= 'f') d = 10u + (c - a); else if (c >= 'A' && c <= 'F') d = 10u + (c - A); else throw new FormatException(); result = 16u*result + d; } return result; } public static int UInt32ToHexBuffer(uint uvalue, char[] buffer, int start) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); var hex = HexChar; if (uvalue == 0) { buffer[start] = '0'; return 1; } var length = 0; for (var i = 0; i < 8; i++) { var c = hex[((uvalue >> i*4) & 15u)]; buffer[start + i] = c; } for (length = 8; length > 0; length--) if (buffer[start + length - 1] != '0') break; Array.Reverse(buffer, start, length); return length; } public static int UInt16ToPaddedHexBuffer(ushort uvalue, char[] buffer, int start) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); const int LENGTH = 4; const string HEX = "0123456789ABCDEF"; var end = start + LENGTH; if (uvalue == 0) { for (var i = start; i < end; i++) buffer[i] = '0'; return LENGTH; } for (var i = 0; i < LENGTH; i++) { var c = HEX[(int) ((uvalue >> i*4) & 15u)]; buffer[end - i - 1] = c; } return LENGTH; } public static ushort PaddedHexStringToUInt16(char[] buffer, int start, int len) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); const uint ZERO = (ushort) '0'; const uint a = (ushort) 'a'; const uint A = (ushort) 'A'; var result = 0u; for (var i = 0; i < len; i++) { var c = buffer[start + i]; var d = 0u; if (c >= '0' && c <= '9') d = (c - ZERO); else if (c >= 'a' && c <= 'f') d = 10u + (c - a); else if (c >= 'A' && c <= 'F') d = 10u + (c - A); else throw new FormatException(); result = 16u*result + d; } return checked((ushort) result); } public static long StringToInt64(char[] buffer, int start, int len, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); const ulong ZERO = (ushort) '0'; var result = 0UL; var neg = false; for (var i = 0; i < len; i++) { var c = buffer[start + i]; if (i == 0 && c == '-') { neg = true; continue; } else if (c < '0' || c > '9') throw new FormatException(); result = checked(10UL*result + (c - ZERO)); } if (neg) return -(long) (result); else return (long) result; } public static int StringToInt32(char[] buffer, int start, int len, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); const uint ZERO = (ushort) '0'; var result = 0u; var neg = false; for (var i = 0; i < len; i++) { var c = buffer[start + i]; if (i == 0 && c == '-') { neg = true; continue; } else if (c < '0' || c > '9') throw new FormatException(); result = checked(10u*result + (c - ZERO)); } if (neg) return -(int) (result); else return (int) result; } public static ulong StringToUInt64(char[] buffer, int start, int len, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); const ulong ZERO = (ushort) '0'; var result = 0UL; for (var i = 0; i < len; i++) { var c = buffer[start + i]; if (c < '0' || c > '9') throw new FormatException(); result = checked(10UL*result + (c - ZERO)); } return result; } public static uint StringToUInt32(char[] buffer, int start, int len, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); const uint ZERO = (ushort) '0'; var result = 0U; for (var i = 0; i < len; i++) { var c = buffer[start + i]; if (c < '0' || c > '9') throw new FormatException(); result = checked(10*result + (c - ZERO)); } return result; } public static double StringToDouble(char[] buffer, int start, int len, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); /* const uint ZERO = (ushort)'0'; char decimalSep = '.'; var whole = 0UL; var fraction = 0U; var fracCount = 0; var neg = false; var decimals = false; for (var i = 0; i < len; i++) { var c = buffer[start + i]; if (i == 0 && c == '-') { neg = true; continue; } else if (c == decimalSep) { decimals = true; continue; } else if (c < '0' || c > '9') throw new FormatException(); if (decimals) { if (fracCount >= 9) // maximum precision 9 digits break; fraction = checked(10U * fraction + (c - ZERO)); fracCount++; } else whole = checked(10UL * whole + (c - ZERO)); } var result = checked((double)whole + (fraction / pow10d[fracCount])); if (neg) result = -result; return result; */ return double.Parse(new string(buffer, start, len), formatProvider); } public static float StringToFloat(char[] buffer, int start, int len, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); /* const uint ZERO = (ushort)'0'; char decimalSep = '.'; var whole = 0U; var fraction = 0U; var fracCount = 0; var neg = false; var decimals = false; for (var i = 0; i < len; i++) { var c = buffer[start + i]; if (i == 0 && c == '-') { neg = true; continue; } else if (c == decimalSep) { decimals = true; continue; } else if (c < '0' || c > '9') throw new FormatException(); if (decimals) { if (fracCount > 9) // maximum precision 9 digits break; fraction = checked(10U * fraction + (c - ZERO)); fracCount++; } else whole = checked(10U * whole + (c - ZERO)); } var result = checked((float)whole + (fraction / pow10s[fracCount])); if (neg) result = -result; return result; */ return float.Parse(new string(buffer, start, len), formatProvider); } public static decimal StringToDecimal(char[] buffer, int start, int len, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0) throw new ArgumentOutOfRangeException("start"); if (len < 0) throw new ArgumentOutOfRangeException("len"); if (start + len > buffer.Length) throw new ArgumentOutOfRangeException(); return decimal.Parse(new string(buffer, start, len), formatProvider); } public static int Int32ToBuffer(int value, char[] buffer, int start, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); const int ZERO = (ushort) '0'; var idx = start; var neg = value < 0; // Take care of sign var uvalue = neg ? (uint) (-value) : (uint) value; // Conversion. Number is reversed. do buffer[idx++] = (char) (ZERO + (uvalue%10)); while ((uvalue /= 10) != 0); if (neg) buffer[idx++] = '-'; var length = idx - start; // Reverse string Array.Reverse(buffer, start, length); return length; } public static int Int64ToBuffer(long value, char[] buffer, int start, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); const int ZERO = (ushort) '0'; var idx = start; // Take care of sign var neg = (value < 0); var uvalue = neg ? (ulong) (-value) : (ulong) value; // Conversion. Number is reversed. do buffer[idx++] = (char) (ZERO + (uvalue%10)); while ((uvalue /= 10) != 0); if (neg) buffer[idx++] = '-'; var length = idx - start; // Reverse string Array.Reverse(buffer, start, length); return length; } public static int UInt32ToBuffer(uint uvalue, char[] buffer, int start, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); const int ZERO = (ushort) '0'; var idx = start; // Take care of sign // Conversion. Number is reversed. do buffer[idx++] = (char) (ZERO + (uvalue%10)); while ((uvalue /= 10) != 0); var length = idx - start; // Reverse string Array.Reverse(buffer, start, length); return length; } public static int UInt64ToBuffer(ulong uvalue, char[] buffer, int start, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); const ulong ZERO = (ulong) '0'; var idx = start; // Conversion. Number is reversed. do buffer[idx++] = (char) (ZERO + (uvalue%10)); while ((uvalue /= 10) != 0UL); var length = idx - start; // Reverse string Array.Reverse(buffer, start, length); return length; } public static int SingleToBuffer(float value, char[] buffer, int start, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); var valueStr = value.ToString("R", formatProvider); valueStr.CopyTo(0, buffer, start, valueStr.Length); return valueStr.Length; } public static int DoubleToBuffer(double value, char[] buffer, int start, IFormatProvider formatProvider = null) { if (buffer == null) throw new ArgumentNullException("buffer"); if (start < 0 || start >= buffer.Length) throw new ArgumentOutOfRangeException("start"); var valueStr = value.ToString("R", formatProvider); valueStr.CopyTo(0, buffer, start, valueStr.Length); return valueStr.Length; } public static int DecimalToBuffer(decimal value, char[] buffer, int start, IFormatProvider formatProvider = null) { var valueStr = value.ToString(null, formatProvider); valueStr.CopyTo(0, buffer, start, valueStr.Length); return valueStr.Length; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonUtils.cs.meta ================================================ fileFormatVersion: 2 guid: 7b759d9c7f3a47f9ab3e9e6059870693 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonWriter.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Linq; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public abstract class JsonWriter : IJsonWriter { public const int DEFAULT_BUFFER_SIZE = 1024; [Flags] private enum Structure : byte { IsContainer = 0x1, IsObject = 0x2 | IsContainer, IsArray = 0x4 | IsContainer, IsStartOfStructure = 0x1 << 7, IsStartOfContainer = IsContainer | IsStartOfStructure } private const long JS_NUMBER_MAX_VALUE_INT64 = 9007199254740992L; private const ulong JS_NUMBER_MAX_VALUE_U_INT64 = 9007199254740992UL; private const double JS_NUMBER_MAX_VALUE_DOUBLE = 9007199254740992.0; private const double JS_NUMBER_MAX_VALUE_SINGLE = 9007199254740992.0f; private const decimal JS_NUMBER_MAX_VALUE_DECIMAL = 9007199254740992.0m; private static readonly char[] Tabs = new char[] { '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t' }; private static readonly char[] Newline = "\r\n".ToCharArray(); private static readonly char[] NameSeparator = ":".ToCharArray(); private static readonly char[] ValueSeparator = ",".ToCharArray(); private static readonly char[] ArrayBegin = "[".ToCharArray(); private static readonly char[] ArrayEnd = "]".ToCharArray(); private static readonly char[] ObjectBegin = "{".ToCharArray(); private static readonly char[] ObjectEnd = "}".ToCharArray(); private static readonly char[] Null = "null".ToCharArray(); private static readonly char[] True = "true".ToCharArray(); private static readonly char[] False = "false".ToCharArray(); private readonly Stack structStack; private readonly char[] buffer; public SerializationContext Context { get; private set; } public long CharactersWritten { get; protected set; } public int InitialPadding { get; set; } protected JsonWriter(SerializationContext context, char[] buffer = null) { if (context == null) throw new ArgumentNullException("context"); if (buffer != null && buffer.Length < 1024) throw new ArgumentOutOfRangeException("buffer", "Buffer should be at least 1024 bytes long."); this.Context = context; this.buffer = buffer ?? new char[DEFAULT_BUFFER_SIZE]; this.structStack = new Stack(10); } public abstract void Flush(); public abstract void WriteJson(string jsonString); public abstract void WriteJson(char[] jsonString, int offset, int charactersToWrite); public void Write(string value) { if (value == null) { this.WriteNull(); return; } this.WriteFormatting(JsonToken.StringLiteral); var len = value.Length; var offset = 0; this.buffer[0] = '"'; this.WriteJson(this.buffer, 0, 1); while (offset < len) { var writtenInBuffer = JsonUtils.EscapeBuffer(value, ref offset, this.buffer, 0); this.WriteJson(this.buffer, 0, writtenInBuffer); } this.buffer[0] = '"'; this.WriteJson(this.buffer, 0, 1); } public void Write(JsonMember member) { this.WriteFormatting(JsonToken.Member); if (member.IsEscapedAndQuoted) { if (member.NameString != null) this.WriteJson(member.NameString); else this.WriteJson(member.NameChars, 0, member.NameChars.Length); } else { if (member.NameString != null) this.WriteString(member.NameString); else this.WriteString(new string(member.NameChars)); this.WriteJson(NameSeparator, 0, NameSeparator.Length); } } public void Write(int number) { this.WriteFormatting(JsonToken.Number); var len = JsonUtils.Int32ToBuffer(number, this.buffer, 0, this.Context.Format); this.WriteJson(this.buffer, 0, len); } public void Write(uint number) { this.WriteFormatting(JsonToken.Number); var len = JsonUtils.UInt32ToBuffer(number, this.buffer, 0, this.Context.Format); this.WriteJson(this.buffer, 0, len); } public void Write(long number) { this.WriteFormatting(JsonToken.Number); var len = JsonUtils.Int64ToBuffer(number, this.buffer, 0, this.Context.Format); if (number > JS_NUMBER_MAX_VALUE_INT64) this.WriteString(new string(this.buffer, 0, len)); else this.WriteJson(this.buffer, 0, len); } public void Write(ulong number) { this.WriteFormatting(JsonToken.Number); var len = JsonUtils.UInt64ToBuffer(number, this.buffer, 0, this.Context.Format); if (number > JS_NUMBER_MAX_VALUE_U_INT64) this.WriteString(new string(this.buffer, 0, len)); else this.WriteJson(this.buffer, 0, len); } public void Write(float number) { this.WriteFormatting(JsonToken.Number); var len = JsonUtils.SingleToBuffer(number, this.buffer, 0, this.Context.Format); if (number > JS_NUMBER_MAX_VALUE_SINGLE) this.WriteString(new string(this.buffer, 0, len)); else this.WriteJson(this.buffer, 0, len); } public void Write(double number) { this.WriteFormatting(JsonToken.Number); var len = JsonUtils.DoubleToBuffer(number, this.buffer, 0, this.Context.Format); if (number > JS_NUMBER_MAX_VALUE_DOUBLE) this.WriteString(new string(this.buffer, 0, len)); else this.WriteJson(this.buffer, 0, len); } public void Write(decimal number) { this.WriteFormatting(JsonToken.Number); var len = JsonUtils.DecimalToBuffer(number, this.buffer, 0, this.Context.Format); if (number > JS_NUMBER_MAX_VALUE_DECIMAL) this.WriteString(new string(this.buffer, 0, len)); else this.WriteJson(this.buffer, 0, len); } public void Write(DateTime dateTime) { this.WriteFormatting(JsonToken.DateTime); if (dateTime.Kind == DateTimeKind.Unspecified) dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Utc); var dateTimeFormat = this.Context.DateTimeFormats.FirstOrDefault() ?? "o"; if (dateTimeFormat.IndexOf('z') >= 0 && dateTime.Kind != DateTimeKind.Local) dateTime = dateTime.ToLocalTime(); var dateString = dateTime.ToString(dateTimeFormat, this.Context.Format); this.Write(dateString); } public void Write(DateTimeOffset dateTimeOffset) { this.WriteFormatting(JsonToken.DateTime); var dateTimeFormat = this.Context.DateTimeFormats.FirstOrDefault() ?? "o"; var dateString = dateTimeOffset.ToString(dateTimeFormat, this.Context.Format); this.Write(dateString); } public void Write(bool value) { this.WriteFormatting(JsonToken.Boolean); if (value) this.WriteJson(True, 0, True.Length); else this.WriteJson(False, 0, False.Length); } public void WriteObjectBegin(int numberOfMembers) { this.WriteFormatting(JsonToken.BeginObject); this.structStack.Push(Structure.IsObject | Structure.IsStartOfStructure); this.WriteJson(ObjectBegin, 0, ObjectBegin.Length); } public void WriteObjectEnd() { this.WriteFormatting(JsonToken.EndOfObject); this.structStack.Pop(); this.WriteNewlineAndPad(0); this.WriteJson(ObjectEnd, 0, ObjectEnd.Length); } public void WriteArrayBegin(int numberOfMembers) { this.WriteFormatting(JsonToken.BeginArray); this.structStack.Push(Structure.IsArray | Structure.IsStartOfStructure); this.WriteJson(ArrayBegin, 0, ArrayBegin.Length); } public void WriteArrayEnd() { this.WriteFormatting(JsonToken.EndOfArray); this.structStack.Pop(); this.WriteJson(ArrayEnd, 0, ArrayEnd.Length); } public void WriteNull() { this.WriteFormatting(JsonToken.Null); this.WriteJson(Null, 0, Null.Length); } public void Reset() { this.CharactersWritten = 0; this.structStack.Clear(); } private void WriteNewlineAndPad(int correction) { if ((this.Context.Options & SerializationOptions.PrettyPrint) != SerializationOptions.PrettyPrint) return; // add padings and linebreaks this.WriteJson(Newline, 0, Newline.Length); var tabs = this.structStack.Count + correction; while (tabs > 0) { this.WriteJson(Tabs, 0, Math.Min(tabs, Tabs.Length)); tabs -= Tabs.Length; } } private void WriteFormatting(JsonToken token) { if (this.structStack.Count <= 0) return; var stackPeek = this.structStack.Peek(); var isNotMemberValue = ((stackPeek & Structure.IsObject) != Structure.IsObject || token == JsonToken.Member); var isEndToken = token == JsonToken.EndOfArray || token == JsonToken.EndOfObject; if ((stackPeek & Structure.IsContainer) != Structure.IsContainer || !isNotMemberValue) return; // it's a begining of container we add padding and remove "is begining" flag if ((stackPeek & Structure.IsStartOfContainer) == Structure.IsStartOfContainer) { stackPeek = this.structStack.Pop(); this.structStack.Push(stackPeek ^ Structure.IsStartOfStructure); // revert "is begining" } // else if it's new array's value or new object's member put comman and padding else if (!isEndToken) this.WriteJson(ValueSeparator, 0, ValueSeparator.Length); // padding // pad only before member in object container(not before value, it's ugly) this.WriteNewlineAndPad(this.InitialPadding + (isEndToken ? -1 : 0)); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonWriter.cs.meta ================================================ fileFormatVersion: 2 guid: ada1b1f263fd495d816316581abc506d timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonWriterExtentions.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public static class JsonWriterExtentions { public static void WriteMember(this IJsonWriter writer, string memberName) { if (writer == null) throw new ArgumentNullException("writer"); if (memberName == null) throw new ArgumentNullException("memberName"); writer.Write((JsonMember)memberName); } public static void WriteDateTime(this IJsonWriter writer, DateTime date) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(date); } public static void WriteDateTime(this IJsonWriter writer, DateTime? date) { if (writer == null) throw new ArgumentNullException("writer"); if (date == null) writer.WriteNull(); else writer.Write(date.Value); } public static void WriteBoolean(this IJsonWriter writer, bool value) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(value); } public static void WriteBoolean(this IJsonWriter writer, bool? value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) writer.WriteNull(); else writer.Write(value.Value); } public static void WriteNumber(this IJsonWriter writer, byte number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, sbyte number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, short number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, ushort number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, int number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, uint number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, long number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, ulong number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, float number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, double number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, decimal number) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(number); } public static void WriteNumber(this IJsonWriter writer, byte? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, sbyte? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, short? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, ushort? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, int? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, uint? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, long? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, ulong? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, float? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, double? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteNumber(this IJsonWriter writer, decimal? number) { if (writer == null) throw new ArgumentNullException("writer"); if (number == null) writer.WriteNull(); else writer.Write(number.Value); } public static void WriteString(this IJsonWriter writer, string literal) { if (writer == null) throw new ArgumentNullException("writer"); if (literal == null) writer.WriteNull(); else writer.Write(literal); } public static void WriteValue(this IJsonWriter writer, object value, Type valueType) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) { writer.WriteNull(); return; } var actualValueType = value.GetType(); var serializer = writer.Context.GetSerializerForType(actualValueType); //var objectSerializer = serializer as ObjectSerializer; //if (objectSerializer != null && valueType == actualValueType) // objectSerializer.SuppressTypeInformation = true; // no need to write type information on when type is obvious serializer.Serialize(writer, value); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/JsonWriterExtentions.cs.meta ================================================ fileFormatVersion: 2 guid: 094b3892dad14bb1858f6e2f26adc4f7 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/BigEndianBitConverter.cs ================================================ // /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { /// /// Implementation of EndianBitConverter which converts to/from big-endian /// byte arrays. /// internal sealed class BigEndianBitConverter : EndianBitConverter { /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// /// /// Different computer architectures store data using different byte orders. "Big-endian" /// means the most significant byte is on the left end of a word. "Little-endian" means the /// most significant byte is on the right end of a word. /// /// true if this converter is little-endian, false otherwise. public override sealed bool IsLittleEndian() { return false; } /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// public override sealed Endianness Endianness { get { return Endianness.BigEndian; } } /// /// Copies the specified number of bytes from value to buffer, starting at index. /// /// The value to copy /// The number of bytes to copy /// The buffer to copy the bytes into /// The index to start at protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) { var endOffset = index + bytes - 1; for (var i = 0; i < bytes; i++) { buffer[endOffset - i] = unchecked((byte) (value & 0xff)); value = value >> 8; } } /// /// Returns a value built from the specified number of bytes from the given buffer, /// starting at index. /// /// The data in byte array format /// The first index to use /// The number of bytes to use /// The value built from the given bytes protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) { long ret = 0; for (var i = 0; i < bytesToConvert; i++) { ret = unchecked((ret << 8) | buffer[startIndex + i]); } return ret; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/BigEndianBitConverter.cs.meta ================================================ fileFormatVersion: 2 guid: d3103829396f4f9780c29cf3fdafe3d9 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/DefaultMsgPackExtensionTypeHandler.cs ================================================ using System; using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { public sealed class DefaultMessagePackExtensionTypeHandler : MessagePackExtensionTypeHandler { public const int EXTENSION_TYPE_TIMESTAMP = -1; public const int EXTENSION_TYPE_DATE_TIME = 40; public const int EXTENSION_TYPE_DATE_TIME_OFFSET = 41; public const int EXTENSION_TYPE_DECIMAL = 42; public const int EXTENSION_TYPE_GUID = 43; public const int GUID_SIZE = 16; public const int DECIMAL_SIZE = 16; public const int DATE_TIME_SIZE = 16; public const int DATE_TIME_OFFSET_SIZE = 16; private static readonly Type[] DefaultExtensionTypes = new[] { typeof(decimal), typeof(DateTime), typeof(DateTimeOffset), typeof(Guid), typeof(DateTimeOffset), typeof(MessagePackTimestamp) }; public static DefaultMessagePackExtensionTypeHandler Instance = new DefaultMessagePackExtensionTypeHandler(EndianBitConverter.Little); private readonly EndianBitConverter bitConverter; public override IEnumerable ExtensionTypes { get { return DefaultExtensionTypes; } } internal DefaultMessagePackExtensionTypeHandler(EndianBitConverter bitConverter) { if (bitConverter == null) throw new ArgumentNullException("bitConverter"); this.bitConverter = bitConverter; } /// public override bool TryRead(sbyte type, ArraySegment data, out object value) { if (data.Array == null) throw new ArgumentNullException("data"); value = default(object); switch (type) { case EXTENSION_TYPE_TIMESTAMP: unchecked { var seconds = 0L; var nanoSeconds = 0u; switch (data.Count) { case 4: seconds = this.bitConverter.ToInt32(data.Array, data.Offset); value = new MessagePackTimestamp(seconds, nanoSeconds); return true; case 8: var data64 = this.bitConverter.ToUInt64(data.Array, data.Offset); seconds = (int)(data64 & 0x00000003ffffffffL); nanoSeconds = (uint)(data64 >> 34 & uint.MaxValue); value = new MessagePackTimestamp(seconds, nanoSeconds); return true; case 12: nanoSeconds = this.bitConverter.ToUInt32(data.Array, data.Offset); seconds = this.bitConverter.ToInt64(data.Array, data.Offset + 4); value = new MessagePackTimestamp(seconds, nanoSeconds); return true; default: return false; } } case EXTENSION_TYPE_DATE_TIME: if (data.Count != DATE_TIME_SIZE) return false; var dateTime = new DateTime(this.bitConverter.ToInt64(data.Array, data.Offset + 1), (DateTimeKind)data.Array[data.Offset]); value = dateTime; return true; case EXTENSION_TYPE_DATE_TIME_OFFSET: if (data.Count != DATE_TIME_OFFSET_SIZE) return false; var offset = new TimeSpan(this.bitConverter.ToInt64(data.Array, data.Offset + 8)); var ticks = this.bitConverter.ToInt64(data.Array, data.Offset); var dateTimeOffset = new DateTimeOffset(ticks, offset); value = dateTimeOffset; return true; case EXTENSION_TYPE_DECIMAL: if (data.Count != DECIMAL_SIZE) return false; var decimalValue = this.bitConverter.ToDecimal(data.Array, data.Offset); value = decimalValue; return true; case EXTENSION_TYPE_GUID: if (data.Count != GUID_SIZE) return false; var buffer = data.Array; unchecked { var guidValue = new Guid ( (uint)(buffer[data.Offset + 3] << 24 | buffer[data.Offset + 2] << 16 | buffer[data.Offset + 1] << 8 | buffer[data.Offset + 0]), (ushort)(buffer[data.Offset + 5] << 8 | buffer[data.Offset + 4]), (ushort)(buffer[data.Offset + 7] << 8 | buffer[data.Offset + 6]), buffer[data.Offset + 8], buffer[data.Offset + 9], buffer[data.Offset + 10], buffer[data.Offset + 11], buffer[data.Offset + 12], buffer[data.Offset + 13], buffer[data.Offset + 14], buffer[data.Offset + 15] ); value = guidValue; return true; } default: return false; } } /// public override bool TryWrite(object value, out sbyte type, ref ArraySegment data) { if (value == null) { type = 0; return false; } else if (value is DateTime) { type = EXTENSION_TYPE_DATE_TIME; if (data.Array == null || data.Count < DATE_TIME_SIZE) data = new ArraySegment(new byte[DATE_TIME_SIZE]); var dateTime = (DateTime)(object)value; Array.Clear(data.Array, data.Offset, DATE_TIME_SIZE); this.bitConverter.CopyBytes(dateTime.Ticks, data.Array, data.Offset + 1); data.Array[data.Offset] = (byte)dateTime.Kind; if (data.Count != DATE_TIME_SIZE) data = new ArraySegment(data.Array, data.Offset, DATE_TIME_SIZE); return true; } else if (value is DateTimeOffset) { type = EXTENSION_TYPE_DATE_TIME_OFFSET; if (data.Array == null || data.Count < DATE_TIME_OFFSET_SIZE) data = new ArraySegment(new byte[DATE_TIME_OFFSET_SIZE]); var dateTimeOffset = (DateTimeOffset)(object)value; this.bitConverter.CopyBytes(dateTimeOffset.DateTime.Ticks, data.Array, data.Offset); this.bitConverter.CopyBytes(dateTimeOffset.Offset.Ticks, data.Array, data.Offset + 8); if (data.Count != DATE_TIME_OFFSET_SIZE) data = new ArraySegment(data.Array, data.Offset, DATE_TIME_OFFSET_SIZE); return true; } else if (value is decimal) { type = EXTENSION_TYPE_DECIMAL; if (data.Array == null || data.Count < DECIMAL_SIZE) data = new ArraySegment(new byte[DECIMAL_SIZE]); var number = (decimal)(object)value; this.bitConverter.CopyBytes(number, data.Array, data.Offset); if (data.Count != DECIMAL_SIZE) data = new ArraySegment(data.Array, data.Offset, DECIMAL_SIZE); return true; } else if (value is Guid) { type = EXTENSION_TYPE_GUID; var guid = (Guid)(object)value; data = new ArraySegment(guid.ToByteArray()); return true; } else if (value is MessagePackTimestamp) { type = EXTENSION_TYPE_TIMESTAMP; var timestamp = (MessagePackTimestamp)(object)value; unchecked { if (timestamp.Seconds <= int.MaxValue && timestamp.Seconds >= int.MinValue) { if (timestamp.NanoSeconds == 0) { const int TIMESTAMP_SIZE = 4; if (data.Array == null || data.Count < TIMESTAMP_SIZE) data = new ArraySegment(new byte[TIMESTAMP_SIZE]); // timestamp 32 this.bitConverter.CopyBytes((int)timestamp.Seconds, data.Array, data.Offset); if (data.Count != TIMESTAMP_SIZE) data = new ArraySegment(data.Array, data.Offset, TIMESTAMP_SIZE); } else { const int TIMESTAMP_SIZE = 8; if (data.Array == null || data.Count < TIMESTAMP_SIZE) data = new ArraySegment(new byte[TIMESTAMP_SIZE]); var data64 = ((ulong)timestamp.NanoSeconds << 34) | ((ulong)timestamp.Seconds & uint.MaxValue); // timestamp 64 this.bitConverter.CopyBytes(data64, data.Array, data.Offset); if (data.Count != TIMESTAMP_SIZE) data = new ArraySegment(data.Array, data.Offset, TIMESTAMP_SIZE); } } else { const int TIMESTAMP_SIZE = 12; if (data.Array == null || data.Count < TIMESTAMP_SIZE) data = new ArraySegment(new byte[TIMESTAMP_SIZE]); // timestamp 96 this.bitConverter.CopyBytes(timestamp.NanoSeconds, data.Array, data.Offset); this.bitConverter.CopyBytes(timestamp.Seconds, data.Array, data.Offset + 4); if (data.Count != TIMESTAMP_SIZE) data = new ArraySegment(data.Array, data.Offset, TIMESTAMP_SIZE); } } return true; } else { type = default(sbyte); return false; } } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/DefaultMsgPackExtensionTypeHandler.cs.meta ================================================ fileFormatVersion: 2 guid: 7ed197b91d8b44368f386ccbde8ee57f timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/EndianBitConverter.cs ================================================ // /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Runtime.InteropServices; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { /// /// Equivalent of System.BitConverter, but with either endianness. /// internal abstract class EndianBitConverter { #region Endianness of this converter /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// /// /// Different computer architectures store data using different byte orders. "Big-endian" /// means the most significant byte is on the left end of a word. "Little-endian" means the /// most significant byte is on the right end of a word. /// /// true if this converter is little-endian, false otherwise. public abstract bool IsLittleEndian(); /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// public abstract Endianness Endianness { get; } #endregion #region Factory properties private static readonly LittleEndianBitConverter little = new LittleEndianBitConverter(); /// /// Returns a little-endian bit converter instance. The same instance is /// always returned. /// public static LittleEndianBitConverter Little { get { return little; } } private static readonly BigEndianBitConverter big = new BigEndianBitConverter(); /// /// Returns a big-endian bit converter instance. The same instance is /// always returned. /// public static BigEndianBitConverter Big { get { return big; } } #endregion #region Double/primitive conversions /// /// Converts the specified double-precision floating point number to a /// 64-bit signed integer. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A 64-bit signed integer whose value is equivalent to value. public long DoubleToInt64Bits(double value) { return BitConverter.DoubleToInt64Bits(value); } /// /// Converts the specified 64-bit signed integer to a double-precision /// floating point number. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A double-precision floating point number whose value is equivalent to value. public double Int64BitsToDouble(long value) { return BitConverter.Int64BitsToDouble(value); } /// /// Converts the specified single-precision floating point number to a /// 32-bit signed integer. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A 32-bit signed integer whose value is equivalent to value. public int SingleToInt32Bits(float value) { return new Int32SingleUnion(value).AsInt32; } /// /// Converts the specified 32-bit signed integer to a single-precision floating point /// number. Note: the endianness of this converter does not /// affect the returned value. /// /// The number to convert. /// A single-precision floating point number whose value is equivalent to value. public float Int32BitsToSingle(int value) { return new Int32SingleUnion(value).AsSingle; } #endregion #region To(PrimitiveType) conversions /// /// Returns a Boolean value converted from one byte at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// true if the byte at startIndex in value is nonzero; otherwise, false. public bool ToBoolean(byte[] value, int startIndex) { CheckByteArgument(value, startIndex, 1); return BitConverter.ToBoolean(value, startIndex); } /// /// Returns a Unicode character converted from two bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A character formed by two bytes beginning at startIndex. public char ToChar(byte[] value, int startIndex) { return unchecked((char) (CheckedFromBytes(value, startIndex, 2))); } /// /// Returns a double-precision floating point number converted from eight bytes /// at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A double precision floating point number formed by eight bytes beginning at startIndex. public double ToDouble(byte[] value, int startIndex) { return Int64BitsToDouble(ToInt64(value, startIndex)); } /// /// Returns a single-precision floating point number converted from four bytes /// at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A single precision floating point number formed by four bytes beginning at startIndex. public float ToSingle(byte[] value, int startIndex) { return Int32BitsToSingle(ToInt32(value, startIndex)); } /// /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 16-bit signed integer formed by two bytes beginning at startIndex. public short ToInt16(byte[] value, int startIndex) { return unchecked((short) (CheckedFromBytes(value, startIndex, 2))); } /// /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 32-bit signed integer formed by four bytes beginning at startIndex. public int ToInt32(byte[] value, int startIndex) { return unchecked((int) (CheckedFromBytes(value, startIndex, 4))); } /// /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 64-bit signed integer formed by eight bytes beginning at startIndex. public long ToInt64(byte[] value, int startIndex) { return CheckedFromBytes(value, startIndex, 8); } /// /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 16-bit unsigned integer formed by two bytes beginning at startIndex. public ushort ToUInt16(byte[] value, int startIndex) { return unchecked((ushort) (CheckedFromBytes(value, startIndex, 2))); } /// /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 32-bit unsigned integer formed by four bytes beginning at startIndex. public uint ToUInt32(byte[] value, int startIndex) { return unchecked((uint) (CheckedFromBytes(value, startIndex, 4))); } /// /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A 64-bit unsigned integer formed by eight bytes beginning at startIndex. public ulong ToUInt64(byte[] value, int startIndex) { return unchecked((ulong) (CheckedFromBytes(value, startIndex, 8))); } /// /// Checks the given argument for validity. /// /// The byte array passed in /// The start index passed in /// The number of bytes required /// value is a null reference /// /// startIndex is less than zero or greater than the length of value minus bytesRequired. /// private static void CheckByteArgument(byte[] value, int startIndex, int bytesRequired) { if (value == null) { throw new ArgumentNullException("value"); } if (startIndex < 0 || startIndex > value.Length - bytesRequired) { throw new ArgumentOutOfRangeException("startIndex"); } } /// /// Checks the arguments for validity before calling FromBytes /// (which can therefore assume the arguments are valid). /// /// The bytes to convert after checking /// The index of the first byte to convert /// The number of bytes to convert /// private long CheckedFromBytes(byte[] value, int startIndex, int bytesToConvert) { CheckByteArgument(value, startIndex, bytesToConvert); return FromBytes(value, startIndex, bytesToConvert); } /// /// Convert the given number of bytes from the given array, from the given start /// position, into a long, using the bytes as the least significant part of the long. /// By the time this is called, the arguments have been checked for validity. /// /// The bytes to convert /// The index of the first byte to convert /// The number of bytes to use in the conversion /// The converted number protected abstract long FromBytes(byte[] value, int startIndex, int bytesToConvert); #endregion #region ToString conversions /// /// Returns a String converted from the elements of a byte array. /// /// An array of bytes. /// All the elements of value are converted. /// /// A String of hexadecimal pairs separated by hyphens, where each pair /// represents the corresponding element in value; for example, "7F-2C-4A". /// public static string ToString(byte[] value) { return BitConverter.ToString(value); } /// /// Returns a String converted from the elements of a byte array starting at a specified array position. /// /// An array of bytes. /// The starting position within value. /// The elements from array position startIndex to the end of the array are converted. /// /// A String of hexadecimal pairs separated by hyphens, where each pair /// represents the corresponding element in value; for example, "7F-2C-4A". /// public static string ToString(byte[] value, int startIndex) { return BitConverter.ToString(value, startIndex); } /// /// Returns a String converted from a specified number of bytes at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// The number of bytes to convert. /// The length elements from array position startIndex are converted. /// /// A String of hexadecimal pairs separated by hyphens, where each pair /// represents the corresponding element in value; for example, "7F-2C-4A". /// public static string ToString(byte[] value, int startIndex, int length) { return BitConverter.ToString(value, startIndex, length); } #endregion #region Decimal conversions /// /// Returns a decimal value converted from sixteen bytes /// at a specified position in a byte array. /// /// An array of bytes. /// The starting position within value. /// A decimal formed by sixteen bytes beginning at startIndex. public decimal ToDecimal(byte[] value, int startIndex) { // HACK: This always assumes four parts, each in their own endianness, // starting with the first part at the start of the byte array. // On the other hand, there's no real format specified... var parts = new int[4]; for (var i = 0; i < 4; i++) { parts[i] = ToInt32(value, startIndex + i*4); } return new decimal(parts); } /// /// Returns the specified decimal value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 16. public byte[] GetBytes(decimal value) { var bytes = new byte[16]; var parts = decimal.GetBits(value); for (var i = 0; i < 4; i++) { CopyBytesImpl(parts[i], 4, bytes, i*4); } return bytes; } /// /// Copies the specified decimal value into the specified byte array, /// beginning at the specified index. /// /// A character to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(decimal value, byte[] buffer, int index) { var parts = decimal.GetBits(value); for (var i = 0; i < 4; i++) { CopyBytesImpl(parts[i], 4, buffer, i*4 + index); } } #endregion #region GetBytes conversions /// /// Returns an array with the given number of bytes formed /// from the least significant bytes of the specified value. /// This is used to implement the other GetBytes methods. /// /// The value to get bytes for /// The number of significant bytes to return private byte[] GetBytes(long value, int bytes) { var buffer = new byte[bytes]; CopyBytes(value, bytes, buffer, 0); return buffer; } /// /// Returns the specified Boolean value as an array of bytes. /// /// A Boolean value. /// An array of bytes with length 1. public byte[] GetBytes(bool value) { return BitConverter.GetBytes(value); } /// /// Returns the specified Unicode character value as an array of bytes. /// /// A character to convert. /// An array of bytes with length 2. public byte[] GetBytes(char value) { return GetBytes(value, 2); } /// /// Returns the specified double-precision floating point value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(double value) { return GetBytes(DoubleToInt64Bits(value), 8); } /// /// Returns the specified 16-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 2. public byte[] GetBytes(short value) { return GetBytes(value, 2); } /// /// Returns the specified 32-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(int value) { return GetBytes(value, 4); } /// /// Returns the specified 64-bit signed integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(long value) { return GetBytes(value, 8); } /// /// Returns the specified single-precision floating point value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(float value) { return GetBytes(SingleToInt32Bits(value), 4); } /// /// Returns the specified 16-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 2. public byte[] GetBytes(ushort value) { return GetBytes(value, 2); } /// /// Returns the specified 32-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 4. public byte[] GetBytes(uint value) { return GetBytes(value, 4); } /// /// Returns the specified 64-bit unsigned integer value as an array of bytes. /// /// The number to convert. /// An array of bytes with length 8. public byte[] GetBytes(ulong value) { return GetBytes(unchecked((long) value), 8); } #endregion #region CopyBytes conversions /// /// Copies the given number of bytes from the least-specific /// end of the specified value into the specified byte array, beginning /// at the specified index. /// This is used to implement the other CopyBytes methods. /// /// The value to copy bytes for /// The number of significant bytes to copy /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into private void CopyBytes(long value, int bytes, byte[] buffer, int index) { if (buffer == null) { throw new ArgumentNullException("buffer", "Byte array must not be null"); } if (buffer.Length < index + bytes) { throw new ArgumentOutOfRangeException("Buffer not big enough for value"); } CopyBytesImpl(value, bytes, buffer, index); } /// /// Copies the given number of bytes from the least-specific /// end of the specified value into the specified byte array, beginning /// at the specified index. /// This must be implemented in concrete derived classes, but the implementation /// may assume that the value will fit into the buffer. /// /// The value to copy bytes for /// The number of significant bytes to copy /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into protected abstract void CopyBytesImpl(long value, int bytes, byte[] buffer, int index); /// /// Copies the specified Boolean value into the specified byte array, /// beginning at the specified index. /// /// A Boolean value. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(bool value, byte[] buffer, int index) { CopyBytes(value ? 1 : 0, 1, buffer, index); } /// /// Copies the specified Unicode character value into the specified byte array, /// beginning at the specified index. /// /// A character to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(char value, byte[] buffer, int index) { CopyBytes(value, 2, buffer, index); } /// /// Copies the specified double-precision floating point value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(double value, byte[] buffer, int index) { CopyBytes(DoubleToInt64Bits(value), 8, buffer, index); } /// /// Copies the specified 16-bit signed integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(short value, byte[] buffer, int index) { CopyBytes(value, 2, buffer, index); } /// /// Copies the specified 32-bit signed integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(int value, byte[] buffer, int index) { CopyBytes(value, 4, buffer, index); } /// /// Copies the specified 64-bit signed integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(long value, byte[] buffer, int index) { CopyBytes(value, 8, buffer, index); } /// /// Copies the specified single-precision floating point value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(float value, byte[] buffer, int index) { CopyBytes(SingleToInt32Bits(value), 4, buffer, index); } /// /// Copies the specified 16-bit unsigned integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(ushort value, byte[] buffer, int index) { CopyBytes(value, 2, buffer, index); } /// /// Copies the specified 32-bit unsigned integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(uint value, byte[] buffer, int index) { CopyBytes(value, 4, buffer, index); } /// /// Copies the specified 64-bit unsigned integer value into the specified byte array, /// beginning at the specified index. /// /// The number to convert. /// The byte array to copy the bytes into /// The first index into the array to copy the bytes into public void CopyBytes(ulong value, byte[] buffer, int index) { CopyBytes(unchecked((long) value), 8, buffer, index); } #endregion #region Private struct used for Single/Int32 conversions /// /// Union used solely for the equivalent of DoubleToInt64Bits and vice versa. /// [StructLayout(LayoutKind.Explicit)] private struct Int32SingleUnion { /// /// Int32 version of the value. /// [FieldOffset(0)] private readonly int i; /// /// Single version of the value. /// [FieldOffset(0)] private readonly float f; /// /// Creates an instance representing the given integer. /// /// The integer value of the new instance. internal Int32SingleUnion(int i) { this.f = 0; // Just to keep the compiler happy this.i = i; } /// /// Creates an instance representing the given floating point number. /// /// The floating point value of the new instance. internal Int32SingleUnion(float f) { this.i = 0; // Just to keep the compiler happy this.f = f; } /// /// Returns the value of the instance as an integer. /// internal int AsInt32 { get { return i; } } /// /// Returns the value of the instance as a floating point number. /// internal float AsSingle { get { return f; } } } #endregion } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/EndianBitConverter.cs.meta ================================================ fileFormatVersion: 2 guid: 8475bccc39284c8d88906b7f019de0e6 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/Endianness.cs ================================================ // /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { /// /// Endianness of a converter /// public enum Endianness { /// /// Little endian - least significant byte first /// LittleEndian, /// /// Big endian - most significant byte first /// BigEndian } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/Endianness.cs.meta ================================================ fileFormatVersion: 2 guid: e6d5923b4a01463885f062f2f8afa1ad timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/LittleEndianBitConverter.cs ================================================ // /* "Miscellaneous Utility Library" Software Licence Version 1.0 Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Jon Skeet and Marc Gravell. Contact skeet@pobox.com, or see http://www.pobox.com/~skeet/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The name "Miscellaneous Utility Library" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact skeet@pobox.com. 5. Products derived from this software may not be called "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library" appear in their name, without prior written permission of Jon Skeet. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { /// /// Implementation of EndianBitConverter which converts to/from little-endian /// byte arrays. /// internal sealed class LittleEndianBitConverter : EndianBitConverter { /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// /// /// Different computer architectures store data using different byte orders. "Big-endian" /// means the most significant byte is on the left end of a word. "Little-endian" means the /// most significant byte is on the right end of a word. /// /// true if this converter is little-endian, false otherwise. public override sealed bool IsLittleEndian() { return true; } /// /// Indicates the byte order ("endianess") in which data is converted using this class. /// public override sealed Endianness Endianness { get { return Endianness.LittleEndian; } } /// /// Copies the specified number of bytes from value to buffer, starting at index. /// /// The value to copy /// The number of bytes to copy /// The buffer to copy the bytes into /// The index to start at protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) { for (var i = 0; i < bytes; i++) { buffer[i + index] = unchecked((byte) (value & 0xff)); value = value >> 8; } } /// /// Returns a value built from the specified number of bytes from the given buffer, /// starting at index. /// /// The data in byte array format /// The first index to use /// The number of bytes to use /// The value built from the given bytes protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) { long ret = 0; for (var i = 0; i < bytesToConvert; i++) { ret = unchecked((ret << 8) | buffer[startIndex + bytesToConvert - 1 - i]); } return ret; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/LittleEndianBitConverter.cs.meta ================================================ fileFormatVersion: 2 guid: b48cfd0cc76d4563871e82c7d7a65db3 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackExtensionType.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using GameDevWare.Serialization.Serializers; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { /// /// Representation of extension types in Message Pack. This type is immutable by design /// [TypeSerializer(typeof(MsgPackExtensionTypeSerializer))] public sealed class MessagePackExtensionType : IEquatable, IComparable, IComparable { private static readonly byte[] EmptyBytes = new byte[0]; private readonly ArraySegment data; private readonly int hashCode; public int Length { get { return this.data.Count; } } public sbyte Type { get; private set; } // ReSharper disable PossibleNullReferenceException public byte this[int index] { get { return this.data.Array[this.data.Offset + index]; } } // ReSharper restore PossibleNullReferenceException public MessagePackExtensionType() { this.Type = 0; // generic binary this.data = new ArraySegment(EmptyBytes, 0, 0); } public MessagePackExtensionType(byte[] binaryData) : this(0, binaryData) { } public MessagePackExtensionType(sbyte type, byte[] binaryData) : this(type, new ArraySegment(binaryData, 0, binaryData.Length)) { } public MessagePackExtensionType(sbyte type, ArraySegment binaryData) { if (binaryData.Array == null) throw new ArgumentNullException("binaryData"); this.data = binaryData; this.Type = type; var buffer = this.data.Array ?? EmptyBytes; if (this.data.Count >= 4) { this.hashCode = unchecked(this.data.Count * 17 + BitConverter.ToInt32(buffer, this.data.Offset)); } else { this.hashCode = this.data.Count; for (var i = this.data.Offset; i < this.data.Offset + this.data.Count; i++) this.hashCode += unchecked(buffer[i] * 114); } } public void CopyTo(byte[] destination, int index, int bytesToCopy) { Buffer.BlockCopy(this.data.Array ?? EmptyBytes, this.data.Offset, destination, index, Math.Min(bytesToCopy, this.Length)); } public byte[] ToByteArray() { if (this.data.Offset != 0 || this.Length != this.data.Count) { var byteArray = new byte[this.Length]; Buffer.BlockCopy(this.data.Array ?? EmptyBytes, this.data.Offset, byteArray, 0, byteArray.Length); return byteArray; } return this.data.Array; } public ArraySegment ToArraySegment() { return this.data; } public string ToBase64() { return Convert.ToBase64String(this.data.Array ?? EmptyBytes, this.data.Offset, this.Length); } public override bool Equals(object obj) { return this.Equals(obj as MessagePackExtensionType); } public override int GetHashCode() { return this.hashCode; } public bool Equals(MessagePackExtensionType other) { if (other == null) return false; if (ReferenceEquals(this, other)) return true; if (this.Length != other.Length) return false; if (this.GetHashCode() != other.GetHashCode()) return false; for (var i = 0; i < this.Length; i++) { if (this[i] != other[i]) return false; } return true; } public int CompareTo(object obj) { return this.CompareTo(obj as MessagePackExtensionType); } public int CompareTo(MessagePackExtensionType other) { if (other == null) return 1; // wee need to align buffers with different sizes for (int i = 0, j = 0; i < this.Length || j < other.Length; i++, j++) { // we need offsets var io = this.Length - other.Length; var jo = other.Length - this.Length; // only negative offset is needed if (io > 0) io = 0; if (jo > 0) jo = 0; // get bytes with offsets var ib = i + io >= 0 ? this[i + io] : (byte)0; var jb = j + jo >= 0 ? other[j + jo] : (byte)0; // compare if (ib > jb) return 1; if (jb > ib) return -1; } return 0; } public static bool operator ==(MessagePackExtensionType a, MessagePackExtensionType b) { if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) return false; if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false; return a.Equals(b); } public static bool operator !=(MessagePackExtensionType a, MessagePackExtensionType b) { return !(a == b); } public static explicit operator byte[] (MessagePackExtensionType messagePackExtension) { if (messagePackExtension != null) return messagePackExtension.ToByteArray(); else return null; } public static explicit operator ArraySegment(MessagePackExtensionType messagePackExtension) { if (messagePackExtension == null) throw new ArgumentNullException("messagePackExtension"); return messagePackExtension.ToArraySegment(); } public override string ToString() { return Convert.ToBase64String(this.data.Array ?? EmptyBytes, this.data.Offset, Math.Min(this.Length, 64)) + ( this.Length > 64 ? "..." : ""); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackExtensionType.cs.meta ================================================ fileFormatVersion: 2 guid: fc357227a2b747739e24bfde7cc369fe timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackExtensionTypeHandler.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Linq; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { public abstract class MessagePackExtensionTypeHandler { public abstract IEnumerable ExtensionTypes { get; } public abstract bool TryRead(sbyte type, ArraySegment data, out object value); public abstract bool TryWrite(object value, out sbyte type, ref ArraySegment data); /// public override string ToString() { return string.Format("Extension Types: {0}", string.Join(", ", this.ExtensionTypes.Select(t => t.ToString()).ToArray())); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackExtensionTypeHandler.cs.meta ================================================ fileFormatVersion: 2 guid: 88fe3b8c87c14c01979834465a77bdd4 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackReader.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { public class MsgPackReader : IJsonReader { public const int DEFAULT_BUFFER_SIZE = 1024 * 8; private static readonly object TrueObject = true; private static readonly object FalseObject = false; internal class MsgPackValueInfo : IValueInfo { private readonly MsgPackReader reader; private object value; internal MsgPackValueInfo(MsgPackReader reader) { if (reader == null) throw new ArgumentNullException("reader"); this.reader = reader; } public JsonToken Token { get; private set; } public bool HasValue { get; private set; } public object Raw { get { return this.value; } set { this.value = value; this.HasValue = true; } } public Type Type { get { if (this.HasValue && this.value != null) return this.value.GetType(); else { switch (this.Token) { case JsonToken.BeginArray: return typeof(List); case JsonToken.Number: return typeof(double); case JsonToken.Member: case JsonToken.StringLiteral: return typeof(string); case JsonToken.DateTime: return typeof(DateTime); case JsonToken.Boolean: return typeof(bool); } return typeof(object); } } } public int LineNumber { get { return 0; } } public int ColumnNumber { get; private set; } public bool AsBoolean { get { return Convert.ToBoolean(this.Raw, this.reader.Context.Format); } } public byte AsByte { get { return Convert.ToByte(this.Raw, this.reader.Context.Format); } } public short AsInt16 { get { return Convert.ToInt16(this.Raw, this.reader.Context.Format); } } public int AsInt32 { get { return Convert.ToInt32(this.Raw, this.reader.Context.Format); } } public long AsInt64 { get { return Convert.ToInt64(this.Raw, this.reader.Context.Format); } } public sbyte AsSByte { get { return Convert.ToSByte(this.Raw, this.reader.Context.Format); } } public ushort AsUInt16 { get { return Convert.ToUInt16(this.Raw, this.reader.Context.Format); } } public uint AsUInt32 { get { return Convert.ToUInt32(this.Raw, this.reader.Context.Format); } } public ulong AsUInt64 { get { return Convert.ToUInt64(this.Raw, this.reader.Context.Format); } } public float AsSingle { get { return Convert.ToSingle(this.Raw, this.reader.Context.Format); } } public double AsDouble { get { return Convert.ToDouble(this.Raw, this.reader.Context.Format); } } public decimal AsDecimal { get { return Convert.ToDecimal(this.Raw, this.reader.Context.Format); } } public DateTime AsDateTime { get { if (this.Raw is DateTime) return (DateTime)this.Raw; else return DateTime.ParseExact(this.AsString, this.reader.Context.DateTimeFormats, this.reader.Context.Format, DateTimeStyles.AssumeUniversal); } } public string AsString { get { var raw = this.Raw; if (raw is string) return (string)raw; else if (raw is byte[]) return Convert.ToBase64String((byte[])raw); else return Convert.ToString(raw, this.reader.Context.Format); } } public void Reset() { this.value = null; this.HasValue = false; this.Token = JsonToken.None; this.ColumnNumber = 0; } public void SetValue(object rawValue, JsonToken token, int position) { this.HasValue = token == JsonToken.Boolean || token == JsonToken.DateTime || token == JsonToken.Member || token == JsonToken.Null || token == JsonToken.Number || token == JsonToken.StringLiteral; this.value = rawValue; this.Token = token; this.ColumnNumber = position; } public override string ToString() { if (this.HasValue) return Convert.ToString(this.value); else return ""; } } internal struct ClosingToken { public JsonToken Token; public long Counter; } private readonly Stream inputStream; private readonly byte[] buffer; private readonly EndianBitConverter bitConverter; private readonly Stack closingTokens; private int bufferOffset; private int bufferRead; private int bufferAvailable; private bool isEndOfStream; private int totalBytesRead; public SerializationContext Context { get; private set; } JsonToken IJsonReader.Token { get { if (this.Value.Token == JsonToken.None) this.NextToken(); if (this.isEndOfStream) return JsonToken.EndOfStream; return this.Value.Token; } } object IJsonReader.RawValue { get { if (this.Value.Token == JsonToken.None) this.NextToken(); return this.Value.Raw; } } IValueInfo IJsonReader.Value { get { if (this.Value.Token == JsonToken.None) this.NextToken(); return this.Value; } } internal MsgPackValueInfo Value { get; private set; } public MsgPackReader(Stream stream, SerializationContext context, Endianness endianness = Endianness.BigEndian, byte[] buffer = null) { if (stream == null) throw new ArgumentNullException("stream"); if (context == null) throw new ArgumentNullException("context"); if (!stream.CanRead) throw JsonSerializationException.StreamIsNotReadable(); if (buffer != null && buffer.Length < 1024) throw new ArgumentOutOfRangeException("buffer", "Buffer should be at least 1024 bytes long."); this.Context = context; this.inputStream = stream; this.buffer = buffer ?? new byte[DEFAULT_BUFFER_SIZE]; this.bufferOffset = 0; this.bufferRead = 0; this.bufferAvailable = 0; this.bitConverter = endianness == Endianness.BigEndian ? EndianBitConverter.Big : (EndianBitConverter)EndianBitConverter.Little; this.closingTokens = new Stack(); this.Value = new MsgPackValueInfo(this); } public bool NextToken() { this.Value.Reset(); if (this.closingTokens.Count > 0 && this.closingTokens.Peek().Counter == 0) { var closingToken = this.closingTokens.Pop(); this.Value.SetValue(null, closingToken.Token, this.totalBytesRead); this.DecrementClosingTokenCounter(); return true; } if (!this.ReadToBuffer(1, throwOnEos: false)) { this.isEndOfStream = true; this.Value.SetValue(null, JsonToken.EndOfStream, this.totalBytesRead); return false; } var pos = this.totalBytesRead; var formatValue = this.buffer[this.bufferOffset]; if (formatValue >= (byte)MsgPackType.FixArrayStart && formatValue <= (byte)MsgPackType.FixArrayEnd) { var arrayCount = formatValue - (byte)MsgPackType.FixArrayStart; this.closingTokens.Push(new ClosingToken { Token = JsonToken.EndOfArray, Counter = arrayCount + 1 }); this.Value.SetValue(null, JsonToken.BeginArray, pos); } else if (formatValue >= (byte)MsgPackType.FixStrStart && formatValue <= (byte)MsgPackType.FixStrEnd) { var strCount = formatValue - (byte)MsgPackType.FixStrStart; var strBytes = this.ReadBytes(strCount); var strValue = this.Context.Encoding.GetString(strBytes.Array, strBytes.Offset, strBytes.Count); var strTokenType = JsonToken.StringLiteral; if (this.closingTokens.Count > 0) { var closingToken = this.closingTokens.Peek(); if (closingToken.Token == JsonToken.EndOfObject && closingToken.Counter > 0 && closingToken.Counter % 2 == 0) strTokenType = JsonToken.Member; } this.Value.SetValue(strValue, strTokenType, pos); } else if (formatValue >= (byte)MsgPackType.FixMapStart && formatValue <= (byte)MsgPackType.FixMapEnd) { var mapCount = formatValue - (byte)MsgPackType.FixMapStart; this.closingTokens.Push(new ClosingToken { Token = JsonToken.EndOfObject, Counter = mapCount * 2 + 1 }); this.Value.SetValue(null, JsonToken.BeginObject, pos); } else if (formatValue >= (byte)MsgPackType.NegativeFixIntStart) { var value = unchecked((sbyte)formatValue); this.Value.SetValue(value, JsonToken.Number, pos); } else if (formatValue <= (byte)MsgPackType.PositiveFixIntEnd) { var value = unchecked((byte)formatValue); this.Value.SetValue(value, JsonToken.Number, pos); } else { switch ((MsgPackType)formatValue) { case MsgPackType.Nil: this.Value.SetValue(null, JsonToken.Null, pos); break; case MsgPackType.Array16: case MsgPackType.Array32: var arrayCount = 0L; if (formatValue == (int)MsgPackType.Array16) { this.ReadToBuffer(2, throwOnEos: true); arrayCount = this.bitConverter.ToUInt16(this.buffer, this.bufferOffset); } else if (formatValue == (int)MsgPackType.Array32) { this.ReadToBuffer(4, throwOnEos: true); arrayCount = this.bitConverter.ToUInt32(this.buffer, this.bufferOffset); } this.closingTokens.Push(new ClosingToken { Token = JsonToken.EndOfArray, Counter = arrayCount + 1 }); this.Value.SetValue(null, JsonToken.BeginArray, pos); break; case MsgPackType.Map16: case MsgPackType.Map32: var mapCount = 0L; if (formatValue == (int)MsgPackType.Map16) { this.ReadToBuffer(2, throwOnEos: true); mapCount = this.bitConverter.ToUInt16(this.buffer, this.bufferOffset); } else if (formatValue == (int)MsgPackType.Map32) { this.ReadToBuffer(4, throwOnEos: true); mapCount = this.bitConverter.ToUInt32(this.buffer, this.bufferOffset); } this.closingTokens.Push(new ClosingToken { Token = JsonToken.EndOfObject, Counter = mapCount * 2 + 1 }); this.Value.SetValue(null, JsonToken.BeginObject, pos); break; case MsgPackType.Str16: case MsgPackType.Str32: case MsgPackType.Str8: var strBytesCount = 0L; if (formatValue == (int)MsgPackType.Str8) { this.ReadToBuffer(1, throwOnEos: true); strBytesCount = this.buffer[this.bufferOffset]; } else if (formatValue == (int)MsgPackType.Str16) { this.ReadToBuffer(2, throwOnEos: true); strBytesCount = this.bitConverter.ToUInt16(this.buffer, this.bufferOffset); } else if (formatValue == (int)MsgPackType.Str32) { this.ReadToBuffer(4, throwOnEos: true); strBytesCount = this.bitConverter.ToUInt32(this.buffer, this.bufferOffset); } var strTokenType = JsonToken.StringLiteral; if (this.closingTokens.Count > 0) { var closingToken = this.closingTokens.Peek(); if (closingToken.Token == JsonToken.EndOfObject && closingToken.Counter > 0 && closingToken.Counter % 2 == 0) strTokenType = JsonToken.Member; } var strBytes = this.ReadBytes(strBytesCount); var strValue = this.Context.Encoding.GetString(strBytes.Array, strBytes.Offset, strBytes.Count); this.Value.SetValue(strValue, strTokenType, pos); break; case MsgPackType.Bin32: case MsgPackType.Bin16: case MsgPackType.Bin8: var bytesCount = 0L; if (formatValue == (int)MsgPackType.Bin8) { this.ReadToBuffer(1, throwOnEos: true); bytesCount = this.buffer[this.bufferOffset]; } else if (formatValue == (int)MsgPackType.Bin16) { this.ReadToBuffer(2, throwOnEos: true); bytesCount = this.bitConverter.ToUInt16(this.buffer, this.bufferOffset); } else if (formatValue == (int)MsgPackType.Bin32) { this.ReadToBuffer(4, throwOnEos: true); bytesCount = this.bitConverter.ToUInt32(this.buffer, this.bufferOffset); } var bytes = this.ReadBytes(bytesCount, forceNewBuffer: true); this.Value.SetValue(bytes.Array, JsonToken.StringLiteral, pos); break; case MsgPackType.FixExt1: case MsgPackType.FixExt16: case MsgPackType.FixExt2: case MsgPackType.FixExt4: case MsgPackType.FixExt8: case MsgPackType.Ext32: case MsgPackType.Ext16: case MsgPackType.Ext8: var dataCount = 0L; if (formatValue == (int)MsgPackType.FixExt1) dataCount = 1; else if (formatValue == (int)MsgPackType.FixExt2) dataCount = 2; else if (formatValue == (int)MsgPackType.FixExt4) dataCount = 4; else if (formatValue == (int)MsgPackType.FixExt8) dataCount = 8; else if (formatValue == (int)MsgPackType.FixExt16) dataCount = 16; if (formatValue == (int)MsgPackType.Ext8) { this.ReadToBuffer(1, throwOnEos: true); dataCount = this.buffer[this.bufferOffset]; } else if (formatValue == (int)MsgPackType.Ext16) { this.ReadToBuffer(2, throwOnEos: true); dataCount = this.bitConverter.ToUInt16(this.buffer, this.bufferOffset); } else if (formatValue == (int)MsgPackType.Ext32) { this.ReadToBuffer(4, throwOnEos: true); dataCount = this.bitConverter.ToUInt32(this.buffer, this.bufferOffset); } this.ReadToBuffer(1, true); var extensionType = unchecked((sbyte)this.buffer[this.bufferOffset]); var data = this.ReadBytes(dataCount); this.Value.SetValue(this.ReadExtensionType(extensionType, data), JsonToken.StringLiteral, pos); break; case MsgPackType.False: this.Value.SetValue(FalseObject, JsonToken.Boolean, pos); break; case MsgPackType.True: this.Value.SetValue(TrueObject, JsonToken.Boolean, pos); break; case MsgPackType.Float32: this.ReadToBuffer(4, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToSingle(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.Float64: this.ReadToBuffer(8, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToDouble(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.Int8: this.ReadToBuffer(1, throwOnEos: true); this.Value.SetValue(unchecked((sbyte)this.buffer[this.bufferOffset]), JsonToken.Number, pos); break; case MsgPackType.Int16: this.ReadToBuffer(2, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToInt16(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.Int32: this.ReadToBuffer(4, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToInt32(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.Int64: this.ReadToBuffer(8, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToInt64(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.UInt8: this.ReadToBuffer(1, throwOnEos: true); this.Value.SetValue(this.buffer[this.bufferOffset], JsonToken.Number, pos); break; case MsgPackType.UInt16: this.ReadToBuffer(2, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToUInt16(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.UInt32: this.ReadToBuffer(4, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToUInt32(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.UInt64: this.ReadToBuffer(8, throwOnEos: true); this.Value.SetValue(this.bitConverter.ToUInt64(this.buffer, this.bufferOffset), JsonToken.Number, pos); break; case MsgPackType.PositiveFixIntStart: case MsgPackType.PositiveFixIntEnd: case MsgPackType.FixMapStart: case MsgPackType.FixMapEnd: case MsgPackType.FixArrayStart: case MsgPackType.FixArrayEnd: case MsgPackType.FixStrStart: case MsgPackType.FixStrEnd: case MsgPackType.Unused: case MsgPackType.NegativeFixIntStart: case MsgPackType.NegativeFixIntEnd: default: throw new UnknownMsgPackFormatException(formatValue); } } this.DecrementClosingTokenCounter(); return true; } public void Reset() { Array.Clear(this.buffer, 0, this.buffer.Length); this.bufferOffset = 0; this.bufferAvailable = 0; this.bufferRead = 0; this.totalBytesRead = 0; this.Value.Reset(); } public bool IsEndOfStream() { return this.isEndOfStream; } private bool ReadToBuffer(int bytesRequired, bool throwOnEos) { this.bufferAvailable -= this.bufferRead; this.bufferOffset += this.bufferRead; this.bufferRead = 0; if (this.bufferAvailable < bytesRequired) { if (this.bufferAvailable > 0) Buffer.BlockCopy(this.buffer, this.bufferOffset, this.buffer, 0, this.bufferAvailable); this.bufferOffset = 0; while (this.bufferAvailable < bytesRequired) { var read = this.inputStream.Read(this.buffer, this.bufferAvailable, this.buffer.Length - this.bufferAvailable); this.bufferAvailable += read; if (read != 0 || this.bufferAvailable >= bytesRequired) continue; if (throwOnEos) throw JsonSerializationException.UnexpectedEndOfStream(this); else return false; } } this.bufferRead = bytesRequired; this.totalBytesRead += bytesRequired; return true; } private ArraySegment ReadBytes(long bytesRequired, bool forceNewBuffer = false) { if (bytesRequired > int.MaxValue) throw new ArgumentOutOfRangeException("bytesRequired"); this.bufferAvailable -= this.bufferRead; this.bufferOffset += this.bufferRead; this.bufferRead = 0; if (this.bufferAvailable >= bytesRequired && !forceNewBuffer) { var bytes = new ArraySegment(this.buffer, this.bufferOffset, (int)bytesRequired); this.bufferAvailable -= (int)bytesRequired; this.bufferOffset += (int)bytesRequired; this.totalBytesRead += (int)bytesRequired; return bytes; } else { var bytes = new byte[bytesRequired]; var bytesOffset = 0; if (this.bufferAvailable > 0 && bytesOffset < bytes.Length) { var bytesToCopy = Math.Min(bytes.Length - bytesOffset, this.bufferAvailable); Buffer.BlockCopy(this.buffer, this.bufferOffset, bytes, bytesOffset, bytesToCopy); bytesOffset += bytesToCopy; this.bufferOffset += bytesToCopy; this.bufferAvailable -= bytesToCopy; this.totalBytesRead += bytesToCopy; } if (this.bufferAvailable == 0) this.bufferOffset = 0; while (bytesOffset < bytes.Length) { var read = this.inputStream.Read(bytes, bytesOffset, bytes.Length - bytesOffset); bytesOffset += read; this.totalBytesRead += read; if (read == 0 && bytesOffset < bytes.Length) throw JsonSerializationException.UnexpectedEndOfStream(this); } return new ArraySegment(bytes, 0, bytes.Length); } } private object ReadExtensionType(sbyte type, ArraySegment data) { var value = default(object); if (this.Context.ExtensionTypeHandler.TryRead(type, data, out value)) { return value; } else { if (ReferenceEquals(data.Array, this.buffer)) data = new ArraySegment((byte[])data.Array.Clone(), data.Offset, data.Count); return new MessagePackExtensionType(type, data); } } private void DecrementClosingTokenCounter() { if (this.closingTokens.Count > 0) { var closingToken = this.closingTokens.Pop(); closingToken.Counter--; this.closingTokens.Push(closingToken); } } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackReader.cs.meta ================================================ fileFormatVersion: 2 guid: 06480cbcfec44e6985c572e8f8c47f4d timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackTimestamp.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using GameDevWare.Serialization.Serializers; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { [TypeSerializer(typeof(MsgPackTimestampSerializer))] public struct MessagePackTimestamp : IEquatable, IComparable { public const int MAX_NANO_SECONDS = 999999999; public readonly long Seconds; public readonly uint NanoSeconds; public MessagePackTimestamp(long seconds, uint nanoSeconds) { if (nanoSeconds > MAX_NANO_SECONDS) nanoSeconds = MAX_NANO_SECONDS; this.Seconds = seconds; this.NanoSeconds = nanoSeconds; } public static explicit operator DateTime(MessagePackTimestamp timestamp) { return new DateTime(JsonUtils.UnixEpochTicks + ((TimeSpan)timestamp).Ticks, DateTimeKind.Unspecified); } public static explicit operator TimeSpan(MessagePackTimestamp timestamp) { return TimeSpan.FromSeconds(timestamp.Seconds) + TimeSpan.FromTicks(timestamp.NanoSeconds / 100); } /// public override int GetHashCode() { return unchecked(this.Seconds.GetHashCode() * 17 + this.NanoSeconds.GetHashCode()); } /// public override bool Equals(object obj) { if (obj is MessagePackTimestamp) return this.Equals((MessagePackTimestamp)obj); else return false; } /// public bool Equals(MessagePackTimestamp other) { return this.Seconds.Equals(other.Seconds) && this.NanoSeconds.Equals(other.NanoSeconds); } /// public int CompareTo(MessagePackTimestamp other) { var cmp = this.Seconds.CompareTo(other.Seconds); if (cmp != 0) return cmp; return this.NanoSeconds.CompareTo(other.NanoSeconds); } public static bool operator >(MessagePackTimestamp a, MessagePackTimestamp b) { return a.CompareTo(b) == 1; } public static bool operator <(MessagePackTimestamp a, MessagePackTimestamp b) { return a.CompareTo(b) == -1; } public static bool operator >=(MessagePackTimestamp a, MessagePackTimestamp b) { return a.CompareTo(b) != -1; } public static bool operator <=(MessagePackTimestamp a, MessagePackTimestamp b) { return a.CompareTo(b) != 1; } public static bool operator ==(MessagePackTimestamp a, MessagePackTimestamp b) { return a.Equals(b); } public static bool operator !=(MessagePackTimestamp a, MessagePackTimestamp b) { return !a.Equals(b); } /// public override string ToString() { return string.Format("seconds: {0}, nanoseconds: {1}", this.Seconds, this.NanoSeconds); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackTimestamp.cs.meta ================================================ fileFormatVersion: 2 guid: 075db24d85444948957bde92119a4fae timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackType.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { public enum MsgPackType : byte { PositiveFixIntStart = 0x00, PositiveFixIntEnd = 0x7f, FixMapStart = 0x80, FixMapEnd = 0x8f, FixArrayStart = 0x90, FixArrayEnd = 0x9f, FixStrStart = 0xa0, FixStrEnd = 0xbf, Nil = 0xc0, Unused = 0xc1, False = 0xc2, True = 0xc3, Bin8 = 0xc4, Bin16 = 0xc5, Bin32 = 0xc6, Ext8 = 0xc7, Ext16 = 0xc8, Ext32 = 0xc9, Float32 = 0xca, Float64 = 0xcb, UInt8 = 0xcc, UInt16 = 0xcd, UInt32 = 0xce, UInt64 = 0xcf, Int8 = 0xd0, Int16 = 0xd1, Int32 = 0xd2, Int64 = 0xd3, FixExt1 = 0xd4, FixExt2 = 0xd5, FixExt4 = 0xd6, FixExt8 = 0xd7, FixExt16 = 0xd8, Str8 = 0xd9, Str16 = 0xda, Str32 = 0xdb, Array16 = 0xdc, Array32 = 0xdd, Map16 = 0xde, Map32 = 0xdf, NegativeFixIntStart = 0xe0, NegativeFixIntEnd = 0xff } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackType.cs.meta ================================================ fileFormatVersion: 2 guid: b16da3c0c31c473aac9e7e69102866c7 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackWriter.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.IO; using System.Linq; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { public class MsgPackWriter : IJsonWriter { public const int DEFAULT_BUFFER_SIZE = 32; private readonly SerializationContext context; private readonly Stream outputStream; private readonly byte[] buffer; private readonly EndianBitConverter bitConverter; private long bytesWritten; public MsgPackWriter(Stream stream, SerializationContext context, byte[] buffer = null) { if (stream == null) throw new ArgumentNullException("stream"); if (context == null) throw new ArgumentNullException("context"); if (!stream.CanWrite) throw JsonSerializationException.StreamIsNotReadable(); if (buffer != null && buffer.Length < 32) throw new ArgumentOutOfRangeException("buffer", "Buffer should be at least 32 bytes long."); this.context = context; this.outputStream = stream; this.buffer = buffer ?? new byte[DEFAULT_BUFFER_SIZE]; this.bitConverter = EndianBitConverter.Big; this.bytesWritten = 0; } public SerializationContext Context { get { return this.context; } } public long CharactersWritten { get { return this.bytesWritten; } } public void Flush() { this.outputStream.Flush(); } public void Write(string value) { if (value == null) { this.WriteNull(); return; } var bytes = this.context.Encoding.GetBytes(value); if (bytes.Length < 32) { var formatByte = (byte)(bytes.Length | (byte)MsgPackType.FixStrStart); this.buffer[0] = formatByte; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (bytes.Length <= byte.MaxValue) { this.WriteType(MsgPackType.Str8); this.buffer[0] = (byte)bytes.Length; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (bytes.Length <= ushort.MaxValue) { this.WriteType(MsgPackType.Str16); this.bitConverter.CopyBytes((ushort)bytes.Length, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 2); this.bytesWritten += 2; } else { this.WriteType(MsgPackType.Str32); this.bitConverter.CopyBytes((uint)bytes.Length, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 4); this.bytesWritten += 4; } this.outputStream.Write(bytes, 0, bytes.Length); this.bytesWritten += bytes.Length; } public void Write(JsonMember value) { var name = value.NameString; if (value.IsEscapedAndQuoted) name = JsonUtils.UnescapeAndUnquote(name); this.WriteString(name); } public void Write(int number) { if (number >= -32 && number < 0) { var formatByte = unchecked((byte)number); this.buffer[0] = formatByte; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (number >= 0 && number < 128) { var formatByte = unchecked((byte)number); this.buffer[0] = formatByte; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (number <= sbyte.MaxValue && number >= sbyte.MinValue) { this.WriteType(MsgPackType.Int8); this.buffer[0] = unchecked((byte)(sbyte)number); this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (number <= short.MaxValue && number >= short.MinValue) { this.WriteType(MsgPackType.Int16); this.bitConverter.CopyBytes(checked((short)number), this.buffer, 0); this.outputStream.Write(this.buffer, 0, 2); this.bytesWritten += 2; } else { this.WriteType(MsgPackType.Int32); this.bitConverter.CopyBytes((int)number, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 4); this.bytesWritten += 4; } } public void Write(uint number) { if (number < 128) { var formatByte = checked((byte)number); this.buffer[0] = formatByte; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (number <= byte.MaxValue) { this.WriteType(MsgPackType.UInt8); this.buffer[0] = checked((byte)number); this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (number <= ushort.MaxValue) { this.WriteType(MsgPackType.UInt16); this.bitConverter.CopyBytes(checked((ushort)number), this.buffer, 0); this.outputStream.Write(this.buffer, 0, 2); this.bytesWritten += 2; } else { this.WriteType(MsgPackType.UInt32); this.bitConverter.CopyBytes((uint)number, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 4); this.bytesWritten += 4; } } public void Write(long number) { if (number <= int.MaxValue && number >= int.MinValue) { this.Write(checked((int)number)); return; } this.WriteType(MsgPackType.Int64); this.bitConverter.CopyBytes(number, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 8); this.bytesWritten += 8; } public void Write(ulong number) { if (number <= uint.MaxValue) { this.Write(checked((uint)number)); return; } this.WriteType(MsgPackType.UInt64); this.bitConverter.CopyBytes(number, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 8); this.bytesWritten += 8; } public void Write(float number) { this.WriteType(MsgPackType.Float32); this.bitConverter.CopyBytes(number, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 4); this.bytesWritten += 4; } public void Write(double number) { this.WriteType(MsgPackType.Float64); this.bitConverter.CopyBytes(number, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 8); this.bytesWritten += 8; } public void Write(decimal number) { var extensionData = this.GetWriteBuffer(); var extensionType = default(sbyte); if (this.context.ExtensionTypeHandler.TryWrite(number, out extensionType, ref extensionData)) { this.Write(extensionType, extensionData); } else { var decimalStr = number.ToString(null, this.context.Format); this.Write(decimalStr); } } public void Write(bool value) { if (value) this.WriteType(MsgPackType.True); else this.WriteType(MsgPackType.False); } public void Write(DateTime dateTime) { var extensionData = this.GetWriteBuffer(); var extensionType = default(sbyte); if (this.context.ExtensionTypeHandler.TryWrite(dateTime, out extensionType, ref extensionData)) { this.Write(extensionType, extensionData); } else { if (dateTime.Kind == DateTimeKind.Unspecified) dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Utc); var dateTimeFormat = this.Context.DateTimeFormats.FirstOrDefault() ?? "o"; if (dateTimeFormat.IndexOf('z') >= 0 && dateTime.Kind != DateTimeKind.Local) dateTime = dateTime.ToLocalTime(); var dateString = dateTime.ToString(dateTimeFormat, this.Context.Format); this.Write(dateString); } } public void Write(DateTimeOffset dateTimeOffset) { var extensionData = this.GetWriteBuffer(); var extensionType = default(sbyte); if (this.context.ExtensionTypeHandler.TryWrite(dateTimeOffset, out extensionType, ref extensionData)) { this.Write(extensionType, extensionData); } else { var dateTimeFormat = this.Context.DateTimeFormats.FirstOrDefault() ?? "o"; var dateString = dateTimeOffset.ToString(dateTimeFormat, this.Context.Format); this.Write(dateString); } } public void Write(byte[] value) { if (value == null) { this.WriteNull(); return; } if (value.Length < byte.MaxValue) { this.buffer[0] = (byte)MsgPackType.Bin8; this.buffer[1] = (byte)value.Length; this.outputStream.Write(this.buffer, 0, 2); this.bytesWritten += 2; } else if (value.Length < ushort.MaxValue) { this.buffer[0] = (byte)MsgPackType.Bin16; this.bitConverter.CopyBytes(checked((ushort)value.Length), this.buffer, 1); this.outputStream.Write(this.buffer, 0, 3); this.bytesWritten += 3; } else { this.buffer[0] = (byte)MsgPackType.Bin32; this.bitConverter.CopyBytes(checked((uint)value.Length), this.buffer, 1); this.outputStream.Write(this.buffer, 0, 5); this.bytesWritten += 5; } this.outputStream.Write(value, 0, value.Length); this.bytesWritten += value.Length; } public void Write(sbyte type, ArraySegment data) { if (data.Array == null) { this.WriteNull(); return; } if (data.Count == 1) { this.WriteType(MsgPackType.FixExt1); } else if (data.Count == 2) { this.WriteType(MsgPackType.FixExt2); } else if (data.Count == 4) { this.WriteType(MsgPackType.FixExt4); } else if (data.Count == 8) { this.WriteType(MsgPackType.FixExt8); } else if (data.Count == 16) { this.WriteType(MsgPackType.FixExt16); } else if (data.Count <= byte.MaxValue) { this.WriteType(MsgPackType.Ext8); this.buffer[0] = (byte)data.Count; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (data.Count <= ushort.MaxValue) { this.WriteType(MsgPackType.Ext16); this.bitConverter.CopyBytes((ushort)data.Count, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 2); this.bytesWritten += 2; } else { this.WriteType(MsgPackType.Ext32); this.bitConverter.CopyBytes((uint)data.Count, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 4); this.bytesWritten += 4; } // write extension type this.buffer[0] = unchecked((byte)type); this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; this.outputStream.Write(data.Array, data.Offset, data.Count); this.bytesWritten += data.Count; } public void WriteObjectBegin(int numberOfMembers) { if (numberOfMembers < 0) throw new ArgumentOutOfRangeException("numberOfMembers"); if (numberOfMembers < 16) { var formatByte = (byte)(numberOfMembers | (byte)MsgPackType.FixMapStart); this.buffer[0] = formatByte; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten += 1; } else if (numberOfMembers <= ushort.MaxValue) { this.WriteType(MsgPackType.Map16); this.bitConverter.CopyBytes((ushort)numberOfMembers, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 2); this.bytesWritten += 2; } else { this.WriteType(MsgPackType.Map32); this.bitConverter.CopyBytes((int)numberOfMembers, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 4); this.bytesWritten += 4; } } public void WriteObjectEnd() { } public void WriteArrayBegin(int numberOfMembers) { if (numberOfMembers < 0) throw new ArgumentOutOfRangeException("numberOfMembers"); if (numberOfMembers < 16) { var formatByte = (byte)(numberOfMembers | (byte)MsgPackType.FixArrayStart); this.buffer[0] = formatByte; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten++; } else if (numberOfMembers <= ushort.MaxValue) { this.WriteType(MsgPackType.Array16); this.bitConverter.CopyBytes((ushort)numberOfMembers, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 2); this.bytesWritten += 2; } else { this.WriteType(MsgPackType.Array32); this.bitConverter.CopyBytes((int)numberOfMembers, this.buffer, 0); this.outputStream.Write(this.buffer, 0, 4); this.bytesWritten += 4; } } public void WriteArrayEnd() { } public void WriteNull() { this.WriteType(MsgPackType.Nil); } private void WriteType(MsgPackType token) { this.buffer[0] = (byte)token; this.outputStream.Write(this.buffer, 0, 1); this.bytesWritten++; } public void WriteJson(string jsonString) { throw new NotSupportedException(); } public void WriteJson(char[] jsonString, int index, int charCount) { throw new NotSupportedException(); } public void Reset() { this.bytesWritten = 0; Array.Clear(this.buffer, 0, this.buffer.Length); } internal ArraySegment GetWriteBuffer() { return new ArraySegment(this.buffer, 16, this.buffer.Length - 16); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/MsgPackWriter.cs.meta ================================================ fileFormatVersion: 2 guid: 4baefb230851459abd8b2a393debe8fb timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/UnknownMsgPackExtentionTypeException.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Runtime.Serialization; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { [Serializable] public sealed class UnknownMsgPackExtentionTypeException : SerializationException { public UnknownMsgPackExtentionTypeException(string message, Exception innerException) : base(message, innerException) { } public UnknownMsgPackExtentionTypeException(string message) : base(message) { } public UnknownMsgPackExtentionTypeException(sbyte invalidExtType) : base(string.Format("Unknown MessagePack extention type '{0}' was readed from stream.", invalidExtType)) { } private UnknownMsgPackExtentionTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/UnknownMsgPackExtentionTypeException.cs.meta ================================================ fileFormatVersion: 2 guid: d57ba530cdc14db5b5cac872c64cdbde timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/UnknownMsgPackFormatException.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Runtime.Serialization; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.MessagePack { [Serializable] public sealed class UnknownMsgPackFormatException : SerializationException { public UnknownMsgPackFormatException(string message, Exception innerException) : base(message, innerException) { } public UnknownMsgPackFormatException(string message) : base(message) { } public UnknownMsgPackFormatException(byte invalidValue) : base(string.Format("Unknown MessagePack format '{0}' was readed from stream.", invalidValue)) { } #if !NETSTANDARD private UnknownMsgPackFormatException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack/UnknownMsgPackFormatException.cs.meta ================================================ fileFormatVersion: 2 guid: 2b91b6c0c5164e7482bd7806d504540b timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MessagePack.meta ================================================ fileFormatVersion: 2 guid: 6e931c62d9d54c0086adef8ca78c048b folderAsset: yes timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/DataMemberDescription.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.ComponentModel; using System.Linq; using System.Reflection; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Metadata { internal abstract class DataMemberDescription : MemberDescription { public abstract bool CanGet { get; } public abstract bool CanSet { get; } public object DefaultValue { get; private set; } public abstract Type ValueType { get; } protected DataMemberDescription(TypeDescription typeDescription, MemberInfo member) : base(typeDescription, member) { var defaultValue = (DefaultValueAttribute) this.GetAttributesOrEmptyList(typeof (DefaultValueAttribute)).FirstOrDefault(); if (defaultValue != null) this.DefaultValue = defaultValue.Value; } public abstract object GetValue(object target); public abstract void SetValue(object target, object value); } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/DataMemberDescription.cs.meta ================================================ fileFormatVersion: 2 guid: 1bc4a7488c8d43b785c5a2e7cf2ab6a5 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/FieldDescription.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Reflection; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Metadata { internal sealed class FieldDescription : DataMemberDescription { private readonly FieldInfo fieldInfo; private readonly Func getFn; private readonly Action setFn; public override bool CanGet { get { return true; } } public override bool CanSet { get { return this.fieldInfo.IsInitOnly == false; } } public override Type ValueType { get { return this.fieldInfo.FieldType; } } public FieldDescription(TypeDescription typeDescription, FieldInfo fieldInfo) : base(typeDescription, fieldInfo) { if (fieldInfo == null) throw new ArgumentNullException("fieldInfo"); this.fieldInfo = fieldInfo; MetadataReflection.TryGetMemberAccessFunc(fieldInfo, out this.getFn, out this.setFn); } public override object GetValue(object target) { if (!this.CanGet) throw new InvalidOperationException("Field is write-only."); if (this.getFn != null) return this.getFn(target); else return fieldInfo.GetValue(target); } public override void SetValue(object target, object value) { if (!this.CanSet) throw new InvalidOperationException("Field is read-only."); if (this.setFn != null) this.setFn(target, value); else this.fieldInfo.SetValue(target, value); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/FieldDescription.cs.meta ================================================ fileFormatVersion: 2 guid: 9cf871662e6c414a8cae05fdc578c14b timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/MemberDescription.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Metadata { internal abstract class MemberDescription { protected const string DATA_CONTRACT_ATTRIBUTE_NAME = "DataContractAttribute"; protected const string DATA_MEMBER_ATTRIBUTE_NAME = "DataMemberAttribute"; protected const string IGNORE_DATA_MEMBER_ATTRIBUTE_NAME = "IgnoreDataMemberAttribute"; private readonly string name; private readonly MemberInfo member; private readonly ReadOnlyCollection attributes; private readonly ILookup attributesByType; public MemberInfo Member { get { return this.member; } } public ReadOnlyCollection Attributes { get { return this.attributes; } } public string Name { get { return this.name; } } protected MemberDescription(TypeDescription typeDescription, MemberInfo member) { if (member == null) throw new ArgumentNullException("member"); this.member = member; this.name = member.Name; var attributesList = new List(); foreach (Attribute attr in member.GetCustomAttributes(true)) attributesList.Add(attr); if (typeDescription != null && typeDescription.IsDataContract) { var dataMemberAttribute = attributesList.FirstOrDefault(a => a.GetType().Name == DATA_MEMBER_ATTRIBUTE_NAME); if (dataMemberAttribute != null) this.name = ReflectionExtensions.GetDataMemberName(dataMemberAttribute) ?? this.name; } this.attributes = new ReadOnlyCollection(attributesList); this.attributesByType = attributesList.ToLookup(a => a.GetType()); } public bool HasAttributes(Type type) { if (type == null) throw new ArgumentNullException("type"); return this.attributesByType.Contains(type); } public IEnumerable GetAttributesOrEmptyList(Type type) { if (type == null) throw new ArgumentNullException("type"); return this.attributesByType[type]; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/MemberDescription.cs.meta ================================================ fileFormatVersion: 2 guid: 33506f0ce7e04e338380f997bb5fd861 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/MetadataReflection.cs ================================================ #if UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_4 || UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER #define UNITY #endif /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Metadata { internal static class MetadataReflection { /// /// Set to true to disable access optimizations (generated read/write access delegates) in AOT runtime. /// public static bool AotRuntime; private static readonly Dictionary> ReadFunctions; private static readonly Dictionary> WriteFunctions; private static readonly Dictionary> ConstructorFunctions; static MetadataReflection() { #if ((UNITY_WEBGL || UNITY_IOS || ENABLE_IL2CPP) && !UNITY_EDITOR) AotRuntime = true; #else try { // try compile expression Expression.Lambda>(Expression.Constant(true)).Compile(); #if (NET35 || UNITY) && !NET_STANDARD_2_0 var voidDynamicMethod = new DynamicMethod("TestVoidMethod", typeof(void), Type.EmptyTypes, restrictedSkipVisibility: true); var il = voidDynamicMethod.GetILGenerator(); il.Emit(OpCodes.Nop); voidDynamicMethod.CreateDelegate(typeof(Action)); #endif } catch { AotRuntime = true; } #endif ReadFunctions = new Dictionary>(); WriteFunctions = new Dictionary>(); ConstructorFunctions = new Dictionary>(); } public static bool TryGetMemberAccessFunc(MethodInfo getMethod, MethodInfo setMethod, out Func getFn, out Action setFn) { getFn = null; setFn = null; if (AotRuntime) return false; if (getMethod != null && !getMethod.IsStatic && getMethod.GetParameters().Length == 0) { lock (ReadFunctions) { if (ReadFunctions.TryGetValue(getMethod, out getFn) == false) { var instanceParam = Expression.Parameter(typeof(object), "instance"); var declaringType = getMethod.DeclaringType; Debug.Assert(declaringType != null, "getMethod.DeclaringType != null"); getFn = Expression.Lambda>( Expression.Convert( Expression.Call( Expression.Convert(instanceParam, declaringType), getMethod), typeof(object)), instanceParam ).Compile(); ReadFunctions.Add(getMethod, getFn); } } } if (setMethod != null && !setMethod.IsStatic && setMethod.GetParameters().Length == 1 && setMethod.DeclaringType != null && setMethod.DeclaringType.IsValueType == false) { lock (WriteFunctions) { if (WriteFunctions.TryGetValue(setMethod, out setFn) == false) { var instanceParam = Expression.Parameter(typeof(object), "instance"); var valueParam = Expression.Parameter(typeof(object), "value"); var declaringType = setMethod.DeclaringType; var valueType = setMethod.GetParameters()[0].ParameterType; Debug.Assert(declaringType != null, "setMethod.DeclaringType != null"); setFn = ((Expression>)Expression.Lambda(typeof(Action), Expression.Call( Expression.Convert(instanceParam, declaringType), setMethod, Expression.Convert(valueParam, valueType)), instanceParam, valueParam )).Compile(); WriteFunctions.Add(setMethod, setFn); } } } return true; } public static bool TryGetMemberAccessFunc(FieldInfo fieldInfo, out Func getFn, out Action setFn) { getFn = null; setFn = null; if (AotRuntime || fieldInfo.IsStatic) return false; lock (ReadFunctions) { if (ReadFunctions.TryGetValue(fieldInfo, out getFn) == false) { var instanceParam = Expression.Parameter(typeof(object), "instance"); var declaringType = fieldInfo.DeclaringType; Debug.Assert(declaringType != null, "getMethodDeclaringType != null"); getFn = Expression.Lambda>( Expression.Convert( Expression.Field( Expression.Convert(instanceParam, declaringType), fieldInfo), typeof(object)), instanceParam ).Compile(); ReadFunctions.Add(fieldInfo, getFn); } } if (fieldInfo.IsInitOnly == false && fieldInfo.DeclaringType != null && fieldInfo.DeclaringType.IsValueType == false) { lock (WriteFunctions) { if (WriteFunctions.TryGetValue(fieldInfo, out setFn) == false) { var declaringType = fieldInfo.DeclaringType; Debug.Assert(declaringType != null, "fieldInfo.DeclaringType != null"); var fieldType = fieldInfo.FieldType; #if (NET35 || UNITY) && !NET_STANDARD_2_0 var setDynamicMethod = new DynamicMethod(declaringType.FullName + "::" + fieldInfo.Name, typeof(void), new Type[] { typeof(object), typeof(object) }, restrictedSkipVisibility: true); var il = setDynamicMethod.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // instance il.Emit(OpCodes.Castclass, declaringType); il.Emit(OpCodes.Ldarg_1); // value if (fieldType.IsValueType) il.Emit(OpCodes.Unbox_Any, fieldType); else il.Emit(OpCodes.Castclass, fieldType); il.Emit(OpCodes.Stfld, fieldInfo); // call instance.Set(value) il.Emit(OpCodes.Ret); setFn = (Action)setDynamicMethod.CreateDelegate(typeof(Action)); #else var instanceParam = Expression.Parameter(typeof(object), "instance"); var valueParam = Expression.Parameter(typeof(object), "value"); setFn = ((Expression>)Expression.Lambda(typeof(Action), Expression.Assign( Expression.Field(Expression.Convert(instanceParam, declaringType), fieldInfo), Expression.Convert(valueParam, fieldType)), instanceParam, valueParam )).Compile(); #endif WriteFunctions.Add(fieldInfo, setFn); } } } return true; } public static bool TryGetConstructor(Type type, out Func ctrFn, out ConstructorInfo defaultConstructor) { if (type == null) throw new ArgumentNullException("type"); ctrFn = null; defaultConstructor = null; if (type.IsAbstract || type.IsInterface) return false; defaultConstructor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(ctr => ctr.GetParameters().Length == 0); if (AotRuntime && defaultConstructor != null) { return true; } if (defaultConstructor == null) return false; lock (ConstructorFunctions) { if (ConstructorFunctions.TryGetValue(type, out ctrFn)) return true; ctrFn = Expression.Lambda>( Expression.Convert( Expression.New(defaultConstructor), typeof(object)) ).Compile(); ConstructorFunctions.Add(type, ctrFn); } return true; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/MetadataReflection.cs.meta ================================================ fileFormatVersion: 2 guid: a818bb9057d9405a939b62a05497176a timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/PropertyDescription.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Reflection; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Metadata { internal sealed class PropertyDescription : DataMemberDescription { private readonly PropertyInfo propertyInfo; private readonly Func getFn; private readonly Action setFn; private readonly MethodInfo getMethod; private readonly MethodInfo setMethod; public override bool CanGet { get { return this.getMethod != null; } } public override bool CanSet { get { return this.setMethod != null; } } public override Type ValueType { get { return this.propertyInfo.PropertyType; } } public PropertyDescription(TypeDescription typeDescription, PropertyInfo propertyInfo) : base(typeDescription, propertyInfo) { if (propertyInfo == null) throw new ArgumentNullException("propertyInfo"); this.propertyInfo = propertyInfo; this.getMethod = propertyInfo.GetGetMethod(nonPublic: true); this.setMethod = propertyInfo.GetSetMethod(nonPublic: true); MetadataReflection.TryGetMemberAccessFunc(this.getMethod, this.setMethod, out this.getFn, out this.setFn); } public override object GetValue(object target) { if (!this.CanGet) throw new InvalidOperationException("Property is write-only."); if (this.getFn != null) return this.getFn(target); else return this.getMethod.Invoke(target, null); } public override void SetValue(object target, object value) { if (!this.CanSet) throw new InvalidOperationException("Property is read-only."); if (this.setFn != null) this.setFn(target, value); else this.setMethod.Invoke(target, new object[] { value }); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/PropertyDescription.cs.meta ================================================ fileFormatVersion: 2 guid: 6abfcb3759b145eaa0fe028ff0f8f3f2 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/TypeDescription.cs ================================================ #if UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_4 || UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER #define UNITY #endif /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; #if NET35 || UNITY using TypeInfo = System.Type; #endif // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Metadata { internal class TypeDescription : MemberDescription { private static readonly Dictionary TypeDescriptions = new Dictionary(); private static readonly object[] EmptyParameters = new object[0]; private readonly TypeInfo objectType; private readonly Func constructorFn; private readonly ConstructorInfo defaultConstructor; private readonly ReadOnlyCollection members; private readonly Dictionary membersByName; public TypeInfo ObjectType { get { return this.objectType; } } public bool IsAnonymousType { get; private set; } public bool IsEnumerable { get; private set; } public bool IsDictionary { get; private set; } public bool IsDataContract { get; private set; } public bool IsSerializable { get; private set; } public ReadOnlyCollection Members { get { return this.members; } } public TypeDescription(TypeInfo objectType) : base(null, objectType) { if (objectType == null) throw new ArgumentNullException("objectType"); this.objectType = objectType; this.IsDataContract = this.Attributes.Any(attribute => attribute.GetType().Name == DATA_CONTRACT_ATTRIBUTE_NAME); #if NETSTANDARD this.IsSerializable = this.objectType.GetCustomAttributes(typeof(SerializableAttribute), true).Any(); #else this.IsSerializable = objectType.IsSerializable; #endif this.IsEnumerable = this.objectType.IsInstantiationOf(typeof(Enumerable)) && this.objectType != typeof(string); this.IsDictionary = typeof(IDictionary).GetTypeInfo().IsAssignableFrom(this.objectType); this.IsAnonymousType = this.objectType.IsSealed && this.objectType.IsNotPublic && this.objectType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any(); var allMembers = this.FindMembers(this.objectType); this.members = allMembers.AsReadOnly(); this.membersByName = allMembers.ToDictionary(m => m.Name, StringComparer.Ordinal); MetadataReflection.TryGetConstructor(this.objectType, out this.constructorFn, out this.defaultConstructor); } private List FindMembers(TypeInfo objectType) { if (objectType == null) throw new ArgumentNullException("objectType"); var members = new List(); var memberNames = new HashSet(StringComparer.Ordinal); var isOptIn = objectType.GetCustomAttributes(false).Any(a => a.GetType().Name == DATA_CONTRACT_ATTRIBUTE_NAME); var searchFlags = BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | (isOptIn ? BindingFlags.NonPublic : 0); var properties = objectType.GetProperties(searchFlags); var fields = objectType.GetFields(searchFlags); foreach (var member in properties.Cast().Concat(fields.Cast())) { if (member is PropertyInfo && (member as PropertyInfo).GetIndexParameters().Length != 0) continue; var dataMemberAttribute = member.GetCustomAttributes(false).FirstOrDefault(a => a.GetType().Name == DATA_MEMBER_ATTRIBUTE_NAME); var ignoreMemberAttribute = member.GetCustomAttributes(false).FirstOrDefault(a => a.GetType().Name == IGNORE_DATA_MEMBER_ATTRIBUTE_NAME); if (isOptIn && dataMemberAttribute == null) continue; else if (!isOptIn && ignoreMemberAttribute != null) continue; var dataMember = default(DataMemberDescription); if (member is PropertyInfo) dataMember = new PropertyDescription(this, member as PropertyInfo); else if (member is FieldInfo) dataMember = new FieldDescription(this, member as FieldInfo); else throw new InvalidOperationException("Unknown member type. Should be PropertyInfo or FieldInfo."); if (string.IsNullOrEmpty(dataMember.Name)) throw JsonSerializationException.TypeIsNotValid(objectType, "has no members with empty name"); if (memberNames.Contains(dataMember.Name)) { var conflictingMember = members.First(m => m.Name == dataMember.Name); throw JsonSerializationException.TypeIsNotValid(objectType, string.Format("has no duplicate member's name '{0}' ('{1}.{2}' and '{3}.{4}')", dataMember.Name, conflictingMember.Member.DeclaringType.Name, conflictingMember.Member.Name, dataMember.Member.DeclaringType.Name, dataMember.Member.Name)); } members.Add(dataMember); memberNames.Add(dataMember.Name); } return members; } public bool TryGetMember(string name, out DataMemberDescription member) { return this.membersByName.TryGetValue(name, out member); } public object CreateInstance() { if (this.constructorFn != null) return this.constructorFn(); else if (this.defaultConstructor != null && !this.objectType.IsAbstract) return this.defaultConstructor.Invoke(EmptyParameters); else throw JsonSerializationException.CantCreateInstanceOfType(this.objectType); } public static TypeDescription Get(Type type) { if (type == null) throw new ArgumentNullException("type"); var typeInfo = type.GetTypeInfo(); lock (TypeDescriptions) { TypeDescription objectTypeDescription; if (!TypeDescriptions.TryGetValue(typeInfo, out objectTypeDescription)) TypeDescriptions.Add(typeInfo, objectTypeDescription = new TypeDescription(typeInfo)); return objectTypeDescription; } } public override string ToString() { return this.objectType.ToString(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/TypeDescription.cs.meta ================================================ fileFormatVersion: 2 guid: 81d49351045b455ba28308583aa6e6c7 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata.meta ================================================ fileFormatVersion: 2 guid: 75ef1581622b423ebeb7f42a7810d1a2 folderAsset: yes timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MsgPack.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.IO; using System.Text; using GameDevWare.Serialization.MessagePack; using GameDevWare.Serialization.Serializers; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public static class MsgPack { public static string[] DefaultDateTimeFormats { get { return Json.DefaultDateTimeFormats; } set { Json.DefaultDateTimeFormats = value; } } public static IFormatProvider DefaultFormat { get { return Json.DefaultFormat; } set { Json.DefaultFormat = value; } } public static Encoding DefaultEncoding { get { return Json.DefaultEncoding; } set { Json.DefaultEncoding = value; } } public static List DefaultSerializers { get { return Json.DefaultSerializers; } } public static MessagePackExtensionTypeHandler ExtensionTypeHandler { get; private set; } static MsgPack() { ExtensionTypeHandler = new DefaultMessagePackExtensionTypeHandler(EndianBitConverter.Big); } public static void Serialize(T objectToSerialize, Stream msgPackOutput) { Serialize(objectToSerialize, msgPackOutput, CreateDefaultContext(SerializationOptions.None)); } public static void Serialize(T objectToSerialize, Stream msgPackOutput, SerializationOptions options) { Serialize(objectToSerialize, msgPackOutput, CreateDefaultContext(options)); } public static void Serialize(T objectToSerialize, Stream msgPackOutput, SerializationContext context) { if (msgPackOutput == null) throw new ArgumentNullException("msgPackOutput"); if (context == null) throw new ArgumentNullException("context"); var writer = new MsgPackWriter(msgPackOutput, context); if (objectToSerialize == null) { writer.WriteNull(); writer.Flush(); return; } writer.WriteValue(objectToSerialize, typeof(T)); writer.Flush(); } public static object Deserialize(Type objectType, byte[] msgPackInput, int offset, int length) { if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); return Deserialize(objectType, new MemoryStream(msgPackInput, offset, length)); } public static object Deserialize(Type objectType, byte[] msgPackInput, int offset, int length, SerializationOptions options) { if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); return Deserialize(objectType, new MemoryStream(msgPackInput, offset, length), options); } public static object Deserialize(Type objectType, byte[] msgPackInput, int offset, int length, SerializationContext context) { if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); return Deserialize(objectType, new MemoryStream(msgPackInput, offset, length), context); } public static object Deserialize(Type objectType, Stream msgPackInput) { return Deserialize(objectType, msgPackInput, CreateDefaultContext(SerializationOptions.None)); } public static object Deserialize(Type objectType, Stream msgPackInput, SerializationOptions options) { return Deserialize(objectType, msgPackInput, CreateDefaultContext(options)); } public static object Deserialize(Type objectType, Stream msgPackInput, SerializationContext context) { if (objectType == null) throw new ArgumentNullException("objectType"); if (context == null) throw new ArgumentNullException("context"); if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); if (!msgPackInput.CanRead) throw JsonSerializationException.StreamIsNotReadable(); var reader = new MsgPackReader(msgPackInput, context); return reader.ReadValue(objectType, false); } public static T Deserialize(byte[] msgPackInput, int offset, int length) { if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); return Deserialize(new MemoryStream(msgPackInput, offset, length)); } public static T Deserialize(byte[] msgPackInput, int offset, int length, SerializationOptions options) { if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); return Deserialize(new MemoryStream(msgPackInput, offset, length), options); } public static T Deserialize(byte[] msgPackInput, int offset, int length, SerializationContext context) { if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); return Deserialize(new MemoryStream(msgPackInput, offset, length), context); } public static T Deserialize(Stream msgPackInput) { return Deserialize(msgPackInput, CreateDefaultContext(SerializationOptions.None)); } public static T Deserialize(Stream msgPackInput, SerializationOptions options) { return Deserialize(msgPackInput, CreateDefaultContext(options)); } public static T Deserialize(Stream msgPackInput, SerializationContext context) { if (context == null) throw new ArgumentNullException("context"); if (msgPackInput == null) throw new ArgumentNullException("msgPackInput"); if (!msgPackInput.CanRead) throw JsonSerializationException.StreamIsNotReadable(); return (T)Deserialize(typeof(T), msgPackInput, context); } private static SerializationContext CreateDefaultContext(SerializationOptions options) { return new SerializationContext { Options = options, EnumSerializerFactory = (enumType) => new EnumNumberSerializer(enumType) }; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/MsgPack.cs.meta ================================================ fileFormatVersion: 2 guid: 758e563fe3ff4497a4fb9f284c016ac4 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/PathSegment.cs ================================================ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public struct PathSegment { public readonly int Index; public readonly object MemberName; public PathSegment(int index) { if (index < 0) throw new ArgumentOutOfRangeException("index"); this.Index = index; this.MemberName = null; } public PathSegment(object memberName) { if (memberName == null) throw new ArgumentNullException("memberName"); this.Index = -1; this.MemberName = memberName; } /// public override string ToString() { if (this.Index >= 0) { return this.Index.ToString(); } else if (this.MemberName != null) { return this.MemberName.ToString(); } else { return string.Empty; } } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/PathSegment.cs.meta ================================================ fileFormatVersion: 2 guid: fcb712e5c13440d993fedb005d30dc78 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/ReflectionExtentions.cs ================================================ #if UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_4 || UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER #define UNITY #endif /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Reflection; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { internal static class ReflectionExtensions { private static readonly Dictionary GetNameMethods = new Dictionary(); private static readonly object[] EmptyArgs = new object[0]; public static bool IsInstantiationOf(this Type type, Type openGenericType) { if (type == null) throw new ArgumentNullException("type"); if (openGenericType == null) throw new ArgumentNullException("openGenericType"); if (openGenericType.IsGenericType && !openGenericType.IsGenericTypeDefinition) throw new ArgumentException(string.Format("Type should be open generic type '{0}'.", openGenericType)); var genericType = type; if (type.IsGenericType) { if (type.IsGenericType && !type.IsGenericTypeDefinition) genericType = type.GetGenericTypeDefinition(); if (genericType == openGenericType || genericType.IsSubclassOf(openGenericType)) return true; } // clean genericType = null; // check interfaces foreach (var interfc in type.GetInterfaces()) { genericType = interfc; if (!interfc.IsGenericType) continue; if (!interfc.IsGenericTypeDefinition) genericType = interfc.GetGenericTypeDefinition(); if (genericType == openGenericType || genericType.IsSubclassOf(openGenericType)) return true; } if (type.BaseType != null && type.BaseType != typeof (object)) return IsInstantiationOf(type.BaseType, openGenericType); return false; } public static bool HasMultipleInstantiations(this Type type, Type openGenericType) { if (type == null) throw new ArgumentNullException("type"); if (openGenericType == null) throw new ArgumentNullException("openGenericType"); if (openGenericType.IsGenericType && !openGenericType.IsGenericTypeDefinition) throw new ArgumentException(string.Format("Type should be open generic type '{0}'.", openGenericType)); // can't has multiple implementations of class if (!openGenericType.IsInterface) return false; var found = 0; var genericType = type; if (type.IsGenericType) { if (type.IsGenericType && !type.IsGenericTypeDefinition) genericType = type.GetGenericTypeDefinition(); if (genericType == openGenericType || genericType.IsSubclassOf(openGenericType)) found++; } // clean genericType = null; // check interfaces foreach (var interfc in type.GetInterfaces()) { genericType = interfc; if (!interfc.IsGenericType) continue; if (!interfc.IsGenericTypeDefinition) genericType = interfc.GetGenericTypeDefinition(); if (genericType == openGenericType || genericType.IsSubclassOf(openGenericType)) found++; } return found > 1; } public static Type[] GetInstantiationArguments(this Type type, Type openGenericType) { if (type == null) throw new ArgumentNullException("type"); if (openGenericType == null) throw new ArgumentNullException("openGenericType"); if (openGenericType.IsGenericType && !openGenericType.IsGenericTypeDefinition) throw new ArgumentException(string.Format("Type should be open generic type '{0}'.", openGenericType)); var genericType = type; if (type.IsGenericType) { if (type.IsGenericType && !type.IsGenericTypeDefinition) genericType = type.GetGenericTypeDefinition(); if (genericType == openGenericType || genericType.IsSubclassOf(openGenericType)) return type.GetGenericArguments(); } // clean genericType = null; // check interfaces foreach (var _interface in type.GetInterfaces()) { genericType = _interface; if (!_interface.IsGenericType) continue; if (!_interface.IsGenericTypeDefinition) genericType = _interface.GetGenericTypeDefinition(); if (genericType == openGenericType || genericType.IsSubclassOf(openGenericType)) return _interface.GetGenericArguments(); } if (type.BaseType != null && type.BaseType != typeof (object)) return GetInstantiationArguments(type.BaseType, openGenericType); return null; } public static string GetDataMemberName(object dataMemberAttribute) { if (dataMemberAttribute == null) throw new ArgumentNullException("dataMemberAttribute"); var type = dataMemberAttribute.GetType(); var getName = default(MethodInfo); lock (GetNameMethods) { if (!GetNameMethods.TryGetValue(type, out getName)) { getName = type.GetMethod("get_Name", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); if (getName == null || getName.ReturnType != typeof (string) || getName.GetParameters().Length != 0) getName = null; if (getName == null) { var getNameProperty = type.GetProperty("Name", BindingFlags.Instance | BindingFlags.Public, null, typeof (string), Type.EmptyTypes, null); if (getNameProperty != null) getName = getNameProperty.GetGetMethod(nonPublic: false); } GetNameMethods.Add(type, getName); } } if (getName != null) return (string) getName.Invoke(dataMemberAttribute, EmptyArgs); else return null; } #if NET35 || UNITY public static Type GetTypeInfo(this Type type) { return type; } #endif } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/ReflectionExtentions.cs.meta ================================================ fileFormatVersion: 2 guid: 5ce5b0d86a6b431f9a9542c4904b8e1e timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/SerializationContext.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Text; using GameDevWare.Serialization.MessagePack; using GameDevWare.Serialization.Serializers; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class SerializationContext { private readonly Dictionary serializers; private MessagePackExtensionTypeHandler extensionTypeHandler; public Stack Hierarchy { get; private set; } public Stack Path { get; private set; } public IFormatProvider Format { get; set; } public string[] DateTimeFormats { get; set; } public Encoding Encoding { get; set; } public Dictionary Serializers { get { return this.serializers; } set { if (value == null) throw new ArgumentNullException("value"); foreach (var kv in value) this.serializers[kv.Key] = kv.Value; } } public MessagePackExtensionTypeHandler ExtensionTypeHandler { get { return this.extensionTypeHandler; } set { if (value == null) throw new ArgumentNullException("value"); this.extensionTypeHandler = value; } } public SerializationOptions Options { get; set; } public Func ObjectSerializerFactory { get; set; } public Func EnumSerializerFactory { get; set; } public Func DictionarySerializerFactory { get; set; } public Func ArraySerializerFactory { get; set; } public Func SerializerFactory { get; set; } public SerializationContext() { this.Hierarchy = new Stack(); this.Path = new Stack(); this.Format = Json.DefaultFormat; this.DateTimeFormats = Json.DefaultDateTimeFormats; this.Encoding = Json.DefaultEncoding; this.ExtensionTypeHandler = MsgPack.ExtensionTypeHandler; this.serializers = Json.DefaultSerializers.ToDictionary(s => s.SerializedType); } public TypeSerializer GetSerializerForType(Type valueType) { if (valueType == null) throw new ArgumentNullException("valueType"); if (valueType.BaseType == typeof(MulticastDelegate) || valueType.BaseType == typeof(Delegate)) throw new InvalidOperationException(string.Format("Unable to serialize delegate type '{0}'.", valueType)); var serializer = default(TypeSerializer); if (this.serializers.TryGetValue(valueType, out serializer)) return serializer; var typeSerializerAttribute = valueType.GetCustomAttributes(typeof(TypeSerializerAttribute), inherit: false).FirstOrDefault() as TypeSerializerAttribute; if (typeSerializerAttribute != null) serializer = this.CreateCustomSerializer(valueType, typeSerializerAttribute); else if (valueType.IsEnum) serializer = this.CreateEnumSerializer(valueType); else if (typeof(IDictionary).IsAssignableFrom(valueType) || valueType.IsInstantiationOf(typeof(IDictionary<,>))) serializer = this.CreateDictionarySerializer(valueType); else if (valueType.IsArray || typeof(IEnumerable).IsAssignableFrom(valueType)) serializer = this.CreateArraySerializer(valueType); else serializer = (this.SerializerFactory != null ? this.SerializerFactory(valueType) : null) ?? this.CreateObjectSerializer(valueType); this.serializers.Add(valueType, serializer); return serializer; } private TypeSerializer CreateDictionarySerializer(Type valueType) { if (this.DictionarySerializerFactory != null) return this.DictionarySerializerFactory(valueType); else return new DictionarySerializer(valueType); } private TypeSerializer CreateEnumSerializer(Type valueType) { if (this.EnumSerializerFactory != null) return this.EnumSerializerFactory(valueType); else return new EnumSerializer(valueType); } private TypeSerializer CreateArraySerializer(Type valueType) { if (this.ArraySerializerFactory != null) return this.ArraySerializerFactory(valueType); else return new ArraySerializer(valueType); } private TypeSerializer CreateObjectSerializer(Type valueType) { if (this.ObjectSerializerFactory != null) return this.ObjectSerializerFactory(valueType); else return new ObjectSerializer(this, valueType); } private TypeSerializer CreateCustomSerializer(Type valueType, TypeSerializerAttribute typeSerializerAttribute) { var serializerType = typeSerializerAttribute.SerializerType; var typeCtr = serializerType.GetConstructor(new[] { typeof(Type) }); if (typeCtr != null) return (TypeSerializer)typeCtr.Invoke(new object[] { valueType }); var ctxTypeCtr = serializerType.GetConstructor(new[] { typeof(SerializationContext), typeof(Type) }); if (ctxTypeCtr != null) return (TypeSerializer)ctxTypeCtr.Invoke(new object[] { this, valueType }); var ctxCtr = serializerType.GetConstructor(new[] { typeof(SerializationContext) }); if (ctxCtr != null) return (TypeSerializer)ctxCtr.Invoke(new object[] { this }); return (TypeSerializer)Activator.CreateInstance(serializerType); } public Type GetType(string name, bool throwOnError, bool ignoreCase) { return Type.GetType(name, throwOnError, ignoreCase); } public Type GetType(string name, bool throwOnError) { return Type.GetType(name, throwOnError); } public Type GetType(string name) { return Type.GetType(name); } /// /// Reset serialization context for future re-use. Clears and collections. /// public void Reset() { this.Hierarchy.Clear(); this.Path.Clear(); } /// /// Get object hierarchy (arrays/objects) path to current reader position. /// /// public string GetPath() { var path = new StringBuilder(); foreach (var segment in this.Path.Reverse()) { var segmentString = segment.ToString(); if (string.IsNullOrEmpty(segmentString)) { continue; } path.Append(segmentString); path.Append("."); } if (path.Length > 0) { path.Length--; } return path.ToString(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/SerializationContext.cs.meta ================================================ fileFormatVersion: 2 guid: 8d8d56843e2f47c58582e1025a2587dc timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/SerializationOptions.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { [Flags] public enum SerializationOptions { None = 0, SuppressTypeInformation = 0x1 << 1, PrettyPrint = 0x1 << 2, } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/SerializationOptions.cs.meta ================================================ fileFormatVersion: 2 guid: 5838dd2a7ce14a40a9439645c1fe216f timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/ArraySerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class ArraySerializer : TypeSerializer { private readonly Type arrayType; private readonly Type instantiatedArrayType; private readonly Type elementType; public override Type SerializedType { get { return this.arrayType; } } public ArraySerializer(Type enumerableType) { if (enumerableType == null) throw new ArgumentNullException("enumerableType"); this.arrayType = this.instantiatedArrayType = enumerableType; this.elementType = this.GetElementType(arrayType); if (this.elementType == null) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "be enumerable"); if (this.arrayType == typeof(IList) || this.arrayType == typeof(ICollection) || this.arrayType == typeof(IEnumerable)) this.instantiatedArrayType = typeof(ArrayList); else if (arrayType.IsInterface && arrayType.IsGenericType && (arrayType.GetGenericTypeDefinition() == typeof(IList<>) || arrayType.GetGenericTypeDefinition() == typeof(ICollection<>) || arrayType.GetGenericTypeDefinition() == typeof(IEnumerable<>))) this.instantiatedArrayType = typeof(List<>).MakeGenericType(this.elementType); } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var container = new ArrayList(); if (reader.Token != JsonToken.BeginArray) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.BeginArray); reader.Context.Hierarchy.Push(container); var i = 0; while (reader.NextToken() && reader.Token != JsonToken.EndOfArray) { reader.Context.Path.Push(new PathSegment(i++)); var value = reader.ReadValue(this.elementType, false); container.Add(value); reader.Context.Path.Pop(); } reader.Context.Hierarchy.Pop(); if (reader.IsEndOfStream()) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.EndOfArray); if (this.instantiatedArrayType == typeof(ArrayList)) return container; else if (this.instantiatedArrayType.IsArray) return container.ToArray(this.elementType); else return Activator.CreateInstance(this.instantiatedArrayType, container.ToArray(this.elementType)); } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var size = 0; if (value is ICollection) size = ((ICollection)value).Count; else size = ((IEnumerable)value).Cast().Count(); writer.WriteArrayBegin(size); var i = 0; foreach (var item in (IEnumerable)value) { writer.Context.Path.Push(new PathSegment(i++)); writer.WriteValue(item, this.elementType); writer.Context.Path.Pop(); } writer.WriteArrayEnd(); } private Type GetElementType(Type arrayType) { if (arrayType == null) throw new ArgumentNullException("arrayType"); var elementType = (Type)null; if (arrayType.IsArray) { elementType = arrayType.GetElementType(); return elementType; } if (arrayType.IsInstantiationOf(typeof(IEnumerable<>))) { if (arrayType.HasMultipleInstantiations(typeof(IEnumerable<>))) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "have only one generic IEnumerable interface"); elementType = arrayType.GetInstantiationArguments(typeof(IEnumerable<>))[0]; } if (elementType == null && typeof(IEnumerable).IsAssignableFrom(arrayType)) elementType = typeof(object); else if (elementType == null) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "be enumerable"); return elementType; } public override string ToString() { return string.Format("array of {1}, {0}", this.arrayType, this.elementType); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/ArraySerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 06ca5f3ec70842a98c968db9509b25b9 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/BinarySerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using GameDevWare.Serialization.MessagePack; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class BinarySerializer : TypeSerializer { public static readonly BinarySerializer Instance = new BinarySerializer(); public override Type SerializedType { get { return typeof(byte[]); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; if (reader.RawValue is byte[]) { return reader.RawValue; } else { var value = reader.RawValue as string; if (value == null) return null; var buffer = Convert.FromBase64String(value); return buffer; } } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) { writer.WriteNull(); return; } if (value != null && value is byte[] == false) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "be array of bytes"); var bytes = (byte[])value; if (writer is MsgPackWriter) { ((MsgPackWriter)writer).Write(bytes); } else { var base64String = Convert.ToBase64String(bytes); writer.WriteString(base64String); } } public override string ToString() { return "byte[] as Base64"; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/BinarySerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 31ebab347e1749c3aecc94f559bcdc31 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/BoundsSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER using System; using UnityEngine; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class BoundsSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(Bounds); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var value = new Bounds(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); if (memberName == "center") value.center = (Vector3)reader.ReadValue(typeof(Vector3)); else if (memberName == "extents") value.extents = (Vector3)reader.ReadValue(typeof(Vector3)); else reader.ReadValue(typeof(object)); } reader.ReadObjectEnd(nextToken: false); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var bounds = (Bounds)value; writer.WriteObjectBegin(2); writer.WriteMember("center"); writer.WriteValue(bounds.center, typeof(Vector3)); writer.WriteMember("extents"); writer.WriteValue(bounds.extents, typeof(Vector3)); writer.WriteObjectEnd(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/BoundsSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: bc132289c33349ff9bd5abd3d12a75d6 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DateTimeOffsetSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Globalization; using System.Linq; using System.Runtime.Serialization; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class DateTimeOffsetSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(DateTimeOffset); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Value.Raw is DateTimeOffset) return reader.Value.Raw; else if (reader.Token == JsonToken.DateTime || reader.Value.Raw is DateTime) return new DateTimeOffset(reader.Value.AsDateTime); var dateTimeOffsetStr = reader.ReadString(false); try { var value = default(DateTimeOffset); if (!DateTimeOffset.TryParse(dateTimeOffsetStr, reader.Context.Format, DateTimeStyles.RoundtripKind, out value)) value = DateTimeOffset.ParseExact(dateTimeOffsetStr, reader.Context.DateTimeFormats, reader.Context.Format, DateTimeStyles.RoundtripKind); return value; } catch (FormatException fe) { throw new SerializationException(string.Format("Failed to parse date '{0}' in with pattern '{1}'.", dateTimeOffsetStr, reader.Context.DateTimeFormats[0]), fe); } } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var dateTimeOffset = (DateTimeOffset)value; writer.Write(dateTimeOffset); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DateTimeOffsetSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: c4ce03616aa546889bd2f53159324084 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DateTimeSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Globalization; using System.Linq; using System.Runtime.Serialization; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class DateTimeSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(DateTime); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.DateTime || reader.RawValue is DateTime) return reader.Value.AsDateTime; var dateTimeStr = reader.ReadString(false); try { var value = default(DateTime); if (!DateTime.TryParse(dateTimeStr, reader.Context.Format, DateTimeStyles.RoundtripKind, out value)) value = DateTime.ParseExact(dateTimeStr, reader.Context.DateTimeFormats, reader.Context.Format, DateTimeStyles.RoundtripKind); return value; } catch (FormatException fe) { throw new SerializationException(string.Format("Failed to parse date '{0}' in with pattern '{1}'.", dateTimeStr, reader.Context.DateTimeFormats[0]), fe); } } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var dataTime = (DateTime)value; writer.Write(dataTime); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DateTimeSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 313eb481f0f545958d48aa9bc511a541 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DictionaryEntrySerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections; using System.Runtime.Serialization; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class DictionaryEntrySerializer : TypeSerializer { public const string KEY_MEMBER_NAME = "Key"; public const string VALUE_MEMBER_NAME = "Value"; public override Type SerializedType { get { return typeof(DictionaryEntry); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.BeginArray) { var entry = new DictionaryEntry(); reader.ReadArrayBegin(); entry.Key = reader.ReadValue(typeof(object)); entry.Value = reader.ReadValue(typeof(object)); reader.ReadArrayEnd(nextToken: false); return entry; } else if (reader.Token == JsonToken.BeginObject) { var entry = new DictionaryEntry(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); switch (memberName) { case KEY_MEMBER_NAME: entry.Key = reader.ReadValue(typeof(object)); break; case VALUE_MEMBER_NAME: entry.Value = reader.ReadValue(typeof(object)); break; case ObjectSerializer.TYPE_MEMBER_NAME: reader.ReadValue(typeof(object)); break; default: throw new SerializationException(string.Format("Unknown member found '{0}' while '{1}' or '{2}' are expected.", memberName, KEY_MEMBER_NAME, VALUE_MEMBER_NAME)); } } reader.ReadObjectEnd(nextToken: false); return entry; } else { throw JsonSerializationException.UnexpectedToken(reader, JsonToken.BeginObject, JsonToken.BeginArray); } } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var entry = (DictionaryEntry)value; writer.WriteObjectBegin(2); writer.WriteMember(KEY_MEMBER_NAME); writer.WriteValue(entry.Key, typeof(object)); writer.WriteMember(VALUE_MEMBER_NAME); writer.WriteValue(entry.Value, typeof(object)); writer.WriteObjectEnd(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DictionaryEntrySerializer.cs.meta ================================================ fileFormatVersion: 2 guid: f4b2e86264a34d819b457cf3eebf2318 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DictionarySerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.Serialization; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class DictionarySerializer : TypeSerializer { private readonly Type dictionaryType; private readonly Type instantiatedDictionaryType; private readonly Type keyType; private readonly Type valueType; private readonly bool isStringKeyType; public override Type SerializedType { get { return this.dictionaryType; } } public DictionarySerializer(Type dictionaryType) { if (dictionaryType == null) throw new ArgumentNullException("dictionaryType"); this.dictionaryType = this.instantiatedDictionaryType = dictionaryType; this.keyType = typeof(object); this.valueType = typeof(object); if (dictionaryType.HasMultipleInstantiations(typeof(IDictionary<,>))) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "have only one generic IDictionary<,> interface"); if (dictionaryType.IsInstantiationOf(typeof(IDictionary<,>))) { var genArgs = dictionaryType.GetInstantiationArguments(typeof(IDictionary<,>)); this.keyType = genArgs[0]; this.valueType = genArgs[1]; if (dictionaryType.IsInterface && dictionaryType.IsGenericType && dictionaryType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) this.instantiatedDictionaryType = typeof(Dictionary<,>).MakeGenericType(genArgs); else if (typeof(IDictionary).IsAssignableFrom(dictionaryType) == false) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "should implement IDictionary interface"); } else if (typeof(IDictionary).IsAssignableFrom(dictionaryType)) { if (dictionaryType == typeof(IDictionary)) { this.instantiatedDictionaryType = typeof(IndexedDictionary); this.keyType = typeof(string); this.valueType = typeof(object); } } else { throw JsonSerializationException.TypeIsNotValid(this.GetType(), "should implement IDictionary interface"); } this.isStringKeyType = this.keyType == typeof(string); } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); var container = new List(); reader.Context.Hierarchy.Push(container); try { if (reader.Token == JsonToken.BeginArray) { reader.ReadArrayBegin(); while (reader.Token != JsonToken.EndOfArray) { var entry = default(DictionaryEntry); if (reader.Token == JsonToken.BeginArray) { reader.ReadArrayBegin(); try { entry.Key = reader.ReadValue(this.keyType); } catch (Exception e) { throw new SerializationException(string.Format("Failed to read '{0}' key of dictionary: {1}\r\nMore detailed information in inner exception.", this.keyType.Name, e.Message), e); } reader.Context.Path.Push(new PathSegment(entry.Key)); try { entry.Value = reader.ReadValue(this.valueType); } catch (Exception e) { throw new SerializationException(string.Format("Failed to read '{0}' value for key '{1}' in dictionary: {2}\r\nMore detailed information in inner exception.", this.valueType.Name, entry.Key, e.Message), e); } reader.Context.Path.Pop(); reader.ReadArrayEnd(); } else { reader.ReadObjectBegin(); var keyIsPushed = false; while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); switch (memberName) { case DictionaryEntrySerializer.KEY_MEMBER_NAME: try { entry.Key = reader.ReadValue(this.keyType); } catch (Exception e) { throw new SerializationException(string.Format("Failed to read '{0}' key of dictionary: {1}\r\nMore detailed information in inner exception.", this.keyType.Name, e.Message), e); } if (!keyIsPushed) { reader.Context.Path.Push(new PathSegment(entry.Key)); keyIsPushed = true; } break; case DictionaryEntrySerializer.VALUE_MEMBER_NAME: try { entry.Value = reader.ReadValue(this.valueType); } catch (Exception e) { throw new SerializationException(string.Format("Failed to read '{0}' value for key '{1}' in dictionary: {2}\r\nMore detailed information in inner exception.", this.valueType.Name, entry.Key ?? "", e.Message), e); } break; case ObjectSerializer.TYPE_MEMBER_NAME: reader.ReadValue(typeof(object)); break; default: throw new SerializationException(string.Format("Unknown member found '{0}' in dictionary entry while '{1}' or '{2}' are expected.", memberName, DictionaryEntrySerializer.KEY_MEMBER_NAME, DictionaryEntrySerializer.VALUE_MEMBER_NAME)); } } if (keyIsPushed) { reader.Context.Path.Pop(); } reader.ReadObjectEnd(); } container.Add(entry); } reader.ReadArrayEnd(nextToken: false); } else if (reader.Token == JsonToken.BeginObject) { reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var entry = default(DictionaryEntry); try { entry.Key = reader.ReadValue(this.keyType); } catch (Exception e) { throw new SerializationException(string.Format("Failed to read '{0}' key of dictionary: {1}\r\nMore detailed information in inner exception.", this.keyType.Name, e.Message), e); } reader.Context.Path.Push(new PathSegment(entry.Key)); try { entry.Value = reader.ReadValue(this.valueType); } catch (Exception e) { throw new SerializationException(string.Format("Failed to read '{0}' value for key '{1}' in dictionary: {2}\r\nMore detailed information in inner exception.", this.valueType.Name, entry.Key, e.Message), e); } reader.Context.Path.Pop(); container.Add(entry); } reader.ReadObjectEnd(nextToken: false); } else { throw JsonSerializationException.UnexpectedToken(reader, JsonToken.BeginObject, JsonToken.BeginArray); } var dictionary = (IDictionary)Activator.CreateInstance(this.instantiatedDictionaryType); foreach (var kv in container) { var key = kv.Key; var value = kv.Value; if (key.GetType() != this.keyType && this.keyType != typeof(object)) { if (this.keyType.IsEnum) key = Enum.Parse(this.keyType, Convert.ToString(key)); else key = Convert.ChangeType(key, this.keyType); } if (dictionary.Contains(key)) dictionary.Remove(key); dictionary.Add(key, value); } return dictionary; } finally { reader.Context.Hierarchy.Pop(); } } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var dictionary = (IDictionary)value; // ReSharper disable PossibleMultipleEnumeration writer.Context.Hierarchy.Push(value); // object if (this.isStringKeyType) { writer.WriteObjectBegin(dictionary.Count); foreach (DictionaryEntry pair in dictionary) { var keyStr = Convert.ToString(pair.Key, writer.Context.Format); writer.Context.Path.Push(new PathSegment(keyStr)); // key writer.WriteMember(keyStr); // value writer.WriteValue(pair.Value, this.valueType); writer.Context.Path.Pop(); } writer.WriteObjectEnd(); } else { writer.WriteArrayBegin(dictionary.Count); foreach (DictionaryEntry pair in dictionary) { writer.Context.Path.Push(new PathSegment(pair.Key)); writer.WriteArrayBegin(2); writer.WriteValue(pair.Key, this.keyType); writer.WriteValue(pair.Value, this.valueType); writer.WriteArrayEnd(); writer.Context.Path.Pop(); } writer.WriteArrayEnd(); } // ReSharper restore PossibleMultipleEnumeration writer.Context.Hierarchy.Pop(); } public override string ToString() { return string.Format("dictionary of {1}:{2}, {0}", this.dictionaryType, this.keyType, this.valueType); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/DictionarySerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 13a732fa56444620a58134388e9cb921 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/EnumNumberSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class EnumNumberSerializer : TypeSerializer { private readonly Type enumType; private readonly Type enumBaseType; public override Type SerializedType { get { return this.enumType; } } public EnumNumberSerializer(Type enumType) { if (enumType == null) throw new ArgumentNullException("enumType"); if (!enumType.IsEnum) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "be a Enum"); this.enumType = enumType; this.enumBaseType = Enum.GetUnderlyingType(enumType); } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.StringLiteral) return Enum.Parse(this.enumType, reader.ReadString(false), true); else if (reader.Token == JsonToken.Number) return Enum.ToObject(this.enumType, reader.ReadValue(this.enumBaseType, false)); else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.Number, JsonToken.StringLiteral); } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); writer.WriteValue(Convert.ChangeType(value, this.enumBaseType), this.enumBaseType); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/EnumNumberSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: c69cddac8b674579a85f39be4d0a6869 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/EnumSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class EnumSerializer : TypeSerializer { private readonly Type enumType; private readonly Type enumBaseType; public override Type SerializedType { get { return this.enumType; } } public EnumSerializer(Type enumType) { if (enumType == null) throw new ArgumentNullException("enumType"); if (!enumType.IsEnum) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "be a Enum"); this.enumType = enumType; this.enumBaseType = Enum.GetUnderlyingType(enumType); } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.StringLiteral) return Enum.Parse(this.enumType, reader.ReadString(false), true); else if (reader.Token == JsonToken.Number) return Enum.ToObject(this.enumType, reader.ReadValue(this.enumBaseType, false)); else throw JsonSerializationException.UnexpectedToken(reader, JsonToken.Number, JsonToken.StringLiteral); } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var valueStr = Convert.ToString(value, writer.Context.Format); writer.WriteString(valueStr); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/EnumSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 767beb9929ef47068dd78d4e4d82c6c9 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/GuidSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using GameDevWare.Serialization.MessagePack; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class GuidSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(Guid); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); var guidStr = reader.ReadString(false); var value = new Guid(guidStr); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var messagePackWriter = writer as MsgPackWriter; if (messagePackWriter != null) { // try to write it as Message Pack extension type var extensionType = default(sbyte); var buffer = messagePackWriter.GetWriteBuffer(); if (messagePackWriter.Context.ExtensionTypeHandler.TryWrite(value, out extensionType, ref buffer)) { messagePackWriter.Write(extensionType, buffer); return; } // if not, continue default serialization } var guid = (Guid)value; var guidStr = guid.ToString(); writer.Write(guidStr); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/GuidSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: d6721c41f669421289be40c0416e683c timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Matrix4x4Serializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER using System; using UnityEngine; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class Matrix4x4Serializer : TypeSerializer { public override Type SerializedType { get { return typeof(Matrix4x4); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var value = new Matrix4x4(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); switch (memberName) { case "m00": value.m00 = reader.ReadSingle(); break; case "m10": value.m10 = reader.ReadSingle(); break; case "m20": value.m20 = reader.ReadSingle(); break; case "m30": value.m30 = reader.ReadSingle(); break; case "m01": value.m01 = reader.ReadSingle(); break; case "m11": value.m11 = reader.ReadSingle(); break; case "m21": value.m21 = reader.ReadSingle(); break; case "m31": value.m31 = reader.ReadSingle(); break; case "m02": value.m02 = reader.ReadSingle(); break; case "m12": value.m12 = reader.ReadSingle(); break; case "m22": value.m22 = reader.ReadSingle(); break; case "m32": value.m32 = reader.ReadSingle(); break; case "m03": value.m03 = reader.ReadSingle(); break; case "m13": value.m13 = reader.ReadSingle(); break; case "m23": value.m23 = reader.ReadSingle(); break; case "m33": value.m33 = reader.ReadSingle(); break; default: reader.ReadValue(typeof(object)); break; } } reader.ReadObjectEnd(nextToken: false); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var matrix = (Matrix4x4)value; writer.WriteObjectBegin(16); writer.WriteMember("m00"); writer.Write(matrix.m00); writer.WriteMember("m10"); writer.Write(matrix.m10); writer.WriteMember("m20"); writer.Write(matrix.m20); writer.WriteMember("m30"); writer.Write(matrix.m30); writer.WriteMember("m01"); writer.Write(matrix.m01); writer.WriteMember("m11"); writer.Write(matrix.m11); writer.WriteMember("m21"); writer.Write(matrix.m21); writer.WriteMember("m31"); writer.Write(matrix.m31); writer.WriteMember("m02"); writer.Write(matrix.m02); writer.WriteMember("m12"); writer.Write(matrix.m12); writer.WriteMember("m22"); writer.Write(matrix.m22); writer.WriteMember("m32"); writer.Write(matrix.m32); writer.WriteMember("m03"); writer.Write(matrix.m03); writer.WriteMember("m13"); writer.Write(matrix.m13); writer.WriteMember("m23"); writer.Write(matrix.m23); writer.WriteMember("m33"); writer.Write(matrix.m33); writer.WriteObjectEnd(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Matrix4x4Serializer.cs.meta ================================================ fileFormatVersion: 2 guid: 47390c8c743d4b078b66766035eaf27c timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/MsgPackExtensionTypeSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Globalization; using GameDevWare.Serialization.MessagePack; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class MsgPackExtensionTypeSerializer : TypeSerializer { private const string DATA_MEMBER_NAME = "$data"; private const string TYPE_MEMBER_NAME = "$type"; /// public override Type SerializedType { get { return typeof(MessagePackExtensionType); } } public override object Deserialize(IJsonReader reader) { if (reader.Token == JsonToken.Null) { return null; } else if (reader.RawValue is MessagePackExtensionType) { return (MessagePackExtensionType)reader.RawValue; } else if (reader.RawValue is byte[]) { return new MessagePackExtensionType((byte[])reader.RawValue); } else if (reader.RawValue is string) { return new MessagePackExtensionType(Convert.FromBase64String(reader.Value.AsString)); } // { "$binary" : "", "$type" : "" } // http://www.mongodb.org/display/DOCS/Mongo+Extended+JSON reader.ReadObjectBegin(); var base64Binary = default(string); var subtypeHex = default(string); while (reader.Token != JsonToken.EndOfObject) { var member = reader.ReadMember(); switch (member) { case DATA_MEMBER_NAME: base64Binary = reader.ReadString(); break; case TYPE_MEMBER_NAME: subtypeHex = reader.ReadString(); break; default: reader.ReadValue(typeof(object)); // skip value break; } } reader.ReadObjectEnd(nextToken: false); if (subtypeHex == null) subtypeHex = "0"; if (base64Binary == null) base64Binary = ""; var binaryType = unchecked((sbyte)byte.Parse(subtypeHex, NumberStyles.HexNumber)); var binary = Convert.FromBase64String(base64Binary); var value = new MessagePackExtensionType(binaryType, binary); return value; } public override void Serialize(IJsonWriter writer, object value) { if (value == null) { writer.WriteNull(); return; } var extensionType = (MessagePackExtensionType)value; var msgPackWriter = writer as MsgPackWriter; if (msgPackWriter != null) { msgPackWriter.Write(extensionType.Type, extensionType.ToArraySegment()); return; } writer.WriteObjectBegin(2); writer.WriteMember(DATA_MEMBER_NAME); writer.WriteString(extensionType.ToBase64()); writer.WriteMember(TYPE_MEMBER_NAME); writer.Write(unchecked((byte)extensionType.Type).ToString("X2")); writer.WriteObjectEnd(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/MsgPackExtensionTypeSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 5e091ddc6ecc49c4a1f563cc24cb2aaf timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/MsgPackTimestampSerializer.cs ================================================ using System; using GameDevWare.Serialization.MessagePack; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public class MsgPackTimestampSerializer : TypeSerializer { private const string SECONDS_MEMBER_NAME = "$seconds"; private const string NANO_SECONDS_MEMBER_NAME = "$nanoSeconds"; /// public override Type SerializedType { get { return typeof(MessagePackTimestamp); } } public override object Deserialize(IJsonReader reader) { if (reader.RawValue is MessagePackTimestamp) { return (MessagePackTimestamp)reader.Value.Raw; } else if (reader.Token == JsonToken.Null) { return null; } reader.ReadObjectBegin(); var seconds = default(long); var nanoSeconds = default(uint); while (reader.Token != JsonToken.EndOfObject) { var member = reader.ReadMember(); switch (member) { case SECONDS_MEMBER_NAME: seconds = reader.ReadInt64(); break; case NANO_SECONDS_MEMBER_NAME: seconds = reader.ReadUInt32(); break; default: reader.ReadValue(typeof(object)); // skip value break; } } reader.ReadObjectEnd(false); var value = new MessagePackTimestamp(seconds, nanoSeconds); return value; } public override void Serialize(IJsonWriter writer, object value) { if (value == null) { writer.WriteNull(); return; } var messagePackWriter = writer as MsgPackWriter; if (messagePackWriter != null) { var extensionType = default(sbyte); var buffer = messagePackWriter.GetWriteBuffer(); if (messagePackWriter.Context.ExtensionTypeHandler.TryWrite(value, out extensionType, ref buffer)) { messagePackWriter.Write(extensionType, buffer); return; } } var timeStamp = (MessagePackTimestamp)value; writer.WriteObjectBegin(2); writer.WriteMember(SECONDS_MEMBER_NAME); writer.Write(timeStamp.Seconds); writer.WriteMember(NANO_SECONDS_MEMBER_NAME); writer.Write(timeStamp.NanoSeconds); writer.WriteObjectEnd(); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/MsgPackTimestampSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 958711d50492492aa2bffb7dadf13927 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/ObjectSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Text.RegularExpressions; using GameDevWare.Serialization.Metadata; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public class ObjectSerializer : TypeSerializer { public const string TYPE_MEMBER_NAME = "_type"; private static readonly Regex VersionRegEx = new Regex(@", Version=[^\]]+", RegexOptions.None); private static readonly string BclTypePart = typeof(byte).AssemblyQualifiedName.Substring(typeof(byte).FullName.Length); private readonly Type objectType; private readonly string objectTypeNameWithoutVersion; private readonly TypeDescription objectTypeDescription; private readonly ObjectSerializer baseTypeSerializer; private readonly SerializationContext context; public override Type SerializedType { get { return this.objectType; } } public bool SuppressTypeInformation { get; set; } public ObjectSerializer(SerializationContext context, Type type) { if (type == null) throw new ArgumentNullException("type"); if (context == null) throw new ArgumentNullException("context"); this.context = context; this.objectType = type; this.objectTypeNameWithoutVersion = GetVersionInvariantObjectTypeName(this.objectType); this.SuppressTypeInformation = (context.Options & SerializationOptions.SuppressTypeInformation) == SerializationOptions.SuppressTypeInformation; if (this.objectType.BaseType != null && this.objectType.BaseType != typeof(object)) { var baseSerializer = context.GetSerializerForType(this.objectType.BaseType); if (baseSerializer is ObjectSerializer == false) { throw JsonSerializationException.TypeRequiresCustomSerializer(this.objectType, this.GetType()); } this.baseTypeSerializer = (ObjectSerializer)baseSerializer; } this.objectTypeDescription = TypeDescription.Get(type); } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token != JsonToken.BeginObject) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.BeginObject); var serializerOverride = default(ObjectSerializer); var container = new IndexedDictionary(10); reader.Context.Hierarchy.Push(container); var instance = this.DeserializeMembers(reader, container, ref serializerOverride); reader.Context.Hierarchy.Pop(); if (reader.Token != JsonToken.EndOfObject) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.EndOfObject); if (instance != null) return instance; else if (serializerOverride != null) return serializerOverride.PopulateInstance(container, null); else return this.PopulateInstance(container, null); } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var container = new IndexedDictionary(); this.CollectMemberValues(value, container); if (this.SuppressTypeInformation || this.objectTypeDescription.IsAnonymousType) { writer.WriteObjectBegin(container.Count); } else { writer.WriteObjectBegin(container.Count + 1); writer.Context.Path.Push(new PathSegment(TYPE_MEMBER_NAME)); writer.WriteMember(TYPE_MEMBER_NAME); writer.WriteString(objectTypeNameWithoutVersion); this.context.Path.Pop(); } foreach (var kv in container) { writer.Context.Path.Push(new PathSegment(kv.Key.Name)); writer.WriteMember(kv.Key.Name); writer.WriteValue(kv.Value, kv.Key.ValueType); this.context.Path.Pop(); } writer.WriteObjectEnd(); } private void CollectMemberValues(object instance, IndexedDictionary container) { if (this.baseTypeSerializer != null) this.baseTypeSerializer.CollectMemberValues(instance, container); foreach (var member in this.objectTypeDescription.Members) { var baseMemberWithSameName = default(DataMemberDescription); if (this.baseTypeSerializer != null && this.baseTypeSerializer.TryGetMember(member.Name, out baseMemberWithSameName)) container.Remove(baseMemberWithSameName); var value = member.GetValue(instance); container[member] = value; } } private object DeserializeMembers(IJsonReader reader, IndexedDictionary container, ref ObjectSerializer serializerOverride) { while (reader.NextToken() && reader.Token != JsonToken.EndOfObject) { if (reader.Token != JsonToken.Member) throw JsonSerializationException.UnexpectedToken(reader, JsonToken.Member); string memberName = null; object value = null; memberName = reader.Value.AsString; // string if (string.Equals(memberName, TYPE_MEMBER_NAME) && this.SuppressTypeInformation == false) { this.context.Path.Push(new PathSegment(TYPE_MEMBER_NAME)); reader.NextToken(); var typeName = reader.ReadString(false); var type = default(Type); try { type = reader.Context.GetType(typeName, true, true); } catch (Exception getTypeError) { throw new SerializationException(string.Format("Failed to resolve type '{0}' of value for '{1}' of '{2}' type.\r\n" + "More detailed information in inner exception.", typeName, memberName, this.objectType.Name), getTypeError); } this.context.Path.Pop(); if (type == typeof(object)) { this.DeserializeMembers(reader, container, ref serializerOverride); return new object(); } var serializer = reader.Context.GetSerializerForType(type); if (serializer is ObjectSerializer) { serializerOverride = (ObjectSerializer)serializer; serializerOverride.DeserializeMembers(reader, container, ref serializerOverride); return null; } else { reader.NextToken(); // nextToken to next member serializerOverride = null; return serializer.Deserialize(reader); } } this.context.Path.Push(new PathSegment(memberName)); var member = default(DataMemberDescription); var valueType = typeof(object); if (this.TryGetMember(memberName, out member)) valueType = member.ValueType; reader.NextToken(); try { value = reader.ReadValue(valueType, false); } catch (Exception e) { throw new SerializationException(string.Format("Failed to read value for member '{0}' of '{1}' type.\r\nMore detailed information in inner exception.", memberName, this.objectType.Name), e); } container[memberName] = value; this.context.Path.Pop(); } return null; } private object PopulateInstance(IndexedDictionary container, object instance) { if (instance == null && objectType == typeof(object)) return container; if (instance == null) instance = objectTypeDescription.CreateInstance(); foreach (var member in this.objectTypeDescription.Members) { var memberName = member.Name; var memberType = member.ValueType; var defaultValue = member.DefaultValue; if (defaultValue == null || container.ContainsKey(memberName)) continue; if (defaultValue.GetType() == memberType) container[memberName] = defaultValue; else if ("[]".Equals(defaultValue) || "{}".Equals(defaultValue)) container[memberName] = memberType.IsArray ? Array.CreateInstance(memberType.GetElementType(), 0) : Activator.CreateInstance(memberType); else if (defaultValue is string) container[memberName] = Json.Deserialize(memberType, (string)defaultValue, context); else container[memberName] = Convert.ChangeType(defaultValue, memberType, context.Format); } foreach (var kv in container) { var memberName = kv.Key; var value = kv.Value; var member = default(DataMemberDescription); if (!this.TryGetMember(memberName, out member)) continue; try { member.SetValue(instance, value); } catch (Exception e) { throw new SerializationException(string.Format("Failed to set member '{0}' to value '{1}' of type {2}.\r\n More detailed information in inner exception.", memberName, value, value != null ? value.GetType().FullName : ""), e); } } if (this.baseTypeSerializer != null) this.baseTypeSerializer.PopulateInstance(container, instance); return instance; } private bool TryGetMember(string memberName, out DataMemberDescription member) { if (memberName == null) throw new ArgumentNullException("memberName"); if (this.objectTypeDescription.TryGetMember(memberName, out member)) return true; if (this.baseTypeSerializer == null) return false; return this.baseTypeSerializer.TryGetMember(memberName, out member); } public static object CreateInstance(IndexedDictionary values) { if (values == null) throw new ArgumentNullException("values"); var instanceType = typeof(object); var instanceTypeName = default(object); if (values.TryGetValue(TYPE_MEMBER_NAME, out instanceTypeName)) { values.Remove(TYPE_MEMBER_NAME); instanceType = Type.GetType((string)instanceTypeName, true); } return CreateInstance(values, instanceType); } public static object CreateInstance(IndexedDictionary values, Type instanceType) { if (instanceType == null) throw new ArgumentNullException("instanceType"); if (values == null) throw new ArgumentNullException("values"); var context = new SerializationContext(); var serializer = new ObjectSerializer(context, instanceType); return serializer.PopulateInstance(values, null); } public static string GetVersionInvariantObjectTypeName(Type type) { if (type == null) throw new ArgumentNullException("type"); var fullName = (type.AssemblyQualifiedName ?? type.FullName ?? type.Name); fullName = VersionRegEx.Replace(fullName, string.Empty); fullName = fullName.Replace(BclTypePart, ""); // remove BCL path of type information for better interop compatibility return fullName; } public override string ToString() { return string.Format("object, {0}", this.objectType); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/ObjectSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: e6ed81f3cf274c95af11e943c5a64203 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/PrimitiveTypeSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class PrimitiveSerializer : TypeSerializer { private readonly Type primitiveType; private readonly TypeCode primitiveTypeCode; public override Type SerializedType { get { return this.primitiveType; } } public PrimitiveSerializer(Type primitiveType) { if (primitiveType == null) throw new ArgumentNullException("primitiveType"); if (primitiveType.IsGenericType && primitiveType.GetGenericTypeDefinition() == typeof(Nullable<>)) throw JsonSerializationException.TypeIsNotValid(typeof(PrimitiveSerializer), "can't be nullable type"); this.primitiveType = primitiveType; this.primitiveTypeCode = Type.GetTypeCode(primitiveType); if (this.primitiveTypeCode == TypeCode.Object || this.primitiveTypeCode == TypeCode.Empty || this.primitiveTypeCode == TypeCode.DBNull) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "be a primitive type"); } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) { if (this.primitiveTypeCode == TypeCode.String) return null; throw JsonSerializationException.UnexpectedToken(reader, JsonToken.Boolean, JsonToken.DateTime, JsonToken.Null, JsonToken.Number, JsonToken.StringLiteral); } var value = default(object); switch (primitiveTypeCode) { case TypeCode.Boolean: value = reader.ReadBoolean(false); break; case TypeCode.Byte: value = reader.ReadByte(false); break; case TypeCode.DateTime: value = reader.ReadDateTime(false); break; case TypeCode.Decimal: value = reader.ReadDecimal(false); break; case TypeCode.Double: value = reader.ReadDouble(false); break; case TypeCode.Int16: value = reader.ReadInt16(false); break; case TypeCode.Int32: value = reader.ReadInt32(false); break; case TypeCode.Int64: value = reader.ReadInt64(false); break; case TypeCode.SByte: value = reader.ReadSByte(false); break; case TypeCode.Single: value = reader.ReadSingle(false); break; case TypeCode.UInt16: value = reader.ReadUInt16(false); break; case TypeCode.UInt32: value = reader.ReadUInt32(false); break; case TypeCode.UInt64: value = reader.ReadUInt64(false); break; default: var valueStr = reader.ReadString(false); value = Convert.ChangeType(valueStr, this.primitiveType, reader.Context.Format); break; } return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); switch (primitiveTypeCode) { case TypeCode.Boolean: writer.WriteBoolean((bool)value); break; case TypeCode.Byte: writer.WriteNumber((byte)value); break; case TypeCode.DateTime: writer.WriteDateTime((DateTime)value); break; case TypeCode.Decimal: writer.WriteNumber((decimal)value); break; case TypeCode.Double: writer.WriteNumber((double)value); break; case TypeCode.Int16: writer.WriteNumber((short)value); break; case TypeCode.Int32: writer.WriteNumber((int)value); break; case TypeCode.Int64: writer.WriteNumber((long)value); break; case TypeCode.SByte: writer.WriteNumber((sbyte)value); break; case TypeCode.Single: writer.WriteNumber((float)value); break; case TypeCode.UInt16: writer.WriteNumber((ushort)value); break; case TypeCode.UInt32: writer.WriteNumber((uint)value); break; case TypeCode.UInt64: writer.WriteNumber((ulong)value); break; default: var valueStr = default(string); if (value is IFormattable) valueStr = (string)Convert.ChangeType(value, typeof(string), writer.Context.Format); else valueStr = value.ToString(); writer.WriteString(valueStr); break; } } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/PrimitiveTypeSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 859ced78d4a94d14b7d47a0e6e1b6ced timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/QuaternionSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER using System; using UnityEngine; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class QuaternionSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(Quaternion); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var value = new Quaternion(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); if (memberName == "x") value.x = reader.ReadSingle(); else if (memberName == "y") value.y = reader.ReadSingle(); else if (memberName == "z") value.z = reader.ReadSingle(); else if (memberName == "w") value.w = reader.ReadSingle(); else reader.ReadValue(typeof(object)); } reader.ReadObjectEnd(nextToken: false); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var quaternion = (Quaternion)value; writer.WriteObjectBegin(4); writer.WriteMember("x"); writer.Write(quaternion.x); writer.WriteMember("y"); writer.Write(quaternion.y); writer.WriteMember("z"); writer.Write(quaternion.z); writer.WriteMember("w"); writer.Write(quaternion.w); writer.WriteObjectEnd(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/QuaternionSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 7a244cc0ddaf41539286e6dca2cfd601 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/RectSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER using System; using UnityEngine; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class RectSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(Rect); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var value = new Rect(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); if (memberName == "x") value.x = reader.ReadSingle(); else if (memberName == "y") value.y = reader.ReadSingle(); else if (memberName == "width") value.width = reader.ReadSingle(); else if (memberName == "height") value.height = reader.ReadSingle(); else reader.ReadValue(typeof(object)); } reader.ReadObjectEnd(nextToken: false); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var vector4 = (Rect)value; writer.WriteObjectBegin(4); writer.WriteMember("x"); writer.Write(vector4.x); writer.WriteMember("y"); writer.Write(vector4.y); writer.WriteMember("width"); writer.Write(vector4.width); writer.WriteMember("height"); writer.Write(vector4.height); writer.WriteObjectEnd(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/RectSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: e9596b5ac9c642cca7b4e9efac805b19 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/StreamSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; using System.IO; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class StreamSerializer : TypeSerializer { public static readonly StreamSerializer Instance = new StreamSerializer(); public override Type SerializedType { get { return typeof(Stream); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; if (reader.RawValue is Stream) return reader.RawValue; else if(reader.RawValue is byte[]) return new MemoryStream((byte[])reader.RawValue); else { var base64Str = Convert.ToString(reader.RawValue, reader.Context.Format); var bytes = Convert.FromBase64String(base64Str); return new MemoryStream(bytes); } } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var stream = value as Stream; if (value != null && stream == null) throw JsonSerializationException.TypeIsNotValid(this.GetType(), "be a Stream"); if (!stream.CanRead) throw new JsonSerializationException("Stream couldn't be readed.", JsonSerializationException.ErrorCode.StreamIsNotReadable); if (stream.CanSeek) { var position = stream.Position; var buffer = new byte[stream.Length - stream.Position]; stream.Read(buffer, 0, buffer.Length); BinarySerializer.Instance.Serialize(writer, buffer); stream.Position = position; } else { var tmpStream = new MemoryStream(); var buffer = new byte[ushort.MaxValue]; var readed = 0; while ((readed = stream.Read(buffer, 0, buffer.Length)) > 0) tmpStream.Write(buffer, 0, readed); BinarySerializer.Instance.Serialize(writer, tmpStream.ToArray()); } } public override string ToString() { return "stream"; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/StreamSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 3de90ee415c945b683bc759458d38fb1 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/TimeSpanSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class TimeSpanSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(TimeSpan); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Number) return new TimeSpan(reader.Value.AsInt64); var timeSpanStr = reader.ReadString(false); var value = TimeSpan.Parse(timeSpanStr); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var timeSpan = (TimeSpan)value; writer.Write((long)timeSpan.Ticks); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/TimeSpanSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 7a6c1dec78094360871fca0d28f291e5 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/UriSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class UriSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(Uri); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); var uriStr = reader.ReadString(false); var value = new Uri(uriStr); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var uri = (Uri)value; writer.WriteString(uri.OriginalString); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/UriSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 295ef6663ceb4552a3dd3c6c265cef7c timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Vector2Serializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER using System; using UnityEngine; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class Vector2Serializer : TypeSerializer { public override Type SerializedType { get { return typeof(Vector2); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var value = new Vector2(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); switch (memberName) { case "x": value.x = reader.ReadSingle(); break; case "y": value.y = reader.ReadSingle(); break; default: reader.ReadValue(typeof(object)); break; } } reader.ReadObjectEnd(nextToken: false); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var vector2 = (Vector2)value; writer.WriteObjectBegin(2); writer.WriteMember("x"); writer.Write(vector2.x); writer.WriteMember("y"); writer.Write(vector2.y); writer.WriteObjectEnd(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Vector2Serializer.cs.meta ================================================ fileFormatVersion: 2 guid: 367d804563384832bf9db1d72a34d639 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Vector3Serializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER using System; using UnityEngine; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class Vector3Serializer : TypeSerializer { public override Type SerializedType { get { return typeof(Vector3); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var value = new Vector3(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); switch (memberName) { case "x": value.x = reader.ReadSingle(); break; case "y": value.y = reader.ReadSingle(); break; case "z": value.z = reader.ReadSingle(); break; default: reader.ReadValue(typeof(object)); break; } } reader.ReadObjectEnd(nextToken: false); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var vector3 = (Vector3)value; writer.WriteObjectBegin(3); writer.WriteMember("x"); writer.Write(vector3.x); writer.WriteMember("y"); writer.Write(vector3.y); writer.WriteMember("z"); writer.Write(vector3.z); writer.WriteObjectEnd(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Vector3Serializer.cs.meta ================================================ fileFormatVersion: 2 guid: fb0cf45e7412469d816f2913f40b0040 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Vector4Serializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ #if UNITY_5 || UNITY_4 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_5_3_OR_NEWER using System; using UnityEngine; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class Vector4Serializer : TypeSerializer { public override Type SerializedType { get { return typeof(Vector4); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); if (reader.Token == JsonToken.Null) return null; var value = new Vector4(); reader.ReadObjectBegin(); while (reader.Token != JsonToken.EndOfObject) { var memberName = reader.ReadMember(); switch (memberName) { case "x": value.x = reader.ReadSingle(); break; case "y": value.y = reader.ReadSingle(); break; case "z": value.z = reader.ReadSingle(); break; case "w": value.w = reader.ReadSingle(); break; default: reader.ReadValue(typeof(object)); break; } } reader.ReadObjectEnd(nextToken: false); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var vector4 = (Vector4)value; writer.WriteObjectBegin(4); writer.WriteMember("x"); writer.Write(vector4.x); writer.WriteMember("y"); writer.Write(vector4.y); writer.WriteMember("z"); writer.Write(vector4.z); writer.WriteMember("w"); writer.Write(vector4.w); writer.WriteObjectEnd(); } } } #endif ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/Vector4Serializer.cs.meta ================================================ fileFormatVersion: 2 guid: a7eb2e329a0943fdb69b1bd0c4702574 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/VersionSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization.Serializers { public sealed class VersionSerializer : TypeSerializer { public override Type SerializedType { get { return typeof(Version); } } public override object Deserialize(IJsonReader reader) { if (reader == null) throw new ArgumentNullException("reader"); var versionStr = reader.ReadString(false); var value = new Version(versionStr); return value; } public override void Serialize(IJsonWriter writer, object value) { if (writer == null) throw new ArgumentNullException("writer"); if (value == null) throw new ArgumentNullException("value"); var version = (Version)value; writer.WriteString(version.ToString()); } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers/VersionSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: e7871724320e4be09e241532835a6270 timeCreated: 1653295313 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/Serializers.meta ================================================ fileFormatVersion: 2 guid: acca4413426e43b8874cab39d0c52faa folderAsset: yes timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/TypeSerializer.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public abstract class TypeSerializer { public abstract Type SerializedType { get; } public abstract object Deserialize(IJsonReader reader); public abstract void Serialize(IJsonWriter writer, object value); } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/TypeSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 44d201fe0e97475982f385f0edf98954 timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/TypeSerializerAttribute.cs ================================================ /* Copyright (c) 2019 Denis Zykov, GameDevWare.com This a part of "Json & MessagePack Serialization" Unity Asset - https://www.assetstore.unity3d.com/#!/content/59918 THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. This source code is distributed via Unity Asset Store, to use it in your project you should accept Terms of Service and EULA https://unity3d.com/ru/legal/as_terms */ using System; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] public class TypeSerializerAttribute : Attribute { public Type SerializerType { get; private set; } public TypeSerializerAttribute(Type type) { if (type == null) throw new ArgumentNullException("type"); if (typeof(TypeSerializer).IsAssignableFrom(type) == false) throw new ArgumentException(string.Format("Type '{0}' should inherit from '{1}'.", type, typeof(TypeSerializer)), "type"); this.SerializerType = type; } } } ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization/TypeSerializerAttribute.cs.meta ================================================ fileFormatVersion: 2 guid: 978e180ab92d4831991cf25c4dde873f timeCreated: 1653295312 ================================================ FILE: Assets/Colyseus/Runtime/GameDevWare.Serialization.meta ================================================ fileFormatVersion: 2 guid: ed2bae19aaafee64ca1ec0da9bc0202d folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime/WebSocket.meta ================================================ fileFormatVersion: 2 guid: 8ad79c09e65aa9d448fed15c550df644 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Runtime.meta ================================================ fileFormatVersion: 2 guid: 4c3110b6bc1ac284fb6c48a5420133a9 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Tests/.gitkeep ================================================ ================================================ FILE: Assets/Colyseus/Tests/Colyseus.Editor.Tests.asmdef ================================================ { "name": "Colyseus.Editor.Tests", "references": [ "ColyseusSDK" ], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } ================================================ FILE: Assets/Colyseus/Tests/Colyseus.Editor.Tests.asmdef.meta ================================================ fileFormatVersion: 2 guid: 81cbeb15a98104ba0883bf60f5cd26be AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Tests/Editor/ColyseusTests.meta ================================================ fileFormatVersion: 2 guid: 66dd0dda83b239249a2d3553751e9854 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Tests/Editor/ServerSettingsEditor.cs ================================================ using System; using Colyseus; using UnityEngine; using UnityEditor; using System.IO; [CustomEditor(typeof(Settings))] public class ServerSettingsEditor : Editor { private SerializedProperty url; private SerializedProperty port; private SerializedProperty secureProto; private SerializedProperty requestHeaders; bool serverInfoExpanded = false; //private Texture colyseusIcon; private float buttonWidth = 250; private float sectionSpacer = 20; void OnEnable() { url = serializedObject.FindProperty("colyseusServerAddress"); port = serializedObject.FindProperty("colyseusServerPort"); secureProto= serializedObject.FindProperty("useSecureProtocol"); requestHeaders = serializedObject.FindProperty("_requestHeaders"); // // TODO: Why loading icon never works? // I've spent more than 2 hours here! // //colyseusIcon = EditorGUIUtility.IconContent("ColyseusSettings").image; } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.LabelField("Colyseus Server Settings", EditorStyles.boldLabel); serverInfoExpanded = EditorGUILayout.Foldout(serverInfoExpanded, "Server Information"); if (serverInfoExpanded) { EditorGUILayout.PropertyField(url); EditorGUILayout.PropertyField(port); EditorGUILayout.PropertyField(secureProto); } EditorGUILayout.PropertyField(requestHeaders); EditorGUILayout.Space(sectionSpacer); EditorGUILayout.LabelField("Additional Resources", EditorStyles.boldLabel); //if (GUILayout.Button("Colyseus Cloud Dashboard", GUILayout.MaxWidth(buttonWidth))) //{ // Application.OpenURL("https://cloud.colyseus.io/"); //} if (GUILayout.Button("Documentation", GUILayout.MaxWidth(buttonWidth))) { Application.OpenURL("https://docs.colyseus.io/?utm_source=unity-editor"); } serializedObject.ApplyModifiedProperties(); } } ================================================ FILE: Assets/Colyseus/Tests/Editor/ServerSettingsEditor.cs.meta ================================================ fileFormatVersion: 2 guid: 555b5346159acaa44a7bf91b37d18bb6 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Tests/Editor.meta ================================================ fileFormatVersion: 2 guid: 4540f545322b50340b34ee357b3ea0bc folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Tests/Runtime/.gitkeep ================================================ ================================================ FILE: Assets/Colyseus/Tests/Runtime.meta ================================================ fileFormatVersion: 2 guid: e93087128c596c843a6b8d2349538646 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/Tests.meta ================================================ fileFormatVersion: 2 guid: c1c27865c8ba44b419c499d6fa54b4c5 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus/package.json ================================================ { "name": "io.colyseus.sdk", "version": "0.17.16", "displayName": "Colyseus SDK", "description": "Colyseus Multiplayer SDK for Unity", "unity": "2019.1", "license": "MIT", "category": "Networking", "repository": { "type": "git", "url": "https://github.com/colyseus/colyseus-unity-sdk.git" }, "author": { "name": "Endel Dreyer", "email": "endel@colyseus.io", "url": "https://colyseus.io/" }, "keywords": [ "colyseus", "multiplayer", "networking", "authoritative", "netcode" ], "dependencies": {}, "samples": [ { "displayName": "Example", "description": "Barebones usage example, demonstrating how to join custom rooms, send messages, and receive state updates. Includes LobbyRoom and QueueRoom examples.", "path": "Runtime/Example~" } ] } ================================================ FILE: Assets/Colyseus/package.json.meta ================================================ fileFormatVersion: 2 guid: e0d64eda35ff7e4408b1587a8edcaa3e PackageManifestImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Colyseus.meta ================================================ fileFormatVersion: 2 guid: 839fe1dd786fb7a4caaee63ba1a9df72 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: CLAUDE.md ================================================ # Bumping the package version - When asked to update version, bump semver patch version at ./Assets/Colyseus/package.json ================================================ FILE: LICENSE ================================================ Copyright (c) 2015-2016 Endel Dreyer MIT License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Packages/manifest.json ================================================ { "dependencies": { "com.unity.2d.sprite": "1.0.0", "com.unity.ai.navigation": "2.0.9", "com.unity.collab-proxy": "2.9.3", "com.unity.ide.rider": "3.0.37", "com.unity.ide.visualstudio": "2.0.25", "com.unity.multiplayer.center": "1.0.0", "com.unity.multiplayer.playmode": "1.6.0", "com.unity.timeline": "1.8.9", "com.unity.ugui": "2.0.0", "com.unity.modules.accessibility": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.audio": "1.0.0", "com.unity.modules.cloth": "1.0.0", "com.unity.modules.director": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", "com.unity.modules.particlesystem": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.physics2d": "1.0.0", "com.unity.modules.screencapture": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.terrainphysics": "1.0.0", "com.unity.modules.tilemap": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.modules.uielements": "1.0.0", "com.unity.modules.umbra": "1.0.0", "com.unity.modules.unityanalytics": "1.0.0", "com.unity.modules.unitywebrequest": "1.0.0", "com.unity.modules.unitywebrequestassetbundle": "1.0.0", "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.unitywebrequesttexture": "1.0.0", "com.unity.modules.unitywebrequestwww": "1.0.0", "com.unity.modules.vehicles": "1.0.0", "com.unity.modules.video": "1.0.0", "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", "com.unity.modules.xr": "1.0.0" } } ================================================ FILE: Packages/packages-lock.json ================================================ { "dependencies": { "com.unity.2d.sprite": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.ai.navigation": { "version": "2.0.9", "depth": 0, "source": "registry", "dependencies": { "com.unity.modules.ai": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.collab-proxy": { "version": "2.9.3", "depth": 0, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.ext.nunit": { "version": "2.0.5", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.ide.rider": { "version": "3.0.37", "depth": 0, "source": "registry", "dependencies": { "com.unity.ext.nunit": "1.0.6" }, "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { "version": "2.0.25", "depth": 0, "source": "registry", "dependencies": { "com.unity.test-framework": "1.1.31" }, "url": "https://packages.unity.com" }, "com.unity.multiplayer.center": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.uielements": "1.0.0" } }, "com.unity.multiplayer.playmode": { "version": "1.6.0", "depth": 0, "source": "registry", "dependencies": { "com.unity.nuget.newtonsoft-json": "2.0.2" }, "url": "https://packages.unity.com" }, "com.unity.nuget.newtonsoft-json": { "version": "3.2.1", "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.test-framework": { "version": "1.5.1", "depth": 1, "source": "builtin", "dependencies": { "com.unity.ext.nunit": "2.0.3", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" } }, "com.unity.timeline": { "version": "1.8.9", "depth": 0, "source": "registry", "dependencies": { "com.unity.modules.audio": "1.0.0", "com.unity.modules.director": "1.0.0", "com.unity.modules.animation": "1.0.0", "com.unity.modules.particlesystem": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.ugui": { "version": "2.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0" } }, "com.unity.modules.accessibility": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.ai": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.androidjni": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.animation": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.assetbundle": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.audio": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.cloth": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.physics": "1.0.0" } }, "com.unity.modules.director": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.audio": "1.0.0", "com.unity.modules.animation": "1.0.0" } }, "com.unity.modules.hierarchycore": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.imageconversion": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.imgui": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.jsonserialize": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.particlesystem": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.physics": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.physics2d": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.screencapture": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.imageconversion": "1.0.0" } }, "com.unity.modules.subsystems": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": { "com.unity.modules.jsonserialize": "1.0.0" } }, "com.unity.modules.terrain": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.terrainphysics": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.physics": "1.0.0", "com.unity.modules.terrain": "1.0.0" } }, "com.unity.modules.tilemap": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.physics2d": "1.0.0" } }, "com.unity.modules.ui": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.uielements": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", "com.unity.modules.hierarchycore": "1.0.0", "com.unity.modules.physics": "1.0.0" } }, "com.unity.modules.umbra": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.unityanalytics": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.unitywebrequest": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" } }, "com.unity.modules.unitywebrequest": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.unitywebrequestassetbundle": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.unitywebrequest": "1.0.0" } }, "com.unity.modules.unitywebrequestaudio": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.unitywebrequest": "1.0.0", "com.unity.modules.audio": "1.0.0" } }, "com.unity.modules.unitywebrequesttexture": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.unitywebrequest": "1.0.0", "com.unity.modules.imageconversion": "1.0.0" } }, "com.unity.modules.unitywebrequestwww": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.unitywebrequest": "1.0.0", "com.unity.modules.unitywebrequestassetbundle": "1.0.0", "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.audio": "1.0.0", "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.imageconversion": "1.0.0" } }, "com.unity.modules.vehicles": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.physics": "1.0.0" } }, "com.unity.modules.video": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.audio": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.modules.unitywebrequest": "1.0.0" } }, "com.unity.modules.vr": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.jsonserialize": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.xr": "1.0.0" } }, "com.unity.modules.wind": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.xr": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.physics": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", "com.unity.modules.subsystems": "1.0.0" } } } } ================================================ FILE: ProjectSettings/PackageManagerSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 61 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_EnablePreviewPackages: 0 m_EnablePackageDependencies: 0 m_AdvancedSettingsExpanded: 1 m_ScopedRegistriesSettingsExpanded: 1 oneTimeWarningShown: 0 m_Registries: - m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 m_Capabilities: 7 m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_ErrorMessage: m_Original: m_Id: m_Name: m_Url: m_Scopes: [] m_IsDefault: 0 m_Capabilities: 0 m_Modified: 0 m_Name: m_Url: m_Scopes: - m_SelectedScopeIndex: 0 ================================================ FILE: ProjectSettings/ProjectVersion.txt ================================================ m_EditorVersion: 6000.2.5f1 m_EditorVersionWithRevision: 6000.2.5f1 (43d04cd1df69) ================================================ FILE: ProjectSettings/XRSettings.asset ================================================ { "m_SettingKeys": [ "VR Device Disabled", "VR Device User Alert" ], "m_SettingValues": [ "False", "False" ] } ================================================ FILE: README.md ================================================


Discussion forum

Colyseus Multiplayer SDK for C#
For Unity, MonoGame, Godot Mono, etc.

> **Note:** This README covers development and contributing to the SDK. For end-user installation and usage, see the [Documentation](https://docs.colyseus.io/) ([Unity](https://docs.colyseus.io/getting-started/unity) | [MonoGame](https://docs.colyseus.io/getting-started/monogame) | [Godot](https://docs.colyseus.io/getting-started/godot)). ## Setup ### Unity Run `unity-setup.sh` to fetch external dependencies (e.g. [NativeWebSocket](https://github.com/endel/NativeWebSocket)) into `Assets/Colyseus/Runtime/WebSocket/`: ``` bash unity-setup.sh ``` This is required before opening the project in Unity. The fetched files are gitignored and bundled automatically during CI for UPM and `.unitypackage` releases. ### NuGet / Godot / MonoGame External dependencies are resolved via NuGet packages — no setup script needed. The SDK is available as the `Colyseus` NuGet package, which depends on `Colyseus.NativeWebSocket`. When the client is created on an engine main thread with a `SynchronizationContext` (Unity, Godot C#), WebSocket callbacks are marshaled back there automatically. Engines without one can register an external dispatcher through `ColyseusContext.RegisterWebSocketForDispatch`, or call `room.Connection.DispatchMessageQueue()` from their update loop. ## Running the test server In order to start a test server for this project's included example, do the following: ``` git clone https://github.com/colyseus/sdks-test-server cd sdks-test-server npm install npm start ``` ## Running tests ``` dotnet test nuget/Colyseus.csproj ``` ### Releasing a new version Update the version number under `Assets/Colyseus/package.json` and push to `master` branch. ## License MIT ================================================ FILE: UserSettings/EditorUserSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!162 &1 EditorUserSettings: m_ObjectHideFlags: 0 serializedVersion: 4 m_ConfigSettings: RecentlyUsedSceneGuid-0: value: 5009500407530c0a585f587645270844424e1a7e2f7825642c7f1931b2b93739 flags: 0 RecentlyUsedSceneGuid-1: value: 5401555453530a035458097a44730b44474e1e2b2d787e65787d4a63e4b5623d flags: 0 RecentlyUsedSceneGuid-2: value: 0007555056545159085f087340770b48404e1b7d292e76347c284462e6e4316c flags: 0 vcSharedLogLevel: value: 0d5e400f0650 flags: 0 m_VCAutomaticAdd: 1 m_VCDebugCom: 0 m_VCDebugCmd: 0 m_VCDebugOut: 0 m_SemanticMergeMode: 2 m_DesiredImportWorkerCount: 2 m_StandbyImportWorkerCount: 2 m_IdleImportWorkerShutdownDelay: 60000 m_VCShowFailedCheckout: 1 m_VCOverwriteFailedCheckoutAssets: 1 m_VCProjectOverlayIcons: 1 m_VCHierarchyOverlayIcons: 1 m_VCOtherOverlayIcons: 1 m_VCAllowAsyncUpdate: 0 m_VCScanLocalPackagesOnConnect: 1 m_ArtifactGarbageCollection: 1 m_CompressAssetsOnImport: 1 ================================================ FILE: nuget/Colyseus.csproj ================================================ netstandard2.1;net8.0 NU5128 9.0 Colyseus Colyseus false false true true false Colyseus Endel Dreyer Colyseus Multiplayer SDK — engine-agnostic core (Schema serialization, matchmaking, rooms). MIT https://colyseus.io/ https://github.com/colyseus/colyseus-unity-sdk colyseus;multiplayer;networking;authoritative;netcode;gamedev README.md ================================================ FILE: nuget/README.md ================================================

Colyseus Multiplayer SDK for C#/.NET

Engine-agnostic core SDK for [Colyseus](https://colyseus.io/) — includes Schema serialization, matchmaking, room management, and state synchronization. Works with Unity, Godot (C#), MonoGame, and any .NET project. ## Installation ```sh dotnet add package Colyseus ``` ## Threading When the client is created on a thread with a `SynchronizationContext` (Unity, Godot C#), Colyseus posts WebSocket callbacks back to that context automatically. For engines without one, either register an external dispatcher through `ColyseusContext.RegisterWebSocketForDispatch`, or call `room.Connection.DispatchMessageQueue()` from your update loop. ## Quick Example ```csharp using Colyseus; using Colyseus.Schema; var client = new Client("ws://localhost:2567"); var room = await client.JoinOrCreate("my_room"); Console.WriteLine("Joined room: " + room.Id); var callbacks = Callbacks.Get(room); callbacks.Listen(state => state.currentTurn, (currentValue, previousValue) => { Console.WriteLine($"Turn changed: {previousValue} -> {currentValue}"); }); callbacks.OnAdd(state => state.players, (sessionId, player) => { Console.WriteLine($"Player joined: {sessionId}"); }); room.Send("move", new { x = 10f, y = 20f }); room.OnMessage("chat", (message) => { Console.WriteLine("Chat: " + message); }); ``` ## Documentation See the full documentation at **https://docs.colyseus.io/getting-started/unity** ## License MIT ================================================ FILE: nuget/tests/HTTPTest.cs ================================================ using NUnit.Framework; namespace Colyseus.Tests { [TestFixture] public class HTTPTest { [Test] public void UnsecureRootPathWithPortTest() { var settings = Colyseus.Settings.Create(); settings.colyseusServerAddress = "localhost"; settings.colyseusServerPort = "2567"; settings.useSecureProtocol = false; var request = new Colyseus.HTTP(settings); Assert.AreEqual("http://localhost:2567/", request.GetRequestURL("").ToString()); } [Test] public void UnsecureChildPathWithPortTest() { var settings = Colyseus.Settings.Create(); settings.colyseusServerAddress = "localhost/path"; settings.colyseusServerPort = "2567"; settings.useSecureProtocol = false; var request = new Colyseus.HTTP(settings); Assert.AreEqual("http://localhost:2567/path/", request.GetRequestURL("").ToString()); } [Test] public void UnsecureChildPathNoPortTest() { var settings = Colyseus.Settings.Create(); settings.colyseusServerAddress = "localhost/path"; settings.colyseusServerPort = "80"; settings.useSecureProtocol = false; var request = new Colyseus.HTTP(settings); Assert.AreEqual("http://localhost/path/", request.GetRequestURL("").ToString()); } [Test] public void SecureChildPathNoPortTest() { var settings = Colyseus.Settings.Create(); settings.colyseusServerAddress = "localhost/path"; settings.colyseusServerPort = "443"; settings.useSecureProtocol = true; var request = new Colyseus.HTTP(settings); Assert.AreEqual("https://localhost/path/", request.GetRequestURL("").ToString()); } [Test] public void SecureChildPathWithPortTest() { var settings = Colyseus.Settings.Create(); settings.colyseusServerAddress = "localhost"; settings.colyseusServerPort = "8080"; settings.useSecureProtocol = true; var request = new Colyseus.HTTP(settings); Assert.AreEqual("https://localhost:8080/", request.GetRequestURL("").ToString()); } } } ================================================ FILE: nuget/tests/HTTPTest.cs.meta ================================================ fileFormatVersion: 2 guid: 44622709f20ef48128f6b944dac20dde ================================================ FILE: nuget/tests/IntegrationTest.cs ================================================ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; using Colyseus; using Colyseus.Schema; namespace Colyseus.Tests { [TestFixture] public class IntegrationTest { private Client client; [SetUp] public void Init() { client = new Client("ws://localhost:2567"); } [Test] public async Task JoinRoom() { var room = await client.JoinOrCreate("my_room"); Assert.IsNotNull(room); Assert.IsNotNull(room.RoomId); Assert.IsNotNull(room.SessionId); Assert.AreEqual("my_room", room.Name); await room.Leave(); } [Test] public async Task ReceiveInitialState() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; var received = await WithTimeout(stateReceived.Task, 5000); Assert.IsTrue(received, "Should receive initial state"); var state = room.State; Assert.IsNotNull(state.players); Assert.IsTrue(state.players.Count >= 1, "Should have at least 1 player (self)"); Assert.IsNotNull(state.players[room.SessionId], "Player should exist with own sessionId"); // Player should have an initial item (sword) var self = state.players[room.SessionId]; Assert.AreEqual(1, self.items.Count); Assert.AreEqual("sword", self.items[0].name); await room.Leave(); } [Test] public async Task SendAndReceiveMessage_Move() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); // Send move message await room.Send("move", new { x = 100, y = 200 }); // Wait for state update await Task.Delay(200); var self = room.State.players[room.SessionId]; Assert.AreEqual(100f, self.x); Assert.AreEqual(200f, self.y); await room.Leave(); } [Test] public async Task SendAndReceiveMessage_AddItem() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); var self = room.State.players[room.SessionId]; var initialCount = self.items.Count; await room.Send("add_item", new { name = "shield" }); await Task.Delay(200); Assert.AreEqual(initialCount + 1, self.items.Count); Assert.AreEqual("shield", self.items[self.items.Count - 1].name); await room.Leave(); } [Test] public async Task SendAndReceiveMessage_RemoveItem() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); var self = room.State.players[room.SessionId]; var initialCount = self.items.Count; Assert.IsTrue(initialCount > 0, "Should have at least 1 item"); await room.Send("remove_item", new { }); await Task.Delay(200); Assert.AreEqual(initialCount - 1, self.items.Count); await room.Leave(); } [Test] public async Task SendAndReceiveMessage_AddBot() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); var initialCount = room.State.players.Count; await room.Send("add_bot", new { }); await Task.Delay(200); Assert.AreEqual(initialCount + 1, room.State.players.Count); // Find the bot Player bot = null; room.State.players.ForEach((key, player) => { if (player.isBot) bot = player; }); Assert.IsNotNull(bot, "Should have a bot player"); Assert.IsTrue(bot.isBot); await room.Leave(); } [Test] public async Task HostAssignment() { var room = await client.Create("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); Assert.IsNotNull(room.State.host, "First player should be assigned as host"); Assert.AreEqual(room.SessionId, room.State.currentTurn, "First player should have the current turn"); await room.Leave(); } [Test] public async Task BroadcastMessage_Weather() { var room = await client.JoinOrCreate("my_room"); var weatherReceived = new TaskCompletionSource(); room.OnMessage("weather", (message) => { if (!weatherReceived.Task.IsCompleted) weatherReceived.TrySetResult(message); }); // Server broadcasts weather every 4 seconds var weather = await WithTimeout(weatherReceived.Task, 6000); Assert.IsNotNull(weather, "Should receive weather broadcast"); await room.Leave(); } [Test] public async Task MultipleClients_SeeEachOther() { var client2 = new Client("ws://localhost:2567"); var room1 = await client.Create("my_room"); var stateReceived1 = new TaskCompletionSource(); room1.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived1.Task.IsCompleted) stateReceived1.TrySetResult(true); }; await WithTimeout(stateReceived1.Task, 5000); // Second client joins the same room var room2 = await client2.JoinById(room1.RoomId); await Task.Delay(500); // Both clients should see both players Assert.AreEqual(2, room1.State.players.Count, "Room1 should see 2 players"); Assert.AreEqual(2, room2.State.players.Count, "Room2 should see 2 players"); Assert.IsNotNull(room1.State.players[room2.SessionId], "Room1 should see Room2's player"); Assert.IsNotNull(room2.State.players[room1.SessionId], "Room2 should see Room1's player"); await room1.Leave(); await room2.Leave(); } [Test] public async Task OnLeave_ConsentedLeave() { var room = await client.JoinOrCreate("my_room"); var leaveCode = new TaskCompletionSource(); room.OnLeave += (code) => { if (!leaveCode.Task.IsCompleted) leaveCode.TrySetResult(code); }; await room.Leave(consented: true); var code = await WithTimeout(leaveCode.Task, 5000); Assert.IsTrue(code > 0, $"Should receive a close code, got {code}"); } [Test] public async Task Reconnection_DropAndReconnect() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); // Send a move so we can verify state persists after reconnection await room.Send("move", new { x = 42, y = 84 }); await Task.Delay(200); var dropReceived = new TaskCompletionSource(); var reconnectReceived = new TaskCompletionSource(); room.OnDrop += (code) => { if (!dropReceived.Task.IsCompleted) dropReceived.TrySetResult(code); }; room.OnReconnect += () => { if (!reconnectReceived.Task.IsCompleted) reconnectReceived.TrySetResult(true); }; // Disable min uptime check so reconnection triggers immediately room.Reconnection.MinUptime = 0; // Force-drop the connection room.Connection.Drop(); var dropCode = await WithTimeout(dropReceived.Task, 5000); Assert.IsTrue( dropCode == (int)CloseCode.ABNORMAL_CLOSURE || dropCode == (int)CloseCode.NO_STATUS_RECEIVED || dropCode == (int)CloseCode.GOING_AWAY, $"Expected a reconnectable close code, got {dropCode}" ); var reconnected = await WithTimeout(reconnectReceived.Task, 10000); Assert.IsTrue(reconnected, "Should reconnect successfully"); // Verify state is preserved after reconnection await Task.Delay(200); var self = room.State.players[room.SessionId]; Assert.IsNotNull(self, "Player should still exist after reconnection"); Assert.AreEqual(42f, self.x, "X position should be preserved"); Assert.AreEqual(84f, self.y, "Y position should be preserved"); await room.Leave(); } [Test] public async Task Reconnection_PlayerDisconnectedFlag() { var client2 = new Client("ws://localhost:2567"); var room1 = await client.Create("my_room"); var stateReceived1 = new TaskCompletionSource(); room1.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived1.Task.IsCompleted) stateReceived1.TrySetResult(true); }; await WithTimeout(stateReceived1.Task, 5000); var room2 = await client2.JoinById(room1.RoomId); await Task.Delay(500); // Delay reconnection so we can observe the disconnected flag room2.Reconnection.MinUptime = 0; room2.Reconnection.MinDelay = 2000; room2.Reconnection.Delay = 2000; // Drop client2's connection room2.Connection.Drop(); // Wait for room1 to see the disconnected flag await Task.Delay(1000); var player2InRoom1 = room1.State.players[room2.SessionId]; Assert.IsNotNull(player2InRoom1, "Player2 should still exist (waiting for reconnect)"); Assert.IsTrue(player2InRoom1.disconnected, "Player2 should be marked as disconnected"); // Wait for room2 to reconnect var reconnectReceived = new TaskCompletionSource(); room2.OnReconnect += () => { if (!reconnectReceived.Task.IsCompleted) reconnectReceived.TrySetResult(true); }; await WithTimeout(reconnectReceived.Task, 10000); await Task.Delay(500); // Verify disconnected flag is cleared player2InRoom1 = room1.State.players[room2.SessionId]; Assert.IsFalse(player2InRoom1.disconnected, "Player2 should no longer be disconnected"); await room1.Leave(); await room2.Leave(); } [Test] public async Task Reconnection_SendMessageAfterReconnect() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); var reconnectReceived = new TaskCompletionSource(); room.OnReconnect += () => { if (!reconnectReceived.Task.IsCompleted) reconnectReceived.TrySetResult(true); }; room.Reconnection.MinUptime = 0; room.Connection.Drop(); await WithTimeout(reconnectReceived.Task, 10000); // Send a message after reconnection await room.Send("move", new { x = 999, y = 888 }); await Task.Delay(200); var self = room.State.players[room.SessionId]; Assert.AreEqual(999f, self.x, "Should process messages after reconnect"); Assert.AreEqual(888f, self.y, "Should process messages after reconnect"); await room.Leave(); } [Test] public async Task Callbacks_OnAdd_OnRemove_Players() { var client2 = new Client("ws://localhost:2567"); var room1 = await client.Create("my_room"); var stateReceived = new TaskCompletionSource(); room1.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); var callbacks = Callbacks.Get(room1); var addedSessionIds = new List(); var removedSessionIds = new List(); callbacks.OnAdd(state => state.players, (sessionId, player) => { addedSessionIds.Add(sessionId); }); callbacks.OnRemove(state => state.players, (sessionId, player) => { removedSessionIds.Add(sessionId); }); // Self should already trigger onAdd Assert.Contains(room1.SessionId, addedSessionIds); // Second client joins var room2 = await client2.JoinById(room1.RoomId); await Task.Delay(500); Assert.Contains(room2.SessionId, addedSessionIds); Assert.AreEqual(2, addedSessionIds.Count); // Second client leaves try { await room2.Leave(); } catch (TaskCanceledException) { } await Task.Delay(500); Assert.Contains(room2.SessionId, removedSessionIds); Assert.AreEqual(1, removedSessionIds.Count); try { await room1.Leave(); } catch (TaskCanceledException) { } } [Test] public async Task JoinRoom_DynamicSchema() { var room = await client.JoinOrCreate("my_room"); Assert.IsNotNull(room); Assert.IsNotNull(room.RoomId); Assert.IsNotNull(room.SessionId); Assert.AreEqual("my_room", room.Name); await room.Leave(); } [Test] public async Task ReceiveInitialState_DynamicSchema() { var room = await client.JoinOrCreate("my_room"); var stateReceived = new TaskCompletionSource(); room.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; var received = await WithTimeout(stateReceived.Task, 5000); Assert.IsTrue(received, "Should receive initial state"); var players = room.State.Get>("players"); Assert.IsNotNull(players); Assert.IsTrue(players.Count >= 1, "Should have at least 1 player (self)"); Assert.IsNotNull(players[room.SessionId], "Player should exist with own sessionId"); // Player should have an initial item (sword) var self = players[room.SessionId]; var items = self.Get>("items"); Assert.AreEqual(1, items.Count); Assert.AreEqual("sword", items[0].Get("name")); await room.Leave(); } [Test] public async Task Callbacks_OnAdd_OnRemove_Players_DynamicSchema() { var client2 = new Client("ws://localhost:2567"); var room1 = await client.Create("my_room"); var stateReceived = new TaskCompletionSource(); room1.OnStateChange += (state, isFirstState) => { if (isFirstState && !stateReceived.Task.IsCompleted) stateReceived.TrySetResult(true); }; await WithTimeout(stateReceived.Task, 5000); var callbacks = Callbacks.Get(room1); var addedSessionIds = new List(); var removedSessionIds = new List(); callbacks.OnAdd("players", (sessionId, player) => { addedSessionIds.Add(sessionId); }); callbacks.OnRemove("players", (sessionId, player) => { removedSessionIds.Add(sessionId); }); // Self should already trigger onAdd Assert.Contains(room1.SessionId, addedSessionIds); // Second client joins var room2 = await client2.JoinById(room1.RoomId); await Task.Delay(500); Assert.Contains(room2.SessionId, addedSessionIds); Assert.AreEqual(2, addedSessionIds.Count); // Second client leaves await room2.Leave(); await Task.Delay(500); Assert.Contains(room2.SessionId, removedSessionIds); Assert.AreEqual(1, removedSessionIds.Count); await room1.Leave(); } private async Task WithTimeout(Task task, int timeoutMs) { using var cts = new CancellationTokenSource(timeoutMs); var completedTask = await Task.WhenAny(task, Task.Delay(timeoutMs, cts.Token)); if (completedTask == task) { cts.Cancel(); return await task; } return default; } } } ================================================ FILE: nuget/tests/IntegrationTest.cs.meta ================================================ fileFormatVersion: 2 guid: 1dd617ab4c23b4a6fabaae0ab001d1d6 ================================================ FILE: nuget/tests/Schema/ArraySchemaClear/ArraySchemaClear.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.ArraySchemaClear { public partial class ArraySchemaClear : Schema { [Type(0, "array", typeof(ArraySchema), "number")] public ArraySchema items = null; } } ================================================ FILE: nuget/tests/Schema/ArraySchemaClear/ArraySchemaClear.cs.meta ================================================ fileFormatVersion: 2 guid: efb6c56f75f5d41e692dc5309b0c6c4d ================================================ FILE: nuget/tests/Schema/ArraySchemaClear.meta ================================================ fileFormatVersion: 2 guid: fb0074debb8384795a731ee7c4385cb3 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/ArraySchemaMultipleSplice/Item.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 1.0.24 // using Colyseus.Schema; namespace SchemaTest.ArraySchemaMultipleSplice { public partial class Item : Schema { [Type(0, "string")] public string name = default(string); } } ================================================ FILE: nuget/tests/Schema/ArraySchemaMultipleSplice/Item.cs.meta ================================================ fileFormatVersion: 2 guid: 4b7bdbaefecf04283828900305de33ef ================================================ FILE: nuget/tests/Schema/ArraySchemaMultipleSplice/MultipleArraySpliceState.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 1.0.24 // using Colyseus.Schema; namespace SchemaTest.ArraySchemaMultipleSplice { public partial class MultipleArraySpliceState : Schema { [Type(0, "ref", typeof(Player))] public Player player = new Player(); } } ================================================ FILE: nuget/tests/Schema/ArraySchemaMultipleSplice/MultipleArraySpliceState.cs.meta ================================================ fileFormatVersion: 2 guid: 1f1a24dd33355499f91b2bb008505f1b ================================================ FILE: nuget/tests/Schema/ArraySchemaMultipleSplice/Player.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 1.0.24 // using Colyseus.Schema; namespace SchemaTest.ArraySchemaMultipleSplice { public partial class Player : Schema { [Type(0, "array", typeof(ArraySchema))] public ArraySchema items = new ArraySchema(); } } ================================================ FILE: nuget/tests/Schema/ArraySchemaMultipleSplice/Player.cs.meta ================================================ fileFormatVersion: 2 guid: ad4f86486859c42aeaf0d80f1507f038 ================================================ FILE: nuget/tests/Schema/ArraySchemaMultipleSplice.meta ================================================ fileFormatVersion: 2 guid: 96d4e1107ea454d60a3f698ce9946363 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/ArraySchemaTypes/ArraySchemaTypes.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.ArraySchemaTypes { public partial class ArraySchemaTypes : Schema { [Type(0, "array", typeof(ArraySchema))] public ArraySchema arrayOfSchemas = null; [Type(1, "array", typeof(ArraySchema), "number")] public ArraySchema arrayOfNumbers = null; [Type(2, "array", typeof(ArraySchema), "string")] public ArraySchema arrayOfStrings = null; [Type(3, "array", typeof(ArraySchema), "int32")] public ArraySchema arrayOfInt32 = null; } } ================================================ FILE: nuget/tests/Schema/ArraySchemaTypes/ArraySchemaTypes.cs.meta ================================================ fileFormatVersion: 2 guid: a3927ef0021c52b4196710583e49c484 ================================================ FILE: nuget/tests/Schema/ArraySchemaTypes/IAmAChild.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.ArraySchemaTypes { public partial class IAmAChild : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); } } ================================================ FILE: nuget/tests/Schema/ArraySchemaTypes/IAmAChild.cs.meta ================================================ fileFormatVersion: 2 guid: 9f1340b75f249704e855879ab42d8f48 ================================================ FILE: nuget/tests/Schema/ArraySchemaTypes.meta ================================================ fileFormatVersion: 2 guid: c3adda012959e4e0a846dd5cfdcdea15 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/BackwardsForwards/PlayerV1.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.BackwardsForwards { public partial class PlayerV1 : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); } } ================================================ FILE: nuget/tests/Schema/BackwardsForwards/PlayerV1.cs.meta ================================================ fileFormatVersion: 2 guid: 1e608157ebaa98c4d8fe1d0ed75f23b0 ================================================ FILE: nuget/tests/Schema/BackwardsForwards/PlayerV2.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.BackwardsForwards { public partial class PlayerV2 : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); [Type(2, "string")] public string name = default(string); [Type(3, "array", typeof(ArraySchema), "string")] public ArraySchema arrayOfStrings = null; } } ================================================ FILE: nuget/tests/Schema/BackwardsForwards/PlayerV2.cs.meta ================================================ fileFormatVersion: 2 guid: a3492361ef7f0764ca8b5b9fc9cf0a61 ================================================ FILE: nuget/tests/Schema/BackwardsForwards/StateV1.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.BackwardsForwards { public partial class StateV1 : Schema { [Type(0, "string")] public string str = default(string); [Type(1, "map", typeof(MapSchema))] public MapSchema map = null; } } ================================================ FILE: nuget/tests/Schema/BackwardsForwards/StateV1.cs.meta ================================================ fileFormatVersion: 2 guid: e762e861c5ad4ea448baff51e42da830 ================================================ FILE: nuget/tests/Schema/BackwardsForwards/StateV2.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.BackwardsForwards { public partial class StateV2 : Schema { [Type(0, "string")] public string str = default(string); [System.Obsolete("field 'map' is deprecated.", true)] [Type(1, "map", typeof(MapSchema))] public MapSchema map = null; [Type(2, "number")] public float countdown = default(float); } } ================================================ FILE: nuget/tests/Schema/BackwardsForwards/StateV2.cs.meta ================================================ fileFormatVersion: 2 guid: f65997833ffe1394bbf8086ce588a544 ================================================ FILE: nuget/tests/Schema/BackwardsForwards.meta ================================================ fileFormatVersion: 2 guid: e5fc27d8b2ee949278f5888c893e2dfe folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/Callbacks/CallbacksState.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.45 // using Colyseus.Schema; namespace SchemaTest.Callbacks { public partial class CallbacksState : Schema { [Type(0, "ref", typeof(Container))] public Container container = null; } } ================================================ FILE: nuget/tests/Schema/Callbacks/CallbacksState.cs.meta ================================================ fileFormatVersion: 2 guid: 10d4dc8bba33dd94a82db82146eb2b11 ================================================ FILE: nuget/tests/Schema/Callbacks/Container.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.45 // using Colyseus.Schema; namespace SchemaTest.Callbacks { public partial class Container : Schema { [Type(0, "map", typeof(MapSchema))] public MapSchema playersMap = null; } } ================================================ FILE: nuget/tests/Schema/Callbacks/Container.cs.meta ================================================ fileFormatVersion: 2 guid: 541e8baeb06ddf04ca6c884331adb0ac ================================================ FILE: nuget/tests/Schema/Callbacks/Item.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.45 // using Colyseus.Schema; namespace SchemaTest.Callbacks { public partial class Item : Schema { [Type(0, "string")] public string name = default(string); [Type(1, "number")] public float value = default(float); } } ================================================ FILE: nuget/tests/Schema/Callbacks/Item.cs.meta ================================================ fileFormatVersion: 2 guid: 440bd948df0ba43f09c259a18f200cf3 ================================================ FILE: nuget/tests/Schema/Callbacks/Player.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.45 // using Colyseus.Schema; namespace SchemaTest.Callbacks { public partial class Player : Schema { [Type(0, "ref", typeof(Vec3))] public Vec3 position = null; [Type(1, "map", typeof(MapSchema))] public MapSchema items = null; } } ================================================ FILE: nuget/tests/Schema/Callbacks/Player.cs.meta ================================================ fileFormatVersion: 2 guid: e9172e24746504b44b7b798eed84ec22 ================================================ FILE: nuget/tests/Schema/Callbacks/Vec3.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.45 // using Colyseus.Schema; namespace SchemaTest.Callbacks { public partial class Vec3 : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); [Type(2, "number")] public float z = default(float); } } ================================================ FILE: nuget/tests/Schema/Callbacks/Vec3.cs.meta ================================================ fileFormatVersion: 2 guid: face922d9afe44f4cad0bdcf506493e4 ================================================ FILE: nuget/tests/Schema/Callbacks.meta ================================================ fileFormatVersion: 2 guid: ce95c7363114341e99ddcbfe8b308fbc folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/ChildSchemaTypes/ChildSchemaTypes.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.ChildSchemaTypes { public partial class ChildSchemaTypes : Schema { [Type(0, "ref", typeof(IAmAChild))] public IAmAChild child = null; [Type(1, "ref", typeof(IAmAChild))] public IAmAChild secondChild = null; } } ================================================ FILE: nuget/tests/Schema/ChildSchemaTypes/ChildSchemaTypes.cs.meta ================================================ fileFormatVersion: 2 guid: 46de100349a4e614dabe1c75c026bc3d ================================================ FILE: nuget/tests/Schema/ChildSchemaTypes/IAmAChild.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.ChildSchemaTypes { public partial class IAmAChild : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); } } ================================================ FILE: nuget/tests/Schema/ChildSchemaTypes/IAmAChild.cs.meta ================================================ fileFormatVersion: 2 guid: 30300a95b05cc9948adb45e084ca728e ================================================ FILE: nuget/tests/Schema/ChildSchemaTypes.meta ================================================ fileFormatVersion: 2 guid: 6946701a252264a3086bc0c141bc2366 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/FilteredTypes/Player.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.FilteredTypes { public partial class Player : Schema { [Type(0, "string")] public string name = default(string); } } ================================================ FILE: nuget/tests/Schema/FilteredTypes/Player.cs.meta ================================================ fileFormatVersion: 2 guid: bc4974b234009414d8fd3ff376e4b1c2 ================================================ FILE: nuget/tests/Schema/FilteredTypes/State.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.FilteredTypes { public partial class State : Schema { [Type(0, "ref", typeof(Player))] public Player playerOne = null; [Type(1, "ref", typeof(Player))] public Player playerTwo = null; [Type(2, "array", typeof(ArraySchema))] public ArraySchema players = null; } } ================================================ FILE: nuget/tests/Schema/FilteredTypes/State.cs.meta ================================================ fileFormatVersion: 2 guid: 2c4b58757e908dd45be8b14bd9e24240 ================================================ FILE: nuget/tests/Schema/FilteredTypes.meta ================================================ fileFormatVersion: 2 guid: 64881287456fa4a5d88b9f3c9a5dcfee folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/InheritedTypes/Bot.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.InheritedTypes { public partial class Bot : Player { [Type(3, "number")] public float power = default(float); } } ================================================ FILE: nuget/tests/Schema/InheritedTypes/Bot.cs.meta ================================================ fileFormatVersion: 2 guid: c3c3f288bd67b9245a5ce84735e31bdb ================================================ FILE: nuget/tests/Schema/InheritedTypes/Entity.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.InheritedTypes { public partial class Entity : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); } } ================================================ FILE: nuget/tests/Schema/InheritedTypes/Entity.cs.meta ================================================ fileFormatVersion: 2 guid: f46d8182fac8cd241a7f3276255ca903 ================================================ FILE: nuget/tests/Schema/InheritedTypes/InheritedTypes.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.InheritedTypes { public partial class InheritedTypes : Schema { [Type(0, "ref", typeof(Entity))] public Entity entity = null; [Type(1, "ref", typeof(Player))] public Player player = null; [Type(2, "ref", typeof(Bot))] public Bot bot = null; [Type(3, "ref", typeof(Entity))] public Entity any = null; } } ================================================ FILE: nuget/tests/Schema/InheritedTypes/InheritedTypes.cs.meta ================================================ fileFormatVersion: 2 guid: 53219bd4741d8b54a870c7133650fb0e ================================================ FILE: nuget/tests/Schema/InheritedTypes/Player.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.InheritedTypes { public partial class Player : Entity { [Type(2, "string")] public string name = default(string); } } ================================================ FILE: nuget/tests/Schema/InheritedTypes/Player.cs.meta ================================================ fileFormatVersion: 2 guid: dc399e0dbf94ed845877a4c73a7651ec ================================================ FILE: nuget/tests/Schema/InheritedTypes.meta ================================================ fileFormatVersion: 2 guid: 58ba81f9d6c3049de8c9e6b48d494636 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/InstanceSharingTypes/Player.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.InstanceSharingTypes { public partial class Player : Schema { [Type(0, "ref", typeof(Position))] public Position position = null; } } ================================================ FILE: nuget/tests/Schema/InstanceSharingTypes/Player.cs.meta ================================================ fileFormatVersion: 2 guid: d62eb91e2d260a64e90ab3a7cdcc5cb7 ================================================ FILE: nuget/tests/Schema/InstanceSharingTypes/Position.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.InstanceSharingTypes { public partial class Position : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); } } ================================================ FILE: nuget/tests/Schema/InstanceSharingTypes/Position.cs.meta ================================================ fileFormatVersion: 2 guid: 766571b4ac054a446829d35adf3f9a72 ================================================ FILE: nuget/tests/Schema/InstanceSharingTypes/State.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.InstanceSharingTypes { public partial class State : Schema { [Type(0, "ref", typeof(Player))] public Player player1 = null; [Type(1, "ref", typeof(Player))] public Player player2 = null; [Type(2, "array", typeof(ArraySchema))] public ArraySchema arrayOfPlayers = null; [Type(3, "map", typeof(MapSchema))] public MapSchema mapOfPlayers = null; } } ================================================ FILE: nuget/tests/Schema/InstanceSharingTypes/State.cs.meta ================================================ fileFormatVersion: 2 guid: f2b5eaaa9a8f1fe41a4fb27f451b0937 ================================================ FILE: nuget/tests/Schema/InstanceSharingTypes.meta ================================================ fileFormatVersion: 2 guid: 664875c2b40d24cea818044701949bfa folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/Item.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 4.0.7 // using Colyseus.Schema; #if UNITY_5_3_OR_NEWER using UnityEngine.Scripting; #endif public partial class Item : Schema { #if UNITY_5_3_OR_NEWER [Preserve] #endif public Item() { } [Type(0, "string")] public string name = default(string); [Type(1, "number")] public float value = default(float); } ================================================ FILE: nuget/tests/Schema/Item.cs.meta ================================================ fileFormatVersion: 2 guid: a5434d2f269ce49ae8f236757533aac8 ================================================ FILE: nuget/tests/Schema/MapSchemaInt8/MapSchemaInt8.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.MapSchemaInt8 { public partial class MapSchemaInt8 : Schema { [Type(0, "string")] public string status = default(string); [Type(1, "map", typeof(MapSchema), "int8")] public MapSchema mapOfInt8 = null; } } ================================================ FILE: nuget/tests/Schema/MapSchemaInt8/MapSchemaInt8.cs.meta ================================================ fileFormatVersion: 2 guid: 08bbf096d257fa74bb2ffc19538fafb9 ================================================ FILE: nuget/tests/Schema/MapSchemaInt8.meta ================================================ fileFormatVersion: 2 guid: 251e6c825126f4972bd0e7449445757d folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/MapSchemaMoveNullifyType/State.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.MapSchemaMoveNullifyType { public partial class State : Schema { [Type(0, "map", typeof(MapSchema), "number")] public MapSchema previous = null; [Type(1, "map", typeof(MapSchema), "number")] public MapSchema current = null; } } ================================================ FILE: nuget/tests/Schema/MapSchemaMoveNullifyType/State.cs.meta ================================================ fileFormatVersion: 2 guid: 8eb639901cc8eeb428128815a12eb093 ================================================ FILE: nuget/tests/Schema/MapSchemaMoveNullifyType.meta ================================================ fileFormatVersion: 2 guid: 85d943bc466e24cfa9226aa0f1856e32 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/MapSchemaTypes/IAmAChild.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.MapSchemaTypes { public partial class IAmAChild : Schema { [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); } } ================================================ FILE: nuget/tests/Schema/MapSchemaTypes/IAmAChild.cs.meta ================================================ fileFormatVersion: 2 guid: 90a7e6eba5991bf478ca7a73f485a4d2 ================================================ FILE: nuget/tests/Schema/MapSchemaTypes/MapSchemaTypes.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.MapSchemaTypes { public partial class MapSchemaTypes : Schema { [Type(0, "map", typeof(MapSchema))] public MapSchema mapOfSchemas = null; [Type(1, "map", typeof(MapSchema), "number")] public MapSchema mapOfNumbers = null; [Type(2, "map", typeof(MapSchema), "string")] public MapSchema mapOfStrings = null; [Type(3, "map", typeof(MapSchema), "int32")] public MapSchema mapOfInt32 = null; } } ================================================ FILE: nuget/tests/Schema/MapSchemaTypes/MapSchemaTypes.cs.meta ================================================ fileFormatVersion: 2 guid: f37944f2f0e8234479f7b069caa80b6b ================================================ FILE: nuget/tests/Schema/MapSchemaTypes.meta ================================================ fileFormatVersion: 2 guid: 16a64debc13484ba3bc6ead2277ccf57 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema/MyRoomState.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 4.0.7 // using Colyseus.Schema; #if UNITY_5_3_OR_NEWER using UnityEngine.Scripting; #endif public partial class MyRoomState : Schema { #if UNITY_5_3_OR_NEWER [Preserve] #endif public MyRoomState() { } [Type(0, "map", typeof(MapSchema))] public MapSchema players = null; [Type(1, "ref", typeof(Player))] public Player host = null; [Type(2, "string")] public string currentTurn = default(string); } ================================================ FILE: nuget/tests/Schema/MyRoomState.cs.meta ================================================ fileFormatVersion: 2 guid: e9fff1bd47aac4e678f9f8aa1d39aa62 ================================================ FILE: nuget/tests/Schema/Player.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 4.0.7 // using Colyseus.Schema; #if UNITY_5_3_OR_NEWER using UnityEngine.Scripting; #endif public partial class Player : Schema { #if UNITY_5_3_OR_NEWER [Preserve] #endif public Player() { } [Type(0, "number")] public float x = default(float); [Type(1, "number")] public float y = default(float); [Type(2, "boolean")] public bool isBot = default(bool); [Type(3, "boolean")] public bool disconnected = default(bool); [Type(4, "array", typeof(ArraySchema))] public ArraySchema items = null; } ================================================ FILE: nuget/tests/Schema/Player.cs.meta ================================================ fileFormatVersion: 2 guid: 1b23527f145f54602bb507f95baa577a ================================================ FILE: nuget/tests/Schema/PrimitiveTypes/PrimitiveTypes.cs ================================================ // // THIS FILE HAS BEEN GENERATED AUTOMATICALLY // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING // // GENERATED USING @colyseus/schema 3.0.0-alpha.40 // using Colyseus.Schema; namespace SchemaTest.PrimitiveTypes { public partial class PrimitiveTypes : Schema { [Type(0, "int8")] public sbyte int8 = default(sbyte); [Type(1, "uint8")] public byte uint8 = default(byte); [Type(2, "int16")] public short int16 = default(short); [Type(3, "uint16")] public ushort uint16 = default(ushort); [Type(4, "int32")] public int int32 = default(int); [Type(5, "uint32")] public uint uint32 = default(uint); [Type(6, "int64")] public long int64 = default(long); [Type(7, "uint64")] public ulong uint64 = default(ulong); [Type(8, "float32")] public float float32 = default(float); [Type(9, "float64")] public double float64 = default(double); [Type(10, "number")] public float varint_int8 = default(float); [Type(11, "number")] public float varint_uint8 = default(float); [Type(12, "number")] public float varint_int16 = default(float); [Type(13, "number")] public float varint_uint16 = default(float); [Type(14, "number")] public float varint_int32 = default(float); [Type(15, "number")] public float varint_uint32 = default(float); [Type(16, "number")] public float varint_int64 = default(float); [Type(17, "number")] public float varint_uint64 = default(float); [Type(18, "number")] public float varint_float32 = default(float); [Type(19, "number")] public float varint_float64 = default(float); [Type(20, "string")] public string str = default(string); [Type(21, "boolean")] public bool boolean = default(bool); } } ================================================ FILE: nuget/tests/Schema/PrimitiveTypes/PrimitiveTypes.cs.meta ================================================ fileFormatVersion: 2 guid: 9337f20e96c6911449e3eaeeb2caa451 ================================================ FILE: nuget/tests/Schema/PrimitiveTypes.meta ================================================ fileFormatVersion: 2 guid: 1932739676ae34fcb94ed29a8b6c8c37 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/Schema.meta ================================================ fileFormatVersion: 2 guid: e239053f82903c846802ac1616263fcc folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: nuget/tests/SchemaDeserializerTest.cs ================================================ using NUnit.Framework; using Colyseus.Schema; namespace Colyseus.Tests { [TestFixture] public class SchemaDeserializerTest { [Test] public void PrimitiveTypesTest() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; byte[] bytes = { 128, 128, 129, 255, 130, 0, 128, 131, 255, 255, 132, 0, 0, 0, 128, 133, 255, 255, 255, 255, 134, 0, 0, 0, 0, 0, 0, 0, 128, 135, 255, 255, 255, 255, 255, 255, 31, 0, 136, 204, 204, 204, 253, 137, 255, 255, 255, 255, 255, 255, 239, 127, 138, 208, 128, 139, 204, 255, 140, 209, 0, 128, 141, 205, 255, 255, 142, 210, 0, 0, 0, 128, 143, 203, 0, 0, 224, 255, 255, 255, 239, 65, 144, 203, 0, 0, 0, 0, 0, 0, 224, 195, 145, 203, 255, 255, 255, 255, 255, 255, 63, 67, 146, 203, 61, 255, 145, 224, 255, 255, 239, 199, 147, 203, 153, 153, 153, 153, 153, 153, 185, 127, 148, 171, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 149, 1 }; decoder.Decode(bytes); Assert.AreEqual(state.int8, -128); Assert.AreEqual(state.uint8, 255); Assert.AreEqual(state.int16, -32768); Assert.AreEqual(state.uint16, 65535); Assert.AreEqual(state.int32, -2147483648); Assert.AreEqual(state.uint32, 4294967295); Assert.AreEqual(state.int64, -9223372036854775808); Assert.AreEqual(state.uint64, 9007199254740991); Assert.AreEqual(state.float32, -3.40282347E+37f); Assert.AreEqual(state.float64, 1.7976931348623157e+308); Assert.AreEqual(state.varint_int8, -128); Assert.AreEqual(state.varint_uint8, 255); Assert.AreEqual(state.varint_int16, -32768); Assert.AreEqual(state.varint_uint16, 65535); Assert.AreEqual(state.varint_int32, -2147483648); Assert.AreEqual(state.varint_uint32, 4294967295); Assert.AreEqual(state.varint_int64, -9223372036854775808); Assert.AreEqual(state.varint_uint64, 9007199254740991); Assert.AreEqual(state.varint_float32, -3.40282347E+38f); Assert.AreEqual(state.varint_float64, float.PositiveInfinity); Assert.AreEqual(state.str, "Hello world"); Assert.AreEqual(state.boolean, true); } [Test] public void ChildSchemaTypesTest() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; byte[] bytes = { 128, 1, 129, 2, 255, 1, 128, 205, 244, 1, 129, 205, 32, 3, 255, 2, 128, 204, 200, 129, 205, 44, 1 }; decoder.Decode(bytes); Assert.AreEqual(state.child.x, 500); Assert.AreEqual(state.child.y, 800); Assert.AreEqual(state.secondChild.x, 200); Assert.AreEqual(state.secondChild.y, 300); } [Test] public void ArraySchemaTypesTest() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; 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 }; var callbacks = Colyseus.Schema.Callbacks.Get(decoder); decoder.Decode(bytes); var arrayOfSchemasOnAdd = 0; var arrayOfNumbersOnAdd = 0; var arrayOfStringsOnAdd = 0; var arrayOfInt32OnAdd = 0; callbacks.OnAdd(state => state.arrayOfSchemas, (key, value) => arrayOfSchemasOnAdd++); callbacks.OnAdd(state => state.arrayOfNumbers, (key, value) => arrayOfNumbersOnAdd++); callbacks.OnAdd(state => state.arrayOfStrings, (key, value) => arrayOfStringsOnAdd++); callbacks.OnAdd(state => state.arrayOfInt32, (key, value) => arrayOfInt32OnAdd++); Assert.AreEqual(2, arrayOfSchemasOnAdd); Assert.AreEqual(4, arrayOfNumbersOnAdd); Assert.AreEqual(3, arrayOfStringsOnAdd); Assert.AreEqual(3, arrayOfInt32OnAdd); 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]); var arrayOfSchemasOnRemove = 0; var arrayOfNumbersOnRemove = 0; var arrayOfStringsOnRemove = 0; var arrayOfInt32OnRemove = 0; callbacks.OnRemove(state => state.arrayOfSchemas, (key, value) => arrayOfSchemasOnRemove++); callbacks.OnRemove(state => state.arrayOfNumbers, (key, value) => arrayOfNumbersOnRemove++); callbacks.OnRemove(state => state.arrayOfStrings, (key, value) => arrayOfStringsOnRemove++); callbacks.OnRemove(state => state.arrayOfInt32, (key, value) => arrayOfInt32OnRemove++); // 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 }; 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 }; decoder.Decode(popBytes); Assert.AreEqual(1, state.arrayOfSchemas.Count); Assert.AreEqual(1, state.arrayOfNumbers.Count); Assert.AreEqual(1, state.arrayOfStrings.Count); Assert.AreEqual(1, state.arrayOfInt32.Count); Assert.AreEqual(1, arrayOfSchemasOnRemove); Assert.AreEqual(3, arrayOfNumbersOnRemove); Assert.AreEqual(2, arrayOfStringsOnRemove); Assert.AreEqual(2, arrayOfInt32OnRemove); // re-initialize ArraySchema's decoder.Decode(new byte[] { 128, 7, 129, 8, 131, 9, 130, 10, 255, 7, 255, 8, 255, 9, 255, 10 }); Assert.AreEqual(0, state.arrayOfSchemas.Count); Assert.AreEqual(0, state.arrayOfNumbers.Count); Assert.AreEqual(0, state.arrayOfStrings.Count); Assert.AreEqual(0, state.arrayOfInt32.Count); } [Test] public void ArraySchemaClearTest() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; int onAddCount = 0; int onRemoveCount = 0; var callbacks = Colyseus.Schema.Callbacks.Get(decoder); callbacks.OnAdd(state => state.items, (key, value) => onAddCount++); callbacks.OnRemove(state => state.items, (key, value) => onRemoveCount++); byte[] bytes = { 128, 1, 255, 1, 128, 0, 1, 128, 1, 2, 128, 2, 3, 128, 3, 4, 128, 4, 5 }; decoder.Decode(bytes); Assert.AreEqual(5, onAddCount); Assert.AreEqual(0, onRemoveCount); byte[] clearBytes = { 255, 1, 10, 255, 1 }; decoder.Decode(clearBytes); Assert.AreEqual(5, onAddCount); Assert.AreEqual(5, onRemoveCount); byte[] reAddBytes = { 255, 1, 128, 0, 1, 128, 1, 2, 128, 2, 3, 128, 3, 4, 128, 4, 5, 255, 1, 255, 1 }; decoder.Decode(reAddBytes); Assert.AreEqual(10, onAddCount); Assert.AreEqual(5, onRemoveCount); } [Test] public void MapSchemaTypesTest() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; 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 }; var callbacks = Colyseus.Schema.Callbacks.Get(decoder); var mapOfSchemasAdd = 0; var mapOfNumbersAdd = 0; var mapOfStringsAdd = 0; var mapOfIntAdd = 0; callbacks.OnAdd(state => state.mapOfSchemas, (key, value) => mapOfSchemasAdd++); callbacks.OnAdd(state => state.mapOfNumbers, (key, value) => mapOfNumbersAdd++); callbacks.OnAdd(state => state.mapOfStrings, (key, value) => mapOfStringsAdd++); callbacks.OnAdd(state => state.mapOfInt32, (key, value) => mapOfIntAdd++); var mapOfSchemasRemove = 0; var mapOfNumbersRemove = 0; var mapOfStringsRemove = 0; var mapOfIntRemove = 0; callbacks.OnRemove(state => state.mapOfSchemas, (key, value) => mapOfSchemasRemove++); callbacks.OnRemove(state => state.mapOfNumbers, (key, value) => mapOfNumbersRemove++); callbacks.OnRemove(state => state.mapOfStrings, (key, value) => mapOfStringsRemove++); callbacks.OnRemove(state => state.mapOfInt32, (key, value) => mapOfIntRemove++); var mapOfSchemasChange = 0; var mapOfNumbersChange = 0; var mapOfStringsChange = 0; var mapOfIntChange = 0; callbacks.OnChange(state => state.mapOfSchemas, (key, value) => mapOfSchemasChange++); callbacks.OnChange(state => state.mapOfNumbers, (key, value) => mapOfNumbersChange++); callbacks.OnChange(state => state.mapOfStrings, (key, value) => mapOfStringsChange++); callbacks.OnChange(state => state.mapOfInt32, (key, value) => mapOfIntChange++); decoder.Decode(bytes); 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); Assert.AreEqual(mapOfSchemasAdd, 3); Assert.AreEqual(mapOfNumbersAdd, 3); Assert.AreEqual(mapOfStringsAdd, 3); Assert.AreEqual(mapOfIntAdd, 3); 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 }; decoder.Decode(deleteBytes); Assert.AreEqual(state.mapOfSchemas.Count, 1); Assert.AreEqual(state.mapOfNumbers.Count, 1); Assert.AreEqual(state.mapOfStrings.Count, 1); Assert.AreEqual(state.mapOfInt32.Count, 1); Assert.AreEqual(mapOfSchemasRemove, 2); Assert.AreEqual(mapOfNumbersRemove, 2); Assert.AreEqual(mapOfStringsRemove, 2); Assert.AreEqual(mapOfIntRemove, 2); Assert.AreEqual(mapOfSchemasChange, 5); Assert.AreEqual(mapOfNumbersChange, 5); Assert.AreEqual(mapOfStringsChange, 5); Assert.AreEqual(mapOfIntChange, 5); } [Test] public void MapSchemaInt8Test() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; 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 }; decoder.Decode(bytes); 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 }; decoder.Decode(addBytes); 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); } [Test] public void MapSchemaMoveNullifyTypeTest() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; byte[] bytes = { 129, 1, 64, 255, 1, 128, 0, 161, 48, 0 }; decoder.Decode(bytes); Assert.DoesNotThrow(() => { // FIXME: this test only passes because empty byte[] moveAndNullifyBytes = { 128, 1, 65 }; decoder.Decode(moveAndNullifyBytes); }); } [Test] public void InheritedTypesTest() { var serializer = new Colyseus.SchemaSerializer(); byte[] handshake = { 128, 1, 255, 1, 128, 0, 2, 128, 1, 8, 128, 2, 12, 128, 3, 15, 255, 2, 130, 3, 128, 0, 255, 3, 128, 0, 4, 128, 1, 5, 128, 2, 6, 128, 3, 7, 255, 4, 128, 166, 101, 110, 116, 105, 116, 121, 130, 1, 129, 163, 114, 101, 102, 255, 5, 128, 166, 112, 108, 97, 121, 101, 114, 130, 2, 129, 163, 114, 101, 102, 255, 6, 128, 163, 98, 111, 116, 130, 3, 129, 163, 114, 101, 102, 255, 7, 128, 163, 97, 110, 121, 130, 1, 129, 163, 114, 101, 102, 255, 8, 130, 9, 128, 1, 255, 9, 128, 0, 10, 128, 1, 11, 255, 10, 128, 161, 120, 129, 166, 110, 117, 109, 98, 101, 114, 255, 11, 128, 161, 121, 129, 166, 110, 117, 109, 98, 101, 114, 255, 12, 130, 13, 128, 2, 129, 1, 255, 13, 128, 0, 14, 255, 14, 128, 164, 110, 97, 109, 101, 129, 166, 115, 116, 114, 105, 110, 103, 255, 15, 130, 16, 128, 3, 129, 2, 255, 16, 128, 0, 17, 255, 17, 128, 165, 112, 111, 119, 101, 114, 129, 166, 110, 117, 109, 98, 101, 114 }; serializer.Handshake(handshake, 0); byte[] bytes = { 128, 1, 129, 2, 130, 3, 131, 4, 213, 3, 255, 1, 128, 205, 244, 1, 129, 205, 32, 3, 255, 2, 128, 204, 200, 129, 205, 44, 1, 130, 166, 80, 108, 97, 121, 101, 114, 255, 3, 128, 100, 129, 204, 150, 130, 163, 66, 111, 116, 131, 204, 200, 255, 4, 131, 100 }; serializer.SetState(bytes); var state = serializer.GetState(); Assert.IsInstanceOf(typeof(SchemaTest.InheritedTypes.Entity), state.entity); Assert.AreEqual(state.entity.x, 500); Assert.AreEqual(state.entity.y, 800); Assert.IsInstanceOf(typeof(SchemaTest.InheritedTypes.Player), state.player); Assert.AreEqual(state.player.x, 200); Assert.AreEqual(state.player.y, 300); Assert.AreEqual(state.player.name, "Player"); Assert.IsInstanceOf(typeof(SchemaTest.InheritedTypes.Bot), state.bot); Assert.AreEqual(state.bot.x, 100); Assert.AreEqual(state.bot.y, 150); Assert.AreEqual(state.bot.name, "Bot"); Assert.AreEqual(state.bot.power, 200); Assert.IsInstanceOf(typeof(SchemaTest.InheritedTypes.Bot), state.any); } [Test] public void BackwardsForwardsTest() { byte[] statev1bytes = { 129, 1, 128, 171, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 255, 1, 128, 0, 163, 111, 110, 101, 2, 255, 2, 128, 203, 232, 229, 22, 37, 231, 231, 209, 63, 129, 203, 240, 138, 15, 5, 219, 40, 223, 63 }; byte[] statev2bytes = { 128, 171, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 130, 10 }; var v2decoder = new Colyseus.Schema.Decoder(); var statev2 = v2decoder.State; v2decoder.Decode(statev1bytes); Assert.AreEqual(statev2.str, "Hello world"); var v1decoder = new Colyseus.Schema.Decoder(); var statev1 = v1decoder.State; v1decoder.Decode(statev2bytes); Assert.AreEqual(statev1.str, "Hello world"); } [Test] public void FilteredTypesTest() { // var client1 = new SchemaTest.FilteredTypes.State(); // client1.Decode(new byte[] { 255, 0, 130, 1, 128, 2, 128, 2, 255, 1, 128, 0, 4, 255, 2, 128, 163, 111, 110, 101, 255, 2, 128, 163, 111, 110, 101, 255, 4, 128, 163, 111, 110, 101 }); // Assert.AreEqual("one", client1.playerOne.name); // Assert.AreEqual("one", client1.players[0].name); // Assert.AreEqual(null, client1.playerTwo.name); // var client2 = new SchemaTest.FilteredTypes.State(); // client2.Decode(new byte[] { 255, 0, 130, 1, 129, 3, 129, 3, 255, 1, 128, 1, 5, 255, 3, 128, 163, 116, 119, 111, 255, 3, 128, 163, 116, 119, 111, 255, 5, 128, 163, 116, 119, 111 }); // Assert.AreEqual("two", client2.playerTwo.name); // Assert.AreEqual("two", client2.players[0].name); // Assert.AreEqual(null, client2.playerOne.name); } [Test] public void InstanceSharingTypesTest() { var decoder = new Colyseus.Schema.Decoder(); var refs = decoder.Refs; var state = decoder.State; decoder.Decode(new byte[] { 130, 1, 131, 2, 128, 3, 129, 3, 255, 1, 255, 2, 255, 3, 128, 4, 255, 4, 128, 10, 129, 10 }); Assert.AreEqual(state.player1, state.player2); Assert.AreEqual(state.player1.position, state.player2.position); Assert.AreEqual(refs.refCounts[state.player1.__refId], 2); Assert.AreEqual(5, refs.refs.Count); decoder.Decode(new byte[] { 64, 65 }); Assert.AreEqual(null, state.player2); Assert.AreEqual(null, state.player2); Assert.AreEqual(3, refs.refs.Count); decoder.Decode(new byte[] { 255, 1, 128, 0, 5, 128, 1, 5, 128, 2, 5, 128, 3, 7, 255, 5, 128, 6, 255, 6, 128, 10, 129, 10, 255, 7, 128, 8, 255, 8, 128, 10, 129, 10 }); Assert.AreEqual(state.arrayOfPlayers[0], state.arrayOfPlayers[1]); Assert.AreEqual(state.arrayOfPlayers[1], state.arrayOfPlayers[2]); Assert.AreNotEqual(state.arrayOfPlayers[2], state.arrayOfPlayers[3]); Assert.AreEqual(7, refs.refs.Count); decoder.Decode(new byte[] { 255, 1, 64, 3, 64, 2, 64, 1 }); Assert.AreEqual(1, state.arrayOfPlayers.Count); Assert.AreEqual(5, refs.refs.Count); var previousArraySchemaRefId = state.arrayOfPlayers.__refId; // Replacing ArraySchema decoder.Decode(new byte[] { 194, 9, 255, 9, 128, 0, 10, 255, 10, 128, 11, 255, 11, 128, 10, 129, 20 }); Assert.AreEqual(false, refs.refs.ContainsKey(previousArraySchemaRefId)); Assert.AreEqual(1, state.arrayOfPlayers.Count); Assert.AreEqual(5, refs.refs.Count); // Clearing ArraySchema decoder.Decode(new byte[] { 255, 9, 10 }); Assert.AreEqual(0, state.arrayOfPlayers.Count); Assert.AreEqual(3, refs.refs.Count); } [Test] public void CallbacksTest() { var decoder = new Colyseus.Schema.Decoder(); var state = decoder.State; var callbacks = Colyseus.Schema.Callbacks.Get(decoder); var onListenContainer = 0; var onPlayerAdd = 0; var onPlayerChange = 0; var onPlayerRemove = 0; var onItemAdd = 0; var onItemChange = 0; var onItemRemove = 0; callbacks.Listen(state => state.container, (container, _) => { onListenContainer++; callbacks.OnAdd(container, container => container.playersMap, (sessionId, player) => { onPlayerAdd++; callbacks.OnAdd(player, player => player.items, (key, item) => { onItemAdd++; }); callbacks.OnChange(player, player => player.items, (key, item) => { onItemChange++; }); callbacks.OnRemove(player, player => player.items, (key, item) => { onItemRemove++; }); }); callbacks.OnChange(container, container => container.playersMap, (sessionId, player) => { onPlayerChange++; }); callbacks.OnRemove(container, container => container.playersMap, (sessionId, player) => { onPlayerRemove++; }); }); // (initial) decoder.Decode(new byte[] { 128, 1, 255, 1, 128, 2, 255, 2 }); // (1st encode) decoder.Decode(new byte[] { 255, 1, 255, 2, 128, 0, 163, 111, 110, 101, 3, 128, 1, 163, 116, 119, 111, 9, 255, 2, 255, 3, 128, 4, 129, 5, 255, 4, 128, 1, 129, 2, 130, 3, 255, 5, 128, 0, 166, 105, 116, 101, 109, 45, 49, 6, 128, 1, 166, 105, 116, 101, 109, 45, 50, 7, 128, 2, 166, 105, 116, 101, 109, 45, 51, 8, 255, 6, 128, 166, 73, 116, 101, 109, 32, 49, 129, 1, 255, 7, 128, 166, 73, 116, 101, 109, 32, 50, 129, 2, 255, 8, 128, 166, 73, 116, 101, 109, 32, 51, 129, 3, 255, 9, 128, 10, 129, 11, 255, 10, 128, 1, 129, 2, 130, 3, 255, 11, 128, 0, 166, 105, 116, 101, 109, 45, 49, 12, 128, 1, 166, 105, 116, 101, 109, 45, 50, 13, 128, 2, 166, 105, 116, 101, 109, 45, 51, 14, 255, 12, 128, 166, 73, 116, 101, 109, 32, 49, 129, 1, 255, 13, 128, 166, 73, 116, 101, 109, 32, 50, 129, 2, 255, 14, 128, 166, 73, 116, 101, 109, 32, 51, 129, 3 }); Assert.AreEqual(1, onListenContainer); Assert.AreEqual(2, onPlayerAdd); Assert.AreEqual(2, onPlayerChange); Assert.AreEqual(6, onItemAdd); Assert.AreEqual(6, onItemChange); // (2nd encode) decoder.Decode(new byte[] { 255, 1, 255, 2, 64, 1, 128, 2, 165, 116, 104, 114, 101, 101, 16, 255, 2, 255, 3, 255, 4, 255, 5, 64, 0, 64, 1, 128, 3, 166, 105, 116, 101, 109, 45, 52, 15, 255, 8, 255, 5, 255, 5, 255, 15, 128, 166, 73, 116, 101, 109, 32, 52, 129, 4, 255, 2, 255, 16, 128, 17, 129, 18, 255, 17, 128, 1, 129, 2, 130, 3, 255, 18, 128, 0, 166, 105, 116, 101, 109, 45, 49, 19, 128, 1, 166, 105, 116, 101, 109, 45, 50, 20, 128, 2, 166, 105, 116, 101, 109, 45, 51, 21, 255, 19, 128, 166, 73, 116, 101, 109, 32, 49, 129, 1, 255, 20, 128, 166, 73, 116, 101, 109, 32, 50, 129, 2, 255, 21, 128, 166, 73, 116, 101, 109, 32, 51, 129, 3 }); // (new container) decoder.Decode(new byte[] { 128, 22, 255, 2, 255, 5, 255, 5, 255, 2, 255, 0, 255, 22, 128, 23, 255, 23, 128, 0, 164, 108, 97, 115, 116, 24, 255, 24, 128, 25, 129, 26, 255, 25, 128, 10, 129, 10, 130, 10, 255, 26, 128, 0, 163, 111, 110, 101, 27, 255, 27, 128, 166, 73, 116, 101, 109, 32, 49, 129, 1 }); Assert.AreEqual(2, onListenContainer); Assert.AreEqual(4, onPlayerAdd); Assert.AreEqual(1, onPlayerRemove); Assert.AreEqual(5, onPlayerChange); Assert.AreEqual(11, onItemAdd); Assert.AreEqual(2, onItemRemove); Assert.AreEqual(13, onItemChange); } [Test] public void ReflectionDecodeTest() { byte[] handshake = { 128, 1, 255, 1, 128, 0, 2, 128, 1, 8, 128, 2, 12, 128, 3, 15, 255, 2, 130, 3, 128, 0, 255, 3, 128, 0, 4, 128, 1, 5, 128, 2, 6, 128, 3, 7, 255, 4, 128, 166, 101, 110, 116, 105, 116, 121, 130, 1, 129, 163, 114, 101, 102, 255, 5, 128, 166, 112, 108, 97, 121, 101, 114, 130, 2, 129, 163, 114, 101, 102, 255, 6, 128, 163, 98, 111, 116, 130, 3, 129, 163, 114, 101, 102, 255, 7, 128, 163, 97, 110, 121, 130, 1, 129, 163, 114, 101, 102, 255, 8, 130, 9, 128, 1, 255, 9, 128, 0, 10, 128, 1, 11, 255, 10, 128, 161, 120, 129, 166, 110, 117, 109, 98, 101, 114, 255, 11, 128, 161, 121, 129, 166, 110, 117, 109, 98, 101, 114, 255, 12, 130, 13, 128, 2, 129, 1, 255, 13, 128, 0, 14, 255, 14, 128, 164, 110, 97, 109, 101, 129, 166, 115, 116, 114, 105, 110, 103, 255, 15, 130, 16, 128, 3, 129, 2, 255, 16, 128, 0, 17, 255, 17, 128, 165, 112, 111, 119, 101, 114, 129, 166, 110, 117, 109, 98, 101, 114 }; var it = new Iterator { Offset = 0 }; var reflectionDecoder = new Decoder(); reflectionDecoder.Decode(handshake, it); var reflection = reflectionDecoder.State; Assert.IsNotNull(reflection.types, "types should not be null"); Assert.Greater(reflection.types.Count, 0, "types should have entries"); Assert.AreEqual(4, reflection.types.Count, $"expected 4 types, rootType={reflection.rootType}"); for (int i = 0; i < reflection.types.Count; i++) { var t = reflection.types[i]; Assert.IsNotNull(t.fields, $"Type[{i}] id={t.id} should have fields"); } } } } ================================================ FILE: nuget/tests/SchemaDeserializerTest.cs.meta ================================================ fileFormatVersion: 2 guid: ddcb67059f6353642b43a764acd9b4fc ================================================ FILE: nuget/tests/SerializationTest.cs ================================================ using NUnit.Framework; using Colyseus; namespace Colyseus.Tests { [TestFixture] public class SerializationTest { [Test] public void NoStateSerializerTest() { var serializer = (ISerializer)new NoneSerializer(); Assert.AreEqual(true, serializer.GetState() is NoState); } } } ================================================ FILE: nuget/tests/SerializationTest.cs.meta ================================================ fileFormatVersion: 2 guid: e8fcb142d5e2049488327be26fbf22bc ================================================ FILE: nuget/tests/WebSocketTransportTest.cs ================================================ #if !UNITY_EDITOR using System; using System.Threading; using NUnit.Framework; namespace Colyseus.Tests { [TestFixture] [NonParallelizable] public class WebSocketTransportTest { private Action _previousRegister; private Action _previousUnregister; [SetUp] public void SetUp() { _previousRegister = ColyseusContext.RegisterWebSocketForDispatch; _previousUnregister = ColyseusContext.UnregisterWebSocketForDispatch; ColyseusContext.RegisterWebSocketForDispatch = null; ColyseusContext.UnregisterWebSocketForDispatch = null; } [TearDown] public void TearDown() { ColyseusContext.RegisterWebSocketForDispatch = _previousRegister; ColyseusContext.UnregisterWebSocketForDispatch = _previousUnregister; } [Test] public void UsesSharedDispatchLoopWithoutContextOrExternalDispatcher() { Assert.IsTrue(WebSocketTransport.ShouldUseSharedDispatchLoop(null)); } [Test] public void SkipsSharedDispatchLoopWhenSynchronizationContextExists() { Assert.IsFalse(WebSocketTransport.ShouldUseSharedDispatchLoop(new SynchronizationContext())); } [Test] public void SkipsSharedDispatchLoopWhenExternalDispatcherExists() { ColyseusContext.RegisterWebSocketForDispatch = _ => { }; Assert.IsFalse(WebSocketTransport.ShouldUseSharedDispatchLoop(null)); } } } #endif ================================================ FILE: nuget-monogame/Colyseus.MonoGame.csproj ================================================ net10.0 9.0 Colyseus.MonoGame Colyseus.MonoGame false true Colyseus.MonoGame Endel Dreyer MonoGame integration for Colyseus Multiplayer SDK — provides a GameComponent that dispatches WebSocket events on the game loop. MIT https://colyseus.io/ https://github.com/colyseus/colyseus-unity-sdk colyseus;multiplayer;networking;monogame;gamedev README.md ================================================ FILE: nuget-monogame/ColyseusGameComponent.cs ================================================ using System.Collections.Generic; using Microsoft.Xna.Framework; namespace Colyseus.MonoGame { public class ColyseusGameComponent : GameComponent { private readonly List _sockets = new List(); public ColyseusGameComponent(Game game) : base(game) { ColyseusContext.RegisterWebSocketForDispatch = (ws) => { lock (_sockets) { _sockets.Add(ws); } }; ColyseusContext.UnregisterWebSocketForDispatch = (ws) => { lock (_sockets) { _sockets.Remove(ws); } }; } public override void Update(GameTime gameTime) { lock (_sockets) { for (var i = 0; i < _sockets.Count; i++) { _sockets[i].DispatchMessageQueue(); } } } } } ================================================ FILE: nuget-monogame/README.md ================================================

Colyseus Multiplayer SDK for MonoGame

MonoGame integration for [Colyseus](https://colyseus.io/) — provides a `GameComponent` that automatically dispatches WebSocket events on the game loop. ## Installation ```sh dotnet add package Colyseus.MonoGame ``` This installs the core `Colyseus` SDK as a dependency. ## Setup Register the `ColyseusGameComponent` in your game's `Initialize()` method: ```csharp using Colyseus.MonoGame; protected override void Initialize() { Components.Add(new ColyseusGameComponent(this)); base.Initialize(); } ``` This single line handles all WebSocket event dispatching on the game thread — no manual polling needed. ## Quick Example ```csharp using Microsoft.Xna.Framework; using Colyseus; using Colyseus.Schema; using Colyseus.MonoGame; public class Game1 : Game { Client client; Room room; protected override void Initialize() { Components.Add(new ColyseusGameComponent(this)); base.Initialize(); } protected override async void LoadContent() { client = new Client("ws://localhost:2567"); room = await client.JoinOrCreate("my_room"); var callbacks = Callbacks.Get(room); callbacks.OnAdd(state => state.players, (sessionId, player) => { System.Diagnostics.Debug.WriteLine($"Player joined: {sessionId}"); }); room.Send("move", new { x = 10f, y = 20f }); room.OnMessage("chat", (message) => { System.Diagnostics.Debug.WriteLine("Chat: " + message); }); } protected override void OnExiting(object sender, EventArgs args) { if (room != null) room.Leave(); base.OnExiting(sender, args); } } ``` ## Documentation See the full documentation at **https://docs.colyseus.io/getting-started/monogame** ## License MIT ================================================ FILE: unity-setup.sh ================================================ #!/bin/bash set -e NATIVEWEBSOCKET_BRANCH="upm-2" NATIVEWEBSOCKET_REPO="https://github.com/endel/NativeWebSocket.git" NATIVEWEBSOCKET_DIR="Assets/Colyseus/Runtime/WebSocket" TMPDIR=$(mktemp -d) trap "rm -rf $TMPDIR" EXIT git clone --branch "$NATIVEWEBSOCKET_BRANCH" --depth 1 "$NATIVEWEBSOCKET_REPO" "$TMPDIR" rm -rf "$NATIVEWEBSOCKET_DIR" cp -r "$TMPDIR/WebSocket" "$NATIVEWEBSOCKET_DIR"