├── .editorconfig ├── .editorconfig - Copy ├── .gitattributes ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── Epp Net.csproj ├── Epp Net.sln ├── EppNet-SourceGen ├── .editorconfig ├── .roslynatorconfig ├── AnalyzerReleases.Shipped.md ├── AnalyzerReleases.Unshipped.md ├── EppNet-SourceGen.csproj └── Source │ ├── Analysis │ ├── AnalysisDiagnostic.cs │ ├── DiagnosticList.cs │ └── NetworkObjectAnalysis.cs │ ├── ExecutionContext.cs │ ├── GenerateObjectSource.cs │ ├── Globals.cs │ ├── Models │ ├── EquatableDictionary.cs │ ├── EquatableHashSet.cs │ ├── EquatableList.cs │ ├── NetworkMethodModel.cs │ ├── NetworkObjectModel.cs │ ├── NetworkParameterTypeModel.cs │ └── ResolverModel.cs │ └── RegisterObjectsGenerator.cs ├── EppNet-Unity ├── EppNet-Unity - Backup.csproj ├── EppNet-Unity.csproj ├── EppNetUnity.cs ├── Source │ └── Data │ │ └── Resolvers │ │ ├── UnityQuaternionResolver.cs │ │ ├── UnityVector2IntResolver.cs │ │ ├── UnityVector2Resolver.cs │ │ ├── UnityVector3IntResolver.cs │ │ ├── UnityVector3Resolver.cs │ │ ├── UnityVector4Resolver.cs │ │ └── UnityVectorResolverBase.cs └── UnityEngine.dll ├── README.md ├── Source ├── Attributes │ ├── NetworkMemberAttribute.cs │ ├── NetworkMethodAttribute.cs │ ├── NetworkObjectAttribute.cs │ ├── NetworkPropertyAttribute.cs │ └── NetworkTypeResolverAttribute.cs ├── Collections │ ├── Iterator.cs │ ├── ObjectCommandList.cs │ ├── OrderedDictionary.cs │ └── PageList.cs ├── Commands │ ├── Command.cs │ ├── CommandContext.cs │ ├── EnumCommandResult.cs │ ├── ObjectCommands.cs │ └── SlimCommand.cs ├── Connections │ ├── ClientConnection.cs │ ├── ConnectEvent.cs │ ├── Connection.cs │ ├── ConnectionService.cs │ ├── ConnectionSlot.cs │ ├── DisconnectEvent.cs │ ├── DisconnectReason.cs │ └── ServerConnection.cs ├── Data │ ├── BytePayload.cs │ ├── Datagrams │ │ ├── Datagram.cs │ │ ├── DisconnectDatagram.cs │ │ ├── IDatagram.cs │ │ ├── ObjectUpdateDatagram.cs │ │ └── PingDatagram.cs │ ├── ICloneable.cs │ ├── IDataHolder.cs │ ├── IMessageHandler.cs │ ├── INameable.cs │ ├── ISignatureEquatable.cs │ ├── InternalProperty.cs │ ├── LockQueue.cs │ ├── NetworkArg.cs │ ├── NetworkArgs.cs │ ├── QuaternionAdapter.cs │ ├── Resolver.cs │ ├── Resolvers │ │ ├── BoolResolver.cs │ │ ├── ByteResolver.cs │ │ ├── ColorResolver.cs │ │ ├── FloatResolver.cs │ │ ├── GuidResolver.cs │ │ ├── Int32Resolver.cs │ │ ├── LongResolver.cs │ │ ├── QuaternionResolver.cs │ │ ├── QuaternionResolverBase.cs │ │ ├── SByteResolver.cs │ │ ├── ShortResolver.cs │ │ ├── String16Resolver.cs │ │ ├── String8Resolver.cs │ │ ├── TimeSpanResolver.cs │ │ ├── UInt32Resolver.cs │ │ ├── ULongResolver.cs │ │ ├── UShortResolver.cs │ │ ├── Vector2Resolver.cs │ │ ├── Vector3Resolver.cs │ │ ├── Vector4Resolver.cs │ │ └── VectorResolverBase.cs │ ├── SlottableEnum.cs │ ├── StringTypes.cs │ └── Timestamp.cs ├── Distribution.cs ├── EppNet.cs ├── Events │ └── EventBase.cs ├── Exceptions │ ├── BytePayloadReadException.cs │ ├── ExceptionStrategy.cs │ └── NetworkException.cs ├── Logging │ ├── ILoggable.cs │ ├── LogLevelFlags.cs │ ├── LogService.cs │ ├── RuntimeFileMetadata.cs │ └── TemplatedMessage.cs ├── Messaging │ ├── Channel.cs │ ├── ChannelAlreadyExistsException.cs │ ├── ChannelFlags.cs │ ├── ChannelService.cs │ ├── IMessageHandler.cs │ ├── MalformedMessageReceivedException.cs │ ├── MessageDirector.cs │ └── MessageSubscriber.cs ├── NetworkFlags.cs ├── Node │ ├── INodeDescendant.cs │ ├── NetworkNode.cs │ ├── NetworkNodeBuilder.cs │ └── NetworkNodeManager.cs ├── Objects │ ├── EnumObjectState.cs │ ├── INetworkObject.cs │ ├── IObjectService.cs │ ├── MyTestObject.cs │ ├── NetworkObjectDefinition.cs │ ├── ObjectAgent.cs │ ├── ObjectEvents.cs │ ├── ObjectMemberDefinition.cs │ ├── ObjectMethodDefinition.cs │ ├── ObjectRegistration.cs │ ├── ObjectService.cs │ ├── ObjectSlot.cs │ ├── Update.cs │ └── UpdateQueue.cs ├── Processes │ ├── Events │ │ └── PacketReceivedEvent.cs │ ├── MultithreadedBuffer.cs │ └── PacketDeserializer.cs ├── Registers │ ├── DatagramRegister.cs │ ├── ICompilable.cs │ ├── IRegistration.cs │ ├── ObjectRegister.cs │ ├── Register.cs │ └── Registration.cs ├── Services │ ├── IRunnable.cs │ ├── IService.cs │ ├── Service.cs │ ├── ServiceManager.cs │ └── ServiceStateChangedEvent.cs ├── Settings │ ├── Configuration.cs │ ├── ConfigurationGroup.cs │ ├── IConfigurationDescendant.cs │ ├── ISetting.cs │ ├── PrimitiveSetting.cs │ ├── SettingValueChangedEvent.cs │ └── Writeable.cs ├── Snapshots │ ├── DesyncEvent.cs │ ├── ObjectSnapshot.cs │ ├── SequenceNumber.cs │ ├── Snapshot.cs │ ├── SnapshotBase.cs │ ├── SnapshotService.cs │ └── SnapshotServiceBase.cs ├── Sockets │ ├── BaseSocket.cs │ ├── ClientSocket.cs │ └── ServerSocket.cs ├── Tests │ ├── BytePayloadBenchmarks.cs │ ├── BytePayloadTests.cs │ ├── GlobalUsings.cs │ ├── PageListTests.cs │ ├── RegisterTests.cs │ ├── ResolverTests.cs │ ├── SocketTest.cs │ └── Tests.csproj ├── Time │ ├── Clock.cs │ ├── ITimestamped.cs │ └── TimeExtensions.cs ├── Utilities │ ├── ActionExtensions.cs │ ├── AttributeFetcher.cs │ ├── BitOperations.cs │ ├── ByteExtensions.cs │ ├── FastMath.cs │ ├── FlagEnumExtensions.cs │ ├── Guard.cs │ ├── NumberExtensions.cs │ ├── SortedListExtensions.cs │ └── StringUtilities.cs └── Zones │ └── IZone.cs └── packages ├── MSTest.TestAdapter.2.2.10 ├── .signature.p7s ├── Icon.png └── MSTest.TestAdapter.2.2.10.nupkg ├── MSTest.TestFramework.2.2.10 ├── .signature.p7s ├── Icon.png ├── MSTest.TestFramework.2.2.10.nupkg └── lib │ ├── net45 │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.XML │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.XML │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.dll │ ├── cs │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── de │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── es │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── fr │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── it │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ja │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ko │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pl │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pt-BR │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ru │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── tr │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── zh-Hans │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ └── zh-Hant │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── net5.0 │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.XML │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.dll │ ├── cs │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── de │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── es │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── fr │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── it │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ja │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ko │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pl │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pt-BR │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ru │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── tr │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── zh-Hans │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ └── zh-Hant │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── netstandard1.0 │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.XML │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.XML │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.dll │ ├── cs │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── de │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── es │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── fr │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── it │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ja │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ko │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pl │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pt-BR │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ru │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── tr │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── zh-Hans │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ └── zh-Hant │ │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ └── uap10.0 │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.XML │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.XML │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.dll │ ├── cs │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── de │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── es │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── fr │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── it │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ja │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ko │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pl │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── pt-BR │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── ru │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── tr │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ ├── zh-Hans │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml │ └── zh-Hant │ ├── Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.xml │ └── Microsoft.VisualStudio.TestPlatform.TestFramework.xml ├── Microsoft.CodeCoverage.17.10.0 ├── .signature.p7s ├── Icon.png ├── Microsoft.CodeCoverage.17.10.0.nupkg ├── ThirdPartyNotices.txt └── lib │ ├── net462 │ └── Microsoft.VisualStudio.CodeCoverage.Shim.dll │ └── netcoreapp3.1 │ └── Microsoft.VisualStudio.CodeCoverage.Shim.dll └── Microsoft.NET.Test.Sdk.17.10.0 ├── .signature.p7s ├── Icon.png ├── Microsoft.NET.Test.Sdk.17.10.0.nupkg ├── buildMultiTargeting └── Microsoft.NET.Test.Sdk.props └── lib ├── net462 └── _._ └── netcoreapp3.1 └── _._ /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | paths-ignore: 10 | - 'EppNet-Unity/**' 11 | pull_request: 12 | branches: [ "master" ] 13 | paths-ignore: 14 | - 'EppNet-Unity/**' 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v3 25 | with: 26 | dotnet-version: 6.0.x 27 | - name: Restore dependencies 28 | run: dotnet restore 29 | - name: Build 30 | run: dotnet build --no-restore 31 | - name: Test 32 | run: dotnet test --no-build --verbosity normal 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | bin/ 3 | obj/ 4 | BenchmarkDotNet.Artifacts/ 5 | build/ -------------------------------------------------------------------------------- /Epp Net.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | EppNet 4 | 0.1.0-alpha 5 | Maverick Liberty 6 | A high-performance networking library powered by ENet. 7 | Networking, UDP, ENet, Multiplayer, GameDev 8 | MIT 9 | https://github.com/EppNet-Networking/EppNet 10 | https://github.com/EppNet-Networking/EppNet 11 | git 12 | netstandard2.1 13 | true 14 | EppNet 15 | 9.0 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | true 50 | $(BaseIntermediateOutputPath)Generated 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Epp Net.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34024.191 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Epp Net", "Epp Net.csproj", "{1B9B3170-A225-4890-A7E2-E4F331DBC673}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FC780FD9-C774-4C76-AEAD-A8742F8DAAC9}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | EndProjectSection 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Source\Tests\Tests.csproj", "{596BBBF5-0810-4692-8A0C-30C6A847294A}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EppNet-Unity", "EppNet-Unity\EppNet-Unity.csproj", "{63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EppNet-SourceGen", "EppNet-SourceGen\EppNet-SourceGen.csproj", "{BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}" 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | ExportDebug|Any CPU = ExportDebug|Any CPU 23 | ExportRelease|Any CPU = ExportRelease|Any CPU 24 | Release|Any CPU = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.Debug|Any CPU.ActiveCfg = Release|Any CPU 28 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.Debug|Any CPU.Build.0 = Release|Any CPU 29 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU 30 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU 31 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.ExportRelease|Any CPU.ActiveCfg = Release|Any CPU 32 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.ExportRelease|Any CPU.Build.0 = Release|Any CPU 33 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {1B9B3170-A225-4890-A7E2-E4F331DBC673}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.ExportDebug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.ExportDebug|Any CPU.Build.0 = Debug|Any CPU 39 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.ExportRelease|Any CPU.ActiveCfg = Release|Any CPU 40 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.ExportRelease|Any CPU.Build.0 = Release|Any CPU 41 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {596BBBF5-0810-4692-8A0C-30C6A847294A}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.ExportDebug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.ExportDebug|Any CPU.Build.0 = Debug|Any CPU 47 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.ExportRelease|Any CPU.ActiveCfg = Release|Any CPU 48 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.ExportRelease|Any CPU.Build.0 = Release|Any CPU 49 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {63EDD7D0-1FB9-477F-B84E-31BED44C3C8A}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.ExportDebug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.ExportDebug|Any CPU.Build.0 = Debug|Any CPU 55 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.ExportRelease|Any CPU.ActiveCfg = Release|Any CPU 56 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.ExportRelease|Any CPU.Build.0 = Release|Any CPU 57 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {BAF4D6EB-847B-479E-BC00-1344DCD1ED6F}.Release|Any CPU.Build.0 = Release|Any CPU 59 | EndGlobalSection 60 | GlobalSection(SolutionProperties) = preSolution 61 | HideSolutionNode = FALSE 62 | EndGlobalSection 63 | GlobalSection(ExtensibilityGlobals) = postSolution 64 | SolutionGuid = {031E31B1-95F4-498E-8819-32E845731277} 65 | EndGlobalSection 66 | EndGlobal 67 | -------------------------------------------------------------------------------- /EppNet-SourceGen/.roslynatorconfig: -------------------------------------------------------------------------------- 1 | roslynator_blank_line_between_closing_brace_and_switch_section = false -------------------------------------------------------------------------------- /EppNet-SourceGen/AnalyzerReleases.Shipped.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/EppNet-SourceGen/AnalyzerReleases.Shipped.md -------------------------------------------------------------------------------- /EppNet-SourceGen/AnalyzerReleases.Unshipped.md: -------------------------------------------------------------------------------- 1 | ### New Rules 2 | 3 | Rule ID | Category | Severity | Notes 4 | --------|----------|----------|-------------------- 5 | EPN001 | SourceGenerator | Warning | Debug message 6 | EPN002 | SourceGenerator | Error | Unspecified analysis error 7 | EPN003 | SourceGenerator | Error | Network Type Resolver related error 8 | EPN004 | SourceGenerator | Error | Network Object related error 9 | EPN005 | SourceGenerator | Error | Network Method related error -------------------------------------------------------------------------------- /EppNet-SourceGen/EppNet-SourceGen.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | EppNet.SourceGen 6 | Latest 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Analysis/AnalysisDiagnostic.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: AnalysisErrorBase.cs 3 | /// Date: March 11, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp; 9 | using Microsoft.CodeAnalysis.CSharp.Syntax; 10 | 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | 14 | namespace EppNet.SourceGen.Analysis 15 | { 16 | 17 | public class AnalysisDiagnostic 18 | { 19 | 20 | /// 21 | /// The descriptor to use for the message. 22 | /// 23 | public readonly DiagnosticDescriptor DiagnosticDescriptor; 24 | 25 | /// 26 | /// The context of what caused the error 27 | /// 28 | public readonly CSharpSyntaxNode Context; 29 | 30 | /// 31 | /// The message to send 32 | /// 33 | public readonly string Message; 34 | 35 | /// 36 | /// Where this error is occurring 37 | /// 38 | public readonly Location[] Locations; 39 | 40 | public AnalysisDiagnostic(DiagnosticDescriptor descriptor, CSharpSyntaxNode context, string message, params Location[] locations) 41 | { 42 | this.DiagnosticDescriptor = descriptor; 43 | this.Context = context; 44 | this.Message = message; 45 | this.Locations = locations; 46 | } 47 | 48 | public AnalysisDiagnostic(DiagnosticDescriptor descriptor, CSharpSyntaxNode context, string message, IEnumerable locations) 49 | { 50 | this.DiagnosticDescriptor = descriptor; 51 | this.Context = context; 52 | this.Message = message; 53 | this.Locations = locations.ToArray(); 54 | } 55 | 56 | /// 57 | /// Creates the diagnostic for the analyzer 58 | /// 59 | /// 60 | public Diagnostic CreateDiagnostic() 61 | { 62 | Location mainLocation = Location.None; 63 | Location[] extLocations = Locations; 64 | string message = Message; 65 | 66 | if (Context != null) 67 | { 68 | if (Context is BaseTypeDeclarationSyntax btds) 69 | message = $"{btds.Identifier.Text}: {Message}"; 70 | 71 | mainLocation = Context.GetLocation(); 72 | } 73 | 74 | if (mainLocation == Location.None && Locations.Length > 0) 75 | mainLocation = Locations[0]; 76 | 77 | if (extLocations == null || extLocations.Length == 0) 78 | return Diagnostic.Create(DiagnosticDescriptor, mainLocation, message); 79 | 80 | return Diagnostic.Create(DiagnosticDescriptor, mainLocation, extLocations, message); 81 | } 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Analysis/DiagnosticList.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: DiagnosticList.cs 3 | /// Date: March 13, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace EppNet.SourceGen.Analysis 11 | { 12 | 13 | /// 14 | /// Wrapper around a lazily created list of 15 | /// 16 | public class DiagnosticList 17 | { 18 | public bool AnalysisMode { get; } 19 | 20 | public bool IsValueCreated { get => _diagnostics.IsValueCreated; } 21 | 22 | private readonly Lazy> _diagnostics; 23 | 24 | public DiagnosticList(ExecutionContext context) 25 | { 26 | this.AnalysisMode = context.IsAnalysis; 27 | this._diagnostics = new(() => []); 28 | } 29 | 30 | public DiagnosticList(bool analysisMode) 31 | { 32 | this.AnalysisMode = analysisMode; 33 | this._diagnostics = new(() => []); 34 | } 35 | 36 | public bool Add(AnalysisDiagnostic diagnostic) 37 | { 38 | if (!AnalysisMode) 39 | return false; 40 | 41 | _diagnostics.Value.Add(diagnostic); 42 | return true; 43 | } 44 | 45 | public bool AddRange(IEnumerable diagnostics) 46 | { 47 | if (!AnalysisMode) 48 | return false; 49 | 50 | _diagnostics.Value.AddRange(diagnostics); 51 | return true; 52 | } 53 | 54 | public List AddAndGet(AnalysisDiagnostic diagnostic) 55 | { 56 | Add(diagnostic); 57 | return Get(); 58 | } 59 | 60 | public List Get() => 61 | _diagnostics.IsValueCreated ? 62 | _diagnostics.Value : 63 | Globals.EmptyDiagnostics; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/ExecutionContext.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: ExecutionContext.cs 3 | /// Date: March 12, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | 9 | using System.Collections.Concurrent; 10 | 11 | namespace EppNet.SourceGen 12 | { 13 | public class ExecutionContext 14 | { 15 | 16 | /// 17 | /// Whether or not this is running in the analysis mode 18 | /// 19 | public bool IsAnalysis; 20 | 21 | public ConcurrentDictionary Resolvers; 22 | 23 | private ConcurrentDictionary _stringPool; 24 | 25 | // Can store TypeSyntax or strings 26 | private ConcurrentDictionary _typesCache; 27 | private ConcurrentDictionary _typeNames; 28 | 29 | public ExecutionContext(bool isAnalysis) 30 | { 31 | this.IsAnalysis = isAnalysis; 32 | this.Resolvers = isAnalysis ? new ConcurrentDictionary() : null; 33 | this._stringPool = new(); 34 | this._typesCache = new(); 35 | this._typeNames = new(SymbolEqualityComparer.Default); 36 | } 37 | 38 | public bool CacheType(TypeSyntax typeSyntax, string typeName, bool isValid = true, bool isNetObject = false) => 39 | _typesCache.TryAdd(typeSyntax, (isValid, isNetObject)) && 40 | _typesCache.TryAdd(typeName, (isValid, isNetObject)); 41 | 42 | public (bool, bool) CacheTypeAndReturn(TypeSyntax typeSyntax, string typeName, bool isValid = true, bool isNetObject = false) 43 | { 44 | _typesCache.TryAdd(typeSyntax, (isValid, isNetObject)); 45 | _typesCache.TryAdd(typeName, (isValid, isNetObject)); 46 | return (isValid, isNetObject); 47 | } 48 | 49 | public (bool, (bool, bool)) GetType(string typeName) 50 | { 51 | bool found = _typesCache.TryGetValue(typeName, out (bool, bool) results); 52 | return (found, results); 53 | } 54 | 55 | public (bool, (bool, bool)) GetType(TypeSyntax typeSyntax) 56 | { 57 | bool found = _typesCache.TryGetValue(typeSyntax, out (bool, bool) results); 58 | return (found, results); 59 | } 60 | 61 | public string GetTypeName(ITypeSymbol typeSymbol) 62 | { 63 | if (typeSymbol == null) 64 | return string.Empty; 65 | 66 | if (_typeNames.TryGetValue(typeSymbol, out var typeName)) 67 | return typeName; 68 | 69 | typeName = typeSymbol.ToDisplayString(Globals.DisplayFormat); 70 | _typeNames.TryAdd(typeSymbol, typeName); 71 | 72 | return GetString(typeName); 73 | } 74 | 75 | public string GetString(string input) => 76 | _stringPool.GetOrAdd(input, input); 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/GenerateObjectSource.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: GenerateObjectSource.cs 3 | /// Date: March 14, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | using EppNet.SourceGen.Models; 7 | 8 | using System.Collections.Generic; 9 | 10 | namespace EppNet.SourceGen.Source 11 | { 12 | 13 | public class GenerateObjectSource 14 | { 15 | 16 | public NetworkObjectModel Model { get; } 17 | 18 | public string FullyQualifiedName { get; } 19 | 20 | public HashSet Imports; 21 | 22 | public GenerateObjectSource(NetworkObjectModel model, string fullyQualifiedName) 23 | { 24 | this.Model = model; 25 | this.FullyQualifiedName = fullyQualifiedName; 26 | } 27 | 28 | 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Models/EquatableDictionary.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: EquatableDictionary.cs 3 | /// Date: March 11, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | 11 | namespace EppNet.SourceGen.Models 12 | { 13 | 14 | public class EquatableDictionary : Dictionary, 15 | IEquatable> 16 | { 17 | public EquatableDictionary() : base() { } 18 | 19 | public EquatableDictionary(Dictionary source) : base(source) { } 20 | 21 | public bool Equals(EquatableDictionary other) 22 | { 23 | if (other is null || Count != other.Count) 24 | return false; 25 | 26 | foreach (KeyValuePair kvp in this) 27 | { 28 | if (!other.TryGetValue(kvp.Key, out TValue otherValue)) 29 | return false; 30 | 31 | if (!EqualityComparer.Default.Equals(kvp.Value, otherValue)) 32 | return false; 33 | } 34 | 35 | // Lengths and key-value pairs are identical 36 | return true; 37 | } 38 | 39 | public override bool Equals(object obj) => 40 | Equals(obj as EquatableDictionary); 41 | 42 | public override int GetHashCode() => 43 | this.Count == 0 ? 0 : 44 | this.Select(item => item.GetHashCode()) 45 | .Aggregate((x, y) => x ^ y); 46 | 47 | public static bool operator ==(EquatableDictionary a, EquatableDictionary b) => 48 | ReferenceEquals(a, b) || a is not null && b is not null && a.Equals(b); 49 | 50 | public static bool operator !=(EquatableDictionary a, EquatableDictionary b) => 51 | !(a == b); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Models/EquatableHashSet.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: EquatableSet.cs 3 | /// Date: March 12, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | 11 | namespace EppNet.SourceGen.Models 12 | { 13 | 14 | public class EquatableHashSet : HashSet, IEquatable> 15 | { 16 | 17 | public EquatableHashSet() : base() { } 18 | 19 | public EquatableHashSet(HashSet source) : base(source) { } 20 | 21 | public bool Equals(EquatableHashSet other) 22 | { 23 | if (other is null || Count != other.Count) 24 | return false; 25 | 26 | return SetEquals(other); 27 | } 28 | 29 | public override bool Equals(object obj) => 30 | obj is not null && 31 | obj is EquatableHashSet other && 32 | Equals(other); 33 | 34 | public override int GetHashCode() => 35 | this.Count == 0 ? 0 : 36 | this.Select(item => item?.GetHashCode() ?? 0) 37 | .Aggregate((x, y) => x ^ y); 38 | 39 | public static bool operator ==(EquatableHashSet set1, EquatableHashSet set2) => 40 | ReferenceEquals(set1, set2) || 41 | set1 is not null && 42 | set2 is not null && 43 | set1.Equals(set2); 44 | 45 | public static bool operator !=(EquatableHashSet set1, EquatableHashSet set2) => 46 | !(set1 == set2); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Models/EquatableList.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: EquatableList.cs 3 | /// Date: ????? 4 | /// Author: Microsoft 5 | /// Source: https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md#use-forattributewithmetadataname 6 | ////////////////////////////////////////////// 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | 12 | namespace EppNet.SourceGen.Models 13 | { 14 | public class EquatableList : List, IEquatable> 15 | { 16 | 17 | public EquatableList() : base() { } 18 | 19 | public EquatableList(List source) : base(source) { } 20 | 21 | public bool Equals(EquatableList other) 22 | { 23 | // If the other list is null or a different size, they're not equal 24 | if (other is null || Count != other.Count) 25 | return false; 26 | 27 | // Compare each pair of elements for equality 28 | for (int i = 0; i < Count; i++) 29 | { 30 | if (!EqualityComparer.Default.Equals(this[i], other[i])) 31 | return false; 32 | } 33 | 34 | // If we got this far, the lists are equal 35 | return true; 36 | } 37 | 38 | public override bool Equals(object obj) => 39 | obj is not null && 40 | obj is EquatableList other && 41 | Equals(other); 42 | 43 | public override int GetHashCode() => 44 | this.Count == 0 ? 0 : 45 | this.Select(item => item?.GetHashCode() ?? 0) 46 | .Aggregate((x, y) => x ^ y); 47 | 48 | public static bool operator ==(EquatableList list1, EquatableList list2) => 49 | ReferenceEquals(list1, list2) || 50 | list1 is not null && 51 | list2 is not null && 52 | list1.Equals(list2); 53 | 54 | public static bool operator !=(EquatableList list1, EquatableList list2) => 55 | !(list1 == list2); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Models/NetworkMethodModel.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: NetworkMethodModel.cs 3 | /// Date: February 17, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using Microsoft.CodeAnalysis; 8 | 9 | using System; 10 | using System.Text; 11 | 12 | namespace EppNet.SourceGen.Models 13 | { 14 | public readonly struct NetworkMethodModel(ISymbol symbol, EquatableList parameters) : IEquatable 15 | { 16 | 17 | public string Name { get; } = symbol.Name; 18 | 19 | public EquatableList Parameters { get; } = parameters; 20 | 21 | public override bool Equals(object obj) 22 | => obj is NetworkMethodModel model && 23 | Equals(model); 24 | 25 | public bool Equals(NetworkMethodModel other) => 26 | Name == other.Name && 27 | Parameters == other.Parameters; 28 | 29 | public override string ToString() 30 | { 31 | int paramCount = Parameters?.Count ?? 0; 32 | StringBuilder builder = new($"Parameters: {paramCount} {Name}("); 33 | 34 | for (int i = 0; i < Parameters.Count; i++) 35 | { 36 | NetworkParameterTypeModel type = Parameters[i]; 37 | int subtypeCount = type.Subtypes?.Count ?? 0; 38 | builder.Append($"st:{subtypeCount} "); 39 | builder.Append(type); 40 | 41 | if (i + 1 < Parameters.Count) 42 | builder.Append(", "); 43 | } 44 | 45 | builder.Append(")"); 46 | return builder.ToString(); 47 | } 48 | 49 | public override int GetHashCode() => 50 | Name.GetHashCode() ^ 51 | Parameters.GetHashCode(); 52 | 53 | public static bool operator ==(NetworkMethodModel left, NetworkMethodModel right) => 54 | left.Equals(right); 55 | 56 | public static bool operator !=(NetworkMethodModel left, NetworkMethodModel right) => 57 | !left.Equals(right); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Models/NetworkObjectModel.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: NetworkObjectModel.cs 3 | /// Date: February 8, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using Microsoft.CodeAnalysis; 8 | 9 | using System; 10 | 11 | namespace EppNet.SourceGen.Models 12 | { 13 | 14 | /// 15 | /// Stores information about a particular network type resolver 16 | /// 17 | public readonly struct NetworkObjectModel(INamedTypeSymbol symbol, 18 | int distType, 19 | EquatableList baseNetObjectsFQNs, 20 | EquatableDictionary> methodDict) : IEquatable 21 | { 22 | public string Name { get; } = symbol.Name; 23 | 24 | public string Namespace { get; } = symbol.ContainingNamespace.Name; 25 | 26 | public string FullNamespace 27 | { 28 | get 29 | { 30 | string fullName = symbol.ToDisplayString(); 31 | return fullName.Substring(0, fullName.Length - Name.Length - 1); 32 | } 33 | } 34 | 35 | public string FullyQualifiedName { get; } = $"{symbol.ContainingNamespace.ToDisplayString()}.{symbol.Name}"; 36 | 37 | /// 38 | /// The hierarchy of network objects where the first element is the first network object in the tree 39 | /// that doesn't inherit from another network object. 40 | /// 41 | public EquatableList NetObjectHierarchy { get; } = baseNetObjectsFQNs; 42 | 43 | public int Distribution { get; } = distType; 44 | 45 | public EquatableDictionary> Methods { get; } = methodDict; 46 | 47 | public override bool Equals(object obj) => 48 | obj is NetworkObjectModel model && 49 | Equals(model); 50 | 51 | public bool Equals(NetworkObjectModel other) => 52 | Name == other.Name && 53 | Namespace == other.Namespace && 54 | FullNamespace == other.FullNamespace && 55 | FullyQualifiedName == other.FullyQualifiedName && 56 | NetObjectHierarchy == other.NetObjectHierarchy && 57 | Methods == other.Methods; 58 | 59 | public override string ToString() 60 | { 61 | if (NetObjectHierarchy.Count > 0) 62 | return $"{FullNamespace}({NetObjectHierarchy[0]}) Methods: {Methods.Count}"; 63 | 64 | return $"{FullNamespace} Methods: {Methods.Count}"; 65 | } 66 | 67 | public override int GetHashCode() => 68 | Name.GetHashCode() ^ 69 | Namespace.GetHashCode() ^ 70 | FullyQualifiedName.GetHashCode() ^ 71 | (NetObjectHierarchy != null ? NetObjectHierarchy.GetHashCode() : 1) ^ 72 | Methods.GetHashCode(); 73 | 74 | public static bool operator ==(NetworkObjectModel left, NetworkObjectModel right) => left.Equals(right); 75 | public static bool operator !=(NetworkObjectModel left, NetworkObjectModel right) => !left.Equals(right); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Models/NetworkParameterTypeModel.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: NetworkParameterTypeModel.cs 3 | /// Date: February 18, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using Microsoft.CodeAnalysis; 8 | 9 | using System; 10 | using System.Text; 11 | 12 | namespace EppNet.SourceGen.Models 13 | { 14 | 15 | public readonly struct NetworkParameterTypeModel(ISymbol symbol, 16 | EquatableList subtypes, 17 | string underlyingType = null, 18 | bool isNetObject = false, 19 | bool isTuple = false) 20 | : IEquatable 21 | { 22 | public string Name { get; } = symbol.Name; 23 | public string Namespace { get; } = symbol.ContainingNamespace?.Name ?? string.Empty; 24 | public string FullyQualifiedName { get; } = symbol.ToDisplayString(Globals.DisplayFormat); 25 | public string UnderlyingTypeFullyQualifiedName { get; } = underlyingType; 26 | public EquatableList Subtypes { get; } = subtypes; 27 | public bool IsNetObject { get; } = isNetObject; 28 | public bool IsTuple { get; } = isTuple; 29 | 30 | public override bool Equals(object obj) => 31 | obj is NetworkParameterTypeModel model && 32 | Equals(model); 33 | 34 | public bool Equals(NetworkParameterTypeModel other) => 35 | Name == other.Name && 36 | Namespace == other.Namespace && 37 | FullyQualifiedName == other.FullyQualifiedName && 38 | Subtypes == other.Subtypes && 39 | UnderlyingTypeFullyQualifiedName == other.UnderlyingTypeFullyQualifiedName && 40 | IsNetObject == other.IsNetObject ^ 41 | IsTuple == other.IsTuple; 42 | 43 | public override string ToString() 44 | { 45 | if (!isTuple && Subtypes != null && Subtypes.Count > 0) 46 | { 47 | StringBuilder builder = new($"{FullyQualifiedName}<"); 48 | 49 | for (int i = 0; i < Subtypes.Count; i++) 50 | { 51 | builder.Append(Subtypes[i].ToString()); 52 | 53 | if (i + 1 < Subtypes.Count) 54 | builder.Append(", "); 55 | } 56 | 57 | builder.Append(">"); 58 | return builder.ToString(); 59 | } 60 | 61 | return FullyQualifiedName; 62 | } 63 | 64 | public override int GetHashCode() => 65 | Name.GetHashCode() ^ 66 | Namespace.GetHashCode() ^ 67 | FullyQualifiedName.GetHashCode() ^ 68 | ((Subtypes != null) ? Subtypes.GetHashCode() : 1) ^ 69 | ((UnderlyingTypeFullyQualifiedName != null) ? UnderlyingTypeFullyQualifiedName.GetHashCode() : 1) ^ 70 | IsNetObject.GetHashCode() ^ 71 | IsTuple.GetHashCode(); 72 | 73 | public static bool operator ==(NetworkParameterTypeModel left, NetworkParameterTypeModel right) => 74 | left.Equals(right); 75 | 76 | public static bool operator !=(NetworkParameterTypeModel left, NetworkParameterTypeModel right) => 77 | !left.Equals(right); 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /EppNet-SourceGen/Source/Models/ResolverModel.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: ResolverModel.cs 3 | /// Date: February 8, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using Microsoft.CodeAnalysis; 8 | 9 | using System; 10 | 11 | namespace EppNet.SourceGen.Models 12 | { 13 | 14 | /// 15 | /// Stores information about a particular network type resolver 16 | /// 17 | public readonly struct ResolverModel(ISymbol symbol, ITypeSymbol typeSymbol) : IEquatable 18 | { 19 | public string Name { get; } = symbol.Name; 20 | 21 | /// 22 | /// Most resolvers will derive from 23 | /// 24 | public string Namespace { get; } = symbol.ContainingNamespace?.Name ?? string.Empty; 25 | 26 | public string FullyQualifiedName { get; } = symbol.ToDisplayString(Globals.DisplayFormat); 27 | 28 | /// 29 | /// The fully qualified name of the type this resolver resolves. 30 | /// 31 | public string ResolvedTypeFullName { get; } = typeSymbol.ToDisplayString(Globals.DisplayFormat); 32 | 33 | public override bool Equals(object obj) => 34 | obj is ResolverModel model && 35 | Equals(model); 36 | 37 | public bool Equals(ResolverModel other) => 38 | Name == other.Name && 39 | Namespace == other.Namespace && 40 | FullyQualifiedName == other.FullyQualifiedName && 41 | ResolvedTypeFullName == other.ResolvedTypeFullName; 42 | 43 | public override string ToString() => 44 | $"{Name}<{ResolvedTypeFullName}>"; 45 | 46 | public override int GetHashCode() => 47 | Name.GetHashCode() ^ 48 | Namespace.GetHashCode() ^ 49 | FullyQualifiedName.GetHashCode() ^ 50 | ResolvedTypeFullName.GetHashCode(); 51 | 52 | public static bool operator ==(ResolverModel left, ResolverModel right) => 53 | left.Equals(right); 54 | 55 | public static bool operator !=(ResolverModel left, ResolverModel right) => 56 | !left.Equals(right); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /EppNet-Unity/EppNet-Unity - Backup.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | EppNet.Unity 6 | enable 7 | 8 | 9 | 10 | $(DefineConstants);EPPNET_UNITY 11 | 12 | 13 | 14 | $(DefineConstants);EPPNET_UNITY 15 | 16 | 17 | 18 | 19 | UnityEngine.dll 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /EppNet-Unity/EppNet-Unity.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | EppNet.Unity 6 | enable 7 | 8 | 9 | 10 | $(DefineConstants);EPPNET_UNITY 11 | 12 | 13 | 14 | $(DefineConstants);EPPNET_UNITY 15 | 16 | 17 | 18 | 19 | UnityEngine.dll 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /EppNet-Unity/EppNetUnity.cs: -------------------------------------------------------------------------------- 1 | using EppNet.Data; 2 | using EppNet.Unity.Data; 3 | 4 | namespace EppNet 5 | { 6 | 7 | public static class EppNetUnity 8 | { 9 | 10 | static EppNetUnity() 11 | { 12 | BytePayload.AddResolver(typeof(UnityEngine.Vector4), UnityVector4Resolver.Instance); 13 | BytePayload.AddResolver(typeof(UnityEngine.Vector3), UnityVector3Resolver.Instance); 14 | BytePayload.AddResolver(typeof(UnityEngine.Vector3Int), UnityVector3IntResolver.Instance); 15 | BytePayload.AddResolver(typeof(UnityEngine.Vector2), UnityVector2Resolver.Instance); 16 | BytePayload.AddResolver(typeof(UnityEngine.Vector2Int), UnityVector2IntResolver.Instance); 17 | BytePayload.AddResolver(typeof(UnityEngine.Quaternion), UnityQuaternionResolver.Instance); 18 | } 19 | 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /EppNet-Unity/Source/Data/Resolvers/UnityVectorResolverBase.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: UnityVectorResolverBase.cs 3 | /// Date: March 29, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System; 10 | using System.Runtime.CompilerServices; 11 | 12 | namespace EppNet.Unity.Data 13 | { 14 | 15 | public abstract class UnityVectorResolverBase : VectorResolverBase where T : struct, IEquatable 16 | { 17 | protected UnityVectorResolverBase(bool autoAdvance = true) : base(autoAdvance) { } 18 | 19 | protected UnityVectorResolverBase(int size, bool autoAdvance = true) : base(size, autoAdvance) { } 20 | 21 | protected UnityVectorResolverBase(int size) : base(size) { } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | protected override void ExtractFloatComponents(T input, Span values) 25 | { 26 | switch (input) 27 | { 28 | 29 | case UnityEngine.Vector4 uv4: 30 | values[0] = uv4.x; 31 | values[1] = uv4.y; 32 | values[2] = uv4.z; 33 | values[3] = uv4.w; 34 | break; 35 | 36 | case UnityEngine.Vector3 uv3: 37 | values[0] = uv3.x; 38 | values[1] = uv3.y; 39 | values[2] = uv3.z; 40 | break; 41 | 42 | case UnityEngine.Vector3Int uv3i: 43 | values[0] = uv3i.x; 44 | values[1] = uv3i.y; 45 | values[2] = uv3i.z; 46 | break; 47 | 48 | case UnityEngine.Vector2 uv2: 49 | values[0] = uv2.x; 50 | values[1] = uv2.y; 51 | break; 52 | 53 | case UnityEngine.Vector2Int uv2i: 54 | values[0] = uv2i.x; 55 | values[1] = uv2i.y; 56 | break; 57 | 58 | default: 59 | base.ExtractFloatComponents(input, values); 60 | break; 61 | 62 | } 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /EppNet-Unity/UnityEngine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/EppNet-Unity/UnityEngine.dll -------------------------------------------------------------------------------- /Source/Attributes/NetworkMemberAttribute.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkMemberAttribute.cs 3 | /// Date: September 23, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System; 10 | 11 | namespace EppNet.Attributes 12 | { 13 | public abstract class NetworkMemberAttribute : Attribute 14 | { 15 | 16 | public SlottableEnum Flags { internal set; get; } 17 | 18 | protected NetworkMemberAttribute() : this(NetworkFlags.None) { } 19 | 20 | protected NetworkMemberAttribute(SlottableEnum flags) 21 | { 22 | this.Flags = flags; 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Source/Attributes/NetworkMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkMethodAttribute.cs 3 | /// Date: September 14, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System; 10 | using System.Reflection; 11 | 12 | namespace EppNet.Attributes 13 | { 14 | 15 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 16 | public class NetworkMethodAttribute : NetworkMemberAttribute 17 | { 18 | 19 | /// 20 | /// requires a getter method to 21 | /// retrieve the current value. 22 | /// 23 | public MethodInfo Getter { internal set; get; } 24 | 25 | public NetworkMethodAttribute() : this(NetworkFlags.None) { } 26 | 27 | public NetworkMethodAttribute(SlottableEnum flags) 28 | { 29 | this.Flags = flags; 30 | this.Getter = null; 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Source/Attributes/NetworkObjectAttribute.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkObjectAttribute.cs 3 | /// Date: September 14, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Attributes 10 | { 11 | 12 | /// 13 | /// Denotes a class as a network object which can communicate updates 14 | /// across the wire. This attribute is NOT inherited and must be added 15 | /// to the exact class you want distributed. 16 | /// 17 | 18 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 19 | public class NetworkObjectAttribute : Attribute 20 | { 21 | 22 | const string Empty = ""; 23 | 24 | /// 25 | /// Specify a creator method for generating a new instance of this object.
26 | /// This should specify the name of an accessible static method that returns the type
27 | /// decorated by this attribute 28 | ///
29 | public string Creator; 30 | 31 | /// 32 | /// Specify a destructor method for ensuring the object is cleaned up properly. 33 | /// 34 | public string Destructor; 35 | 36 | /// 37 | /// Whether or not instances of this object are global. (Not tied to a particular zone) 38 | /// 39 | public bool Global; 40 | 41 | public Distribution Dist; 42 | 43 | public NetworkObjectAttribute(string creatorMethodName = Empty, 44 | string destructorMethodName = Empty, 45 | bool isGlobal = false, 46 | Distribution dist = Distribution.Shared) 47 | { 48 | this.Creator = creatorMethodName; 49 | this.Destructor = destructorMethodName; 50 | this.Global = isGlobal; 51 | this.Dist = dist; 52 | } 53 | 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /Source/Attributes/NetworkPropertyAttribute.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkPropertyAttribute.cs 3 | /// Date: September 23, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Attributes 10 | { 11 | 12 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 13 | public class NetworkPropertyAttribute : NetworkMemberAttribute { } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Source/Attributes/NetworkTypeResolverAttribute.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: NetworkTypeResolverAttribute.cs 3 | /// Date: February 2, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Attributes 10 | { 11 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 12 | public class NetworkTypeResolverAttribute : Attribute { } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Source/Collections/Iterator.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: Iterator.cs 3 | /// Date: September 10, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Utilities; 8 | 9 | using System.Collections.Generic; 10 | using System.Collections.Immutable; 11 | using System.Diagnostics.CodeAnalysis; 12 | 13 | namespace EppNet.Collections 14 | { 15 | 16 | public ref struct Iterator 17 | { 18 | 19 | public int Index { private set; get; } 20 | 21 | private ImmutableArray _elements; 22 | 23 | public Iterator([NotNull] IEnumerable collection) 24 | { 25 | Guard.AgainstNull(collection); 26 | this.Index = -1; 27 | this._elements = collection.ToImmutableArray(); 28 | } 29 | 30 | /// 31 | /// Increments the internal index to the next one. 32 | /// 33 | /// 34 | 35 | public T Next() 36 | => _elements[++Index]; 37 | 38 | /// 39 | /// Checks if we have something next in the iterator 40 | /// 41 | /// 42 | 43 | public bool HasNext() 44 | => (Index + 1) < _elements.Length; 45 | 46 | /// 47 | /// Fetches the current object 48 | /// 49 | /// 50 | 51 | public T Current() 52 | => (Index == -1) 53 | ? default 54 | : _elements[Index]; 55 | } 56 | 57 | public static class IteratorExtensions 58 | { 59 | 60 | public static Iterator Iterator(this IEnumerable enumerable) 61 | => new Iterator(enumerable); 62 | 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /Source/Collections/ObjectCommandList.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: CommandList.cs 3 | /// Date: August 13, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Commands; 8 | using EppNet.Objects; 9 | using EppNet.Snapshots; 10 | using EppNet.Utilities; 11 | 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Diagnostics.CodeAnalysis; 15 | using System.Threading; 16 | 17 | namespace EppNet.Collections 18 | { 19 | 20 | public class ObjectCommandList : IDisposable 21 | { 22 | 23 | public readonly ObjectAgent Object; 24 | public readonly ObjectSnapshot Snapshot; 25 | public ObjectSnapshot RelativeTo { get => _relativeTo; } 26 | 27 | public int ReliableCommands { get => _reliableCmds; } 28 | 29 | public bool Absolute => RelativeTo == null; 30 | 31 | protected List _list; 32 | protected ReaderWriterLockSlim _lock; 33 | 34 | protected int _reliableCmds; 35 | protected ObjectSnapshot _relativeTo; 36 | 37 | public ObjectCommandList([NotNull] ObjectSnapshot snapshot) 38 | { 39 | Guard.AgainstNull(snapshot); 40 | this.Object = snapshot.Object; 41 | this.Snapshot = snapshot; 42 | 43 | this._list = new(); 44 | this._lock = new(); 45 | this._reliableCmds = 0; 46 | this._relativeTo = null; 47 | } 48 | 49 | public void SetRelativeTo([NotNull] ObjectSnapshot other) 50 | { 51 | Guard.AgainstNull(other); 52 | 53 | } 54 | 55 | public void Dispose() 56 | { 57 | _list?.Clear(); 58 | _list = null; 59 | 60 | _lock?.Dispose(); 61 | _lock = null; 62 | 63 | _reliableCmds = 0; 64 | } 65 | 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /Source/Commands/Command.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: Command.cs 3 | /// Date: August 4, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | using EppNet.Utilities; 9 | 10 | using Microsoft.Extensions.ObjectPool; 11 | 12 | using System; 13 | using System.Collections; 14 | using System.Collections.Generic; 15 | 16 | namespace EppNet.Commands 17 | { 18 | 19 | public static class Commands 20 | { 21 | 22 | public const int DefaultCommandPoolSize = 256; 23 | 24 | internal static readonly List _cmdsList = new List(); 25 | internal static readonly Dictionary _cmdPools = new(); 26 | 27 | public static readonly SlottableEnum None = SlottableEnum._Internal_CreateAndAddTo(_cmdsList, "None", 1); 28 | 29 | // Creation and deletion are in the same slot as they can undo each other 30 | public static readonly SlottableEnum Create = _Internal_CreatePoolableAndAddTo(_cmdsList, "Create_Object", 1); 31 | public static readonly SlottableEnum Delete = _Internal_CreatePoolableAndAddTo(_cmdsList, "Delete_Object", 1); 32 | 33 | // Generate, Enable, and Disable are in the same slot as they can undo each other 34 | public static readonly SlottableEnum Object_Generate = SlottableEnum._Internal_CreateAndAddTo(_cmdsList, "Object_Generate", 2); 35 | public static readonly SlottableEnum Object_Enable = SlottableEnum._Internal_CreateAndAddTo(_cmdsList, "Object_Enable", 2); 36 | public static readonly SlottableEnum Object_Disable = SlottableEnum._Internal_CreateAndAddTo(_cmdsList, "Object_Disable", 2); 37 | 38 | public static readonly SlottableEnum Object_SetParent = _Internal_CreatePoolableAndAddTo(_cmdsList, "Object_SetParent", 3); 39 | public static readonly SlottableEnum Object_SetProperty = _Internal_CreatePoolableAndAddTo(_cmdsList, "Object_SetProperty", 4); 40 | public static readonly SlottableEnum Object_CallMethod = _Internal_CreatePoolableAndAddTo(_cmdsList, "Object_CallMethod", 5); 41 | public static readonly SlottableEnum Object_AckSnapshot = SlottableEnum._Internal_CreateAndAddTo(_cmdsList, "Object_AckSnapshot", 6); 42 | 43 | internal static SlottableEnum _Internal_CreatePoolableAndAddTo(IList group, string name, uint slot) where T : class, ICommand, new() 44 | { 45 | SlottableEnum slottable = SlottableEnum._Internal_CreateAndAddTo(group, name, slot); 46 | _cmdPools[slottable] = new CommandObjectPool(DefaultCommandPoolSize); 47 | return slottable; 48 | } 49 | 50 | } 51 | 52 | public interface IObjectPool 53 | { 54 | object Get(); 55 | 56 | void Return(object obj); 57 | } 58 | 59 | public class CommandObjectPool : IObjectPool where TCommand : class, ICommand, new() 60 | { 61 | public DefaultObjectPool Pool { get; } 62 | 63 | public CommandObjectPool(int poolSize) 64 | { 65 | this.Pool = new(new DefaultPooledObjectPolicy(), poolSize); 66 | } 67 | 68 | public object Get() 69 | => Pool.Get(); 70 | 71 | public void Return(object obj) 72 | { 73 | if (Guard.IsNotNullOf(obj, out TCommand command)) 74 | Pool.Return(command); 75 | } 76 | 77 | } 78 | 79 | public interface ICommandTarget { } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Source/Commands/CommandContext.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: CommandContext.cs 3 | /// Date: August 5, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | using EppNet.Node; 7 | using EppNet.Objects; 8 | using EppNet.Time; 9 | using EppNet.Utilities; 10 | 11 | using System; 12 | using System.Diagnostics.CodeAnalysis; 13 | 14 | namespace EppNet.Commands 15 | { 16 | /// 17 | /// Command contexts are necessary for s to function. They provide 18 | /// the context necessary for a to execute;
without this, the 19 | /// can't execute anything. 20 | ///
21 | public class CommandContext : INodeDescendant 22 | { 23 | 24 | public NetworkNode Node { get => _node; } 25 | public readonly ICommandTarget Target; 26 | 27 | public TimeSpan Timestamp { private set; get; } 28 | 29 | protected readonly NetworkNode _node; 30 | 31 | /// 32 | /// Creates a new command context assuming that the specified derives from 33 | /// . Uses if no Timestamp provided.
34 | /// NOTE: Throws if does not derive from . 35 | ///
36 | /// 37 | /// 38 | /// 39 | /// 40 | 41 | public CommandContext([NotNull] ICommandTarget target, TimeSpan? time = null) 42 | { 43 | Guard.AgainstNull(target); 44 | this.Target = target; 45 | 46 | if (target is INodeDescendant nDesc) 47 | this._node = nDesc.Node; 48 | else 49 | throw new InvalidOperationException("Must specify NetworkNode explicitly in separate constructor!"); 50 | 51 | this.Timestamp = time ?? _node.Time; 52 | } 53 | 54 | /// 55 | /// Creates a new command context with the specified and . 56 | /// Uses if no Timestamp provided.
57 | ///
58 | /// 59 | /// 60 | /// 61 | /// 62 | 63 | public CommandContext([NotNull] ICommandTarget target, [NotNull] NetworkNode node, TimeSpan? time = null) 64 | { 65 | Guard.AgainstNull(target, node); 66 | this.Target = target; 67 | this._node = node; 68 | this.Timestamp = time ?? _node.Time; 69 | } 70 | 71 | protected EnumCommandResult _Internal_LookupObject(long id, out ObjectSlot slot) 72 | { 73 | ObjectService service = Node.Services.GetService(); 74 | slot = default; 75 | 76 | if (!this.IsNotNull(arg: service, tmpMsg: new("Object Service could not be found!"), fatal: true)) 77 | return EnumCommandResult.NoService; 78 | 79 | if (service.TryGetById(id, out slot)) 80 | return EnumCommandResult.Ok; 81 | 82 | return EnumCommandResult.NotFound; 83 | } 84 | 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Source/Commands/EnumCommandResult.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: EnumCommandResult.cs 3 | /// Date: August 4, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | namespace EppNet.Commands 8 | { 9 | 10 | public enum EnumCommandResult 11 | { 12 | 13 | /// 14 | /// The command executed successfully. 15 | /// 16 | Ok = 0, 17 | 18 | /// 19 | /// The relevant objects could not be found and the command 20 | /// could not proceed. 21 | /// 22 | NotFound = 1, 23 | 24 | /// 25 | /// The relevant objects aren't in the correct state to 26 | /// proceed with the command. 27 | /// 28 | InvalidState = 2, 29 | 30 | /// 31 | /// Provided argument(s) aren't valid for the specified command. 32 | /// 33 | BadArgument = 3, 34 | 35 | /// 36 | /// The relevant service to execute the command is offline or inaccessible. 37 | /// 38 | NoService = 4, 39 | 40 | /// 41 | /// The command is unavailable. 42 | /// 43 | Unavailable = 5, 44 | 45 | /// 46 | /// Nodes of relevant objects don't match
47 | /// Cannot mismatch objects of different nodes due to sandboxing 48 | ///
49 | NodeMismatch = 6 50 | } 51 | 52 | public static class EnumCommandResultExtensions 53 | { 54 | 55 | public static bool IsOk(this EnumCommandResult result) 56 | => result == EnumCommandResult.Ok; 57 | public static bool IsOk(EnumCommandResult? result) 58 | => result.HasValue && 59 | result.Value.IsOk(); 60 | 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /Source/Commands/SlimCommand.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: SlimCommand.cs 3 | /// Date: August 5, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using Microsoft.Extensions.ObjectPool; 10 | 11 | using System; 12 | using System.Diagnostics.CodeAnalysis; 13 | 14 | 15 | namespace EppNet.Commands 16 | { 17 | 18 | public interface ICommand : IDisposable 19 | { 20 | public EnumCommandResult Execute(in object context); 21 | } 22 | 23 | public interface ICommand : ICommand where TContext : CommandContext 24 | { 25 | public EnumCommandResult Execute(in TContext context); 26 | } 27 | 28 | /// 29 | /// Slim Commands are meant to store as little data as possible as this class is meant 30 | /// to be stored in a collection. If every command stored all the context information (filled in by ), 31 | /// then we could very easily waste a TON of memory. If a command is stored in a collection, its 32 | /// executing context is most likely the same as all the other commands. 33 | /// 34 | 35 | public abstract class SlimCommand : ICommand where T : CommandContext 36 | { 37 | 38 | public readonly SlottableEnum EnumType; 39 | public readonly IObjectPool Pool; 40 | 41 | protected SlimCommand([NotNull] SlottableEnum enumType) 42 | { 43 | this.EnumType = enumType; 44 | 45 | Commands._cmdPools.TryGetValue(enumType, out Pool); 46 | } 47 | 48 | /// 49 | /// What is this garbage? A slim command is meant to only contain distinguishing data about 50 | /// a particular command to save memory. 51 | /// 52 | /// 53 | /// 54 | public abstract EnumCommandResult Execute(in T context); 55 | 56 | public EnumCommandResult Execute(in object context) => Execute(context as T); 57 | public abstract void Dispose(); 58 | 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Source/Connections/ClientConnection.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ClientConnection.cs 3 | /// Date: September 12, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | using ENet; 7 | 8 | using EppNet.Data.Datagrams; 9 | using EppNet.Sockets; 10 | 11 | namespace EppNet.Connections 12 | { 13 | public class ClientConnection : Connection 14 | { 15 | 16 | public ClientConnection() { } 17 | 18 | internal ClientConnection(BaseSocket socket, Peer peer) : base(socket, peer) { } 19 | 20 | /// 21 | /// Forcibly closes the connection. 22 | /// 23 | 24 | public void Eject() 25 | => Eject(DisconnectReasons.Ejected); 26 | 27 | /// 28 | /// Forcibly closes the connection with the 29 | /// specified reason. 30 | /// 31 | /// 32 | 33 | public void Eject(DisconnectReason reason) 34 | { 35 | using DisconnectDatagram datagram = new(reason); 36 | SendInstant(datagram); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/Connections/ConnectEvent.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ConnectEvent.cs 3 | /// Date: July 22, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | using EppNet.Time; 9 | 10 | namespace EppNet.Connections 11 | { 12 | 13 | public readonly struct ConnectEvent : ITimestamped 14 | { 15 | 16 | public readonly Connection Connection; 17 | public readonly Timestamp Timestamp { get; } 18 | 19 | public ConnectEvent(Connection connection) 20 | { 21 | this.Connection = connection; 22 | this.Timestamp = new(connection.Time()); 23 | } 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Source/Connections/ConnectionSlot.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ConnectionSlot.cs 3 | /// Date: February 17, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Collections; 8 | 9 | using System; 10 | 11 | namespace EppNet.Connections 12 | { 13 | 14 | public struct ConnectionSlot : IPageable, IEquatable 15 | { 16 | 17 | public IPage Page { set; get; } 18 | 19 | public long ID { set; get; } 20 | 21 | public bool Allocated { set; get; } 22 | 23 | public Connection Connection { set; get; } 24 | 25 | public ConnectionSlot(IPage page, long id, Connection connection) 26 | { 27 | this.Page = page; 28 | this.ID = id; 29 | this.Connection = connection; 30 | this.Allocated = false; 31 | } 32 | 33 | public readonly override bool Equals(object obj) 34 | => obj is ConnectionSlot other && 35 | Equals(other); 36 | 37 | public readonly bool Equals(ConnectionSlot other) 38 | => other.Page == Page && 39 | other.ID == ID && 40 | other.Connection == Connection; 41 | 42 | public readonly override int GetHashCode() 43 | => ID.GetHashCode() ^ 44 | Page.GetHashCode() ^ 45 | (Connection != null ? Connection.GetHashCode() : 1); 46 | 47 | public void Dispose() 48 | { 49 | if (Connection != null && Connection is IDisposable disposable) 50 | disposable.Dispose(); 51 | 52 | this.Connection = null; 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Source/Connections/DisconnectEvent.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: DisconnectEvent.cs 3 | /// Date: September 12, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Connections 10 | { 11 | 12 | public readonly struct DisconnectEvent 13 | { 14 | 15 | public readonly Connection Connection; 16 | public readonly DisconnectReason Reason; 17 | 18 | /// 19 | /// Timestamp is the same as 20 | /// 21 | public readonly TimeSpan Time; 22 | 23 | public DisconnectEvent(Connection connection, DisconnectReason reason) 24 | { 25 | this.Connection = connection; 26 | this.Reason = reason; 27 | this.Time = connection.Node.Time; 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Source/Connections/DisconnectReason.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: DisconnectReason.cs 3 | /// Date: September 12, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace EppNet.Connections 11 | { 12 | 13 | public static class DisconnectReasons 14 | { 15 | internal static List _reasons = new(); 16 | 17 | public static readonly DisconnectReason Unknown = new("Connection to the remote host was lost."); 18 | public static readonly DisconnectReason TimedOut = new("Connection to the remote host has timed out."); 19 | public static readonly DisconnectReason Ejected = new("Connection was forcibly closed by the remote host."); 20 | public static readonly DisconnectReason Quit = new(string.Empty); 21 | 22 | public static DisconnectReason GetFromID(byte id) 23 | { 24 | int index = (int)id; 25 | 26 | if (-1 < index && index < _reasons.Count) 27 | return _reasons[index]; 28 | 29 | return Unknown; 30 | } 31 | 32 | public static DisconnectReason GetFromID(uint id) => GetFromID((byte)id); 33 | 34 | public static DisconnectReason From(DisconnectReason baseReason, string message) => new(baseReason.ID, message); 35 | } 36 | 37 | public readonly struct DisconnectReason : IEquatable 38 | { 39 | 40 | #region Operators 41 | 42 | public static bool operator ==(DisconnectReason left, DisconnectReason right) => left.Equals(right); 43 | public static bool operator !=(DisconnectReason left, DisconnectReason right) => !left.Equals(right); 44 | 45 | #endregion 46 | 47 | public readonly byte ID; 48 | public readonly string Message; 49 | 50 | public DisconnectReason(string message) 51 | { 52 | this.ID = (byte) DisconnectReasons._reasons.Count; 53 | this.Message = message; 54 | 55 | DisconnectReasons._reasons.Add(new DisconnectReason(message)); 56 | } 57 | 58 | internal DisconnectReason(byte id, string message) 59 | { 60 | this.ID = id; 61 | this.Message = message; 62 | } 63 | 64 | public bool Equals(DisconnectReason other) => ID == other.ID && Message.Equals(other.Message, StringComparison.Ordinal); 65 | 66 | public override bool Equals(object obj) 67 | { 68 | if (obj == null) 69 | return false; 70 | 71 | if (obj is DisconnectReason otherReason) 72 | return ID == otherReason.ID && Message.Equals(otherReason.Message, StringComparison.Ordinal); 73 | 74 | return false; 75 | } 76 | 77 | public override int GetHashCode() 78 | { 79 | int hashCode = ID; 80 | hashCode ^= Message.GetHashCode(); 81 | return hashCode; 82 | } 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Source/Connections/ServerConnection.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ServerConnection.cs 3 | /// Date: September 2, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | using ENet; 7 | 8 | using EppNet.Sockets; 9 | 10 | using System; 11 | 12 | namespace EppNet.Connections 13 | { 14 | /// 15 | /// This represents a connection to the server 16 | /// 17 | public class ServerConnection : Connection 18 | { 19 | 20 | /// 21 | /// This is the time the simulation started on the server-side.
22 | /// It is unaffected by clock synchronization events as this time 23 | /// is set at connection reception. 24 | ///
25 | public TimeSpan ServerTime { internal set; get; } 26 | 27 | public ServerConnection(BaseSocket socket, Peer peer) : base(socket, peer) 28 | { 29 | this.ServerTime = default; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/Data/Datagrams/Datagram.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: Datagram.cs 3 | /// Date: September 10, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Connections; 8 | using EppNet.Logging; 9 | using EppNet.Messaging; 10 | 11 | namespace EppNet.Data.Datagrams 12 | { 13 | 14 | public abstract class Datagram : BytePayload, IDatagram, ILoggable 15 | { 16 | 17 | public static byte GetDatagramIndexAndData(BytePayload payload, out byte data) 18 | { 19 | byte b = payload.ReadByte(); 20 | data = 0; 21 | 22 | if (AvailableHeaderBits > 0) 23 | { 24 | // First, Let's extract the bits related to the Datagram's key (index) 25 | // This starts at bit 0 and goes to (8 - AvailableHeaderBits) - 1. 26 | byte indexByte = (byte)(b & ((1 << 8 - AvailableHeaderBits) - 1)); 27 | 28 | // Now, let's fetch the data 29 | int mask = (1 << AvailableHeaderBits) - 1; 30 | int shifted = b >> (8 - AvailableHeaderBits); 31 | int extractedBits = shifted & mask; 32 | data = (byte)extractedBits; 33 | 34 | b = indexByte; 35 | } 36 | 37 | return b; 38 | } 39 | 40 | /// 41 | /// The length of every header in bytes 42 | /// 43 | public static int HeaderByteLength { internal set; get; } 44 | 45 | public static int AvailableHeaderBits { internal set; get; } 46 | public static int MaxHeaderDataDecimalValue { internal set; get; } 47 | 48 | public ILoggable Notify { get => this; } 49 | 50 | public byte Index { internal set; get; } 51 | 52 | /// 53 | /// Denotes special data that should be at most 3 bits 54 | /// and is sent within the header. 55 | /// 56 | public byte HeaderData { set; get; } 57 | 58 | public bool Written { internal set; get; } 59 | public long Size { get => Length; } 60 | 61 | /// 62 | /// This is populated remotely. 63 | /// 64 | public Connection Sender { internal set; get; } 65 | public byte ChannelID { internal set; get; } 66 | public bool Collectible { get => _collectible; } 67 | 68 | protected bool _collectible; 69 | 70 | public Datagram() 71 | { 72 | this.ChannelID = 0x0; 73 | this.Sender = null; 74 | this.Written = false; 75 | this._collectible = true; 76 | } 77 | 78 | public virtual void Read() { } 79 | public virtual void Write() 80 | { 81 | WriteHeader(); 82 | this.Written = true; 83 | 84 | if (!_collectible && ChannelID != (byte)Channels.Connectivity) 85 | Notify.Warning("Datagram isn't collectible and isn't communicating on the connectivity channel. Consider making it collectible!"); 86 | } 87 | 88 | public virtual void WriteHeader() 89 | { 90 | this.WriteUInt8(GetHeader()); 91 | } 92 | 93 | public byte GetHeader() 94 | { 95 | byte header = Index; 96 | 97 | // Let's see if we have header data to write. 98 | if (HeaderData > 0 && HeaderFitsExtraData()) 99 | { 100 | byte edit = header; 101 | int startBit = 8 - AvailableHeaderBits; 102 | int mask = ((1 << AvailableHeaderBits) - 1) << startBit; 103 | edit &= (byte)~mask; 104 | 105 | int alignedSourceBits = (HeaderData << startBit) & mask; 106 | edit |= (byte)alignedSourceBits; 107 | header = edit; 108 | } 109 | 110 | return header; 111 | } 112 | 113 | public byte GetChannelID() => ChannelID; 114 | 115 | public Connection GetSender() => Sender; 116 | 117 | public bool HeaderFitsExtraData() 118 | => HeaderData <= MaxHeaderDataDecimalValue; 119 | public bool HasHeaderData() 120 | => HeaderData > 0; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /Source/Data/Datagrams/DisconnectDatagram.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: DisconnectDatagram.cs 3 | /// Date: September 25, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Connections; 8 | using EppNet.Utilities; 9 | 10 | namespace EppNet.Data.Datagrams 11 | { 12 | 13 | public class DisconnectDatagram : Datagram 14 | { 15 | public DisconnectReason Reason { internal set; get; } 16 | 17 | public DisconnectDatagram() 18 | { 19 | this.Reason = DisconnectReasons.Quit; 20 | this.ChannelID = 0x0; 21 | } 22 | 23 | public DisconnectDatagram(DisconnectReason reason) : this() 24 | { 25 | this.Reason = reason; 26 | } 27 | 28 | public override void Write() 29 | { 30 | base.Write(); 31 | 32 | DisconnectReason generic = DisconnectReasons.GetFromID(Reason.ID); 33 | byte reasonId = Reason.ID; 34 | 35 | if (generic.Message != Reason.Message) 36 | { 37 | // The reason message is different than the generic. Let's turn on 38 | // the first bit to denote that. 39 | reasonId = reasonId.EnableBit(7); 40 | this.WriteByte(reasonId); 41 | this.WriteString8(Reason.Message); 42 | return; 43 | } 44 | 45 | this.WriteByte(reasonId); 46 | } 47 | 48 | public override void Read() 49 | { 50 | base.Read(); 51 | 52 | byte reasonId = this.ReadByte(); 53 | string reasonMessage = string.Empty; 54 | 55 | if (reasonId.IsBitOn(7)) 56 | { 57 | // Bit 7 was enabled, so that means we have a custom message 58 | reasonId = reasonId.ResetBit(7); 59 | reasonMessage = this.ReadString8(); 60 | } 61 | 62 | this.Reason = DisconnectReasons.GetFromID(Reason.ID); 63 | 64 | if (Reason == DisconnectReasons.Unknown) 65 | Reason = new DisconnectReason(reasonId, reasonMessage); 66 | 67 | else if (!string.IsNullOrEmpty(reasonMessage)) 68 | Reason = DisconnectReasons.From(Reason, reasonMessage); 69 | } 70 | 71 | public override void Dispose() 72 | { 73 | this.Reason = default; 74 | base.Dispose(); 75 | } 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Source/Data/Datagrams/IDatagram.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: IDatagram.cs 3 | /// Date: September 12, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Connections; 8 | 9 | using System; 10 | 11 | namespace EppNet.Data.Datagrams 12 | { 13 | 14 | public interface IDatagram : IDisposable 15 | { 16 | public byte Index { get; } 17 | 18 | public long Size { get; } 19 | public bool Written { get; } 20 | 21 | /// 22 | /// Collectible datagrams are datagrams that are collected within a channel so something can be 23 | /// executed with it later on. Set this flag to false if you are executing some code from within the Read function 24 | /// 25 | public bool Collectible { get; } 26 | 27 | public void Read(); 28 | 29 | public void Write(); 30 | 31 | public byte[] Pack(); 32 | 33 | public void WriteHeader(); 34 | public byte GetHeader(); 35 | public byte GetChannelID(); 36 | public Connection GetSender(); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Source/Data/Datagrams/ObjectUpdateDatagram.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ObjectUpdateDatagram.cs 3 | /// Date: September 25, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | using EppNet.Objects; 9 | 10 | using System.Collections.Generic; 11 | 12 | namespace EppNet.Data.Datagrams 13 | { 14 | 15 | public class ObjectUpdateDatagram : Datagram 16 | { 17 | 18 | public ObjectAgent[] Objects { protected set; get; } 19 | 20 | /// 21 | /// Stores a key-value pair of s with incoming or outgoing updates. 22 | /// 23 | public Dictionary> Transients { protected set; get; } 24 | 25 | public bool SnapshotUpdates { protected set; get; } 26 | 27 | public ObjectUpdateDatagram() 28 | { 29 | this.ChannelID = 0x1; 30 | this.Objects = null; 31 | this.Transients = new Dictionary>(); 32 | this.SnapshotUpdates = false; 33 | } 34 | 35 | public ObjectUpdateDatagram(bool snapshotUpdates, params ObjectAgent[] objectsToUpdate) : this() 36 | { 37 | this.SnapshotUpdates = snapshotUpdates; 38 | this.Objects = objectsToUpdate; 39 | 40 | if (SnapshotUpdates) 41 | this.ChannelID = 0x2; 42 | } 43 | 44 | public override void Write() 45 | { 46 | base.Write(); 47 | 48 | // Let's only send the objects that have pending updates. 49 | /* 50 | foreach (ObjectDelegate obj in Objects) 51 | { 52 | List outgoing = SnapshotUpdates ? 53 | obj.OutgoingSnapshotUpdates.Flush() 54 | : obj.OutgoingReliableUpdates.FlushQueue(); 55 | 56 | if (outgoing.Count > 0) 57 | Transients.Add(obj, outgoing); 58 | }*/ 59 | 60 | // Let's record how many objects are in this datagram. 61 | this.WriteByte((byte)Transients.Count); 62 | 63 | foreach (KeyValuePair> kvp in Transients) 64 | { 65 | // Write the ID of the object. 66 | this.WriteULong((ulong) kvp.Key.ID); 67 | 68 | // Let's record the number of updates incoming. 69 | this.WriteByte((byte)kvp.Value.Count); 70 | 71 | foreach (Update update in kvp.Value) 72 | update.WriteTo(this); 73 | } 74 | 75 | } 76 | 77 | public override void Read() 78 | { 79 | base.Read(); 80 | 81 | byte numObjects = this.ReadByte(); 82 | 83 | for (int i = 0; i < numObjects; i++) 84 | { 85 | ulong id = (ulong) this.ReadULong(); 86 | /* 87 | ObjectAgent objDelegate = ObjectManager.Get().GetObject((long)id); 88 | 89 | if (objDelegate == null) 90 | { 91 | // FIXME: Have to handle when we don't have the object created yet. 92 | continue; 93 | } 94 | 95 | byte numUpdates = ReadByte(); 96 | List updates = new List(); 97 | 98 | for (int j = 0; j < numUpdates; j++) 99 | updates.Add(Update.From(objDelegate, this)); 100 | 101 | Transients.Add(objDelegate, updates);*/ 102 | } 103 | } 104 | 105 | public override void Dispose() 106 | { 107 | this.Objects = null; 108 | this.Transients.Clear(); 109 | base.Dispose(); 110 | } 111 | 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /Source/Data/Datagrams/PingDatagram.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: PingDatagram.cs 3 | /// Date: September 12, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Messaging; 8 | 9 | using System; 10 | 11 | namespace EppNet.Data.Datagrams 12 | { 13 | 14 | public class PingDatagram : Datagram 15 | { 16 | public float Time { set; get; } 17 | public float ReceivedTime { set; get; } 18 | 19 | public PingDatagram() 20 | { 21 | this.ChannelID = (byte)Channels.Connectivity; 22 | this._collectible = false; 23 | } 24 | 25 | public override void Write() 26 | { 27 | base.Write(); 28 | this.WriteFloat(Time); 29 | this.WriteFloat(ReceivedTime); 30 | } 31 | 32 | public override void Read() 33 | { 34 | base.Read(); 35 | 36 | this.Time = this.ReadFloat(); 37 | 38 | if (Sender.IsServer()) 39 | { 40 | this.ReceivedTime = this.ReadFloat(); 41 | 42 | TimeSpan remoteTimeSpan = TimeSpan.FromMilliseconds(Time); 43 | Sender.Node.Socket.Clock.Synchronize(remoteTimeSpan); 44 | return; 45 | } 46 | 47 | PingDatagram pong = new() 48 | { 49 | ReceivedTime = Time, 50 | Time = (float)Sender.Node.Socket.Node.Time.TotalMilliseconds 51 | }; 52 | 53 | // Send an acknowledgement 54 | Sender.SendInstant(pong); 55 | pong.Dispose(); 56 | } 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Source/Data/ICloneable.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ICloneable.cs 3 | /// Date: March 28, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Data 8 | { 9 | 10 | /// 11 | /// Not to be confused with , this creates a type-safe clone.
12 | /// Output should NEVER be null 13 | ///
14 | /// 15 | 16 | public interface ICloneable 17 | { 18 | 19 | /// 20 | /// Creates a type-safe clone 21 | /// 22 | /// Copy; NEVER null 23 | T Clone(); 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Source/Data/IMessageHandler.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: IMessageHandler.cs 3 | /// Date: September 14, 2023 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Data.Datagrams; 8 | 9 | namespace EppNet.Data 10 | { 11 | 12 | public interface IMessageHandler 13 | { 14 | 15 | public void OnDatagramReceived(Datagram datagram); 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Source/Data/INameable.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: INameable.cs 3 | /// Date: July 28, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Data 8 | { 9 | 10 | public interface INameable 11 | { 12 | 13 | public string Name { get; } 14 | 15 | /// 16 | /// Checks if the name associated with this is valid
17 | /// Default is ! 18 | ///
19 | /// 20 | public bool IsNameValid() => 21 | !string.IsNullOrEmpty(Name); 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Source/Data/ISignatureEquatable.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ISignatureEquatable.cs 3 | /// Date: March 28, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Data 8 | { 9 | 10 | /// 11 | /// When dealing with generics, this ensures that the types are equivalent 12 | /// 13 | /// 14 | 15 | public interface ISignatureEquatable 16 | { 17 | 18 | /// 19 | /// Checks for type equivalence 20 | /// 21 | /// 22 | /// 23 | bool SignatureEquals(T other); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Source/Data/InternalProperty.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: InternalProperty.cs 3 | /// Date: February 16, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Utilities; 8 | 9 | using System; 10 | 11 | namespace EppNet.Data 12 | { 13 | 14 | /// 15 | /// Wrapper of a property only settable by this library
16 | /// Created to get around interface restrictions 17 | ///
18 | /// 19 | 20 | public class InternalProperty 21 | { 22 | 23 | public T Value { private set; get; } 24 | 25 | public Action OnChanged; 26 | 27 | public InternalProperty() 28 | { 29 | this.Value = default; 30 | this.OnChanged = null; 31 | } 32 | 33 | internal void Set(T newValue) 34 | { 35 | T current = Value; 36 | this.Value = newValue; 37 | 38 | OnChanged?.GlobalInvoke(newValue, current); 39 | } 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Source/Data/LockQueue.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: LockQueue.cs 3 | /// Date: September 27, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System.Collections.Generic; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | namespace EppNet.Data 11 | { 12 | 13 | public class LockQueue : Queue 14 | { 15 | 16 | protected object _lock; 17 | 18 | public LockQueue() : base() 19 | { 20 | _lock = new object(); 21 | } 22 | 23 | public LockQueue(int capacity) : base(capacity) 24 | { 25 | _lock = new object(); 26 | } 27 | 28 | public LockQueue(IEnumerable items) : base(items) 29 | { 30 | _lock = new object(); 31 | } 32 | 33 | public new void Enqueue(T item) 34 | { 35 | lock (_lock) 36 | base.Enqueue(item); 37 | } 38 | 39 | public virtual bool TryEnqueue(T item) 40 | { 41 | lock (_lock) 42 | { 43 | bool contains = base.Contains(item); 44 | 45 | if (!contains) 46 | base.Enqueue(item); 47 | 48 | return !contains; 49 | } 50 | } 51 | 52 | public new T Dequeue() 53 | { 54 | lock (_lock) 55 | return base.Dequeue(); 56 | } 57 | 58 | public new bool TryDequeue([MaybeNullWhen(false)] out T item) 59 | { 60 | lock (_lock) 61 | return base.TryDequeue(out item); 62 | } 63 | 64 | public new T Peek() 65 | { 66 | lock (_lock) 67 | return base.Peek(); 68 | } 69 | 70 | public new bool TryPeek([MaybeNullWhen(false)] out T result) 71 | { 72 | lock (_lock) 73 | return base.TryPeek(out result); 74 | } 75 | 76 | public new bool Contains(T item) 77 | { 78 | lock (_lock) 79 | return base.Contains(item); 80 | } 81 | 82 | public new T[] ToArray() 83 | { 84 | lock (_lock) 85 | return base.ToArray(); 86 | } 87 | 88 | public new void CopyTo(T[] array, int arrayIndex) 89 | { 90 | lock (_lock) 91 | base.CopyTo(array, arrayIndex); 92 | } 93 | 94 | public new void Clear() 95 | { 96 | lock (_lock) 97 | base.Clear(); 98 | } 99 | 100 | public virtual List FlushQueue() 101 | { 102 | lock (_lock) 103 | { 104 | List list = new List(); 105 | 106 | for (int i = 0; i < Count; i++) 107 | list.Add(base.Dequeue()); 108 | 109 | return list; 110 | } 111 | } 112 | 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /Source/Data/NetworkArg.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkArg.cs 3 | /// Date: March 28, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace EppNet.Data 11 | { 12 | public interface INetworkArg : ICloneable, IEquatable, ISignatureEquatable 13 | { 14 | public Type Type { get; } 15 | 16 | public object BoxedValue { get; } 17 | 18 | public object Get() => 19 | BoxedValue; 20 | } 21 | 22 | public interface INetworkArg : INetworkArg 23 | { 24 | public TArg Value { get; } 25 | 26 | public new TArg Get() => 27 | Value; 28 | } 29 | 30 | public sealed class NetworkArg : INetworkArg 31 | { 32 | 33 | /// 34 | /// The object wrapped by this class. 35 | /// 36 | 37 | public TArg Value { set; get; } 38 | 39 | /// 40 | /// NOTE: Uses boxing to set the internal value!
41 | /// Prefer typesafe assignment with 42 | ///
43 | 44 | public object BoxedValue 45 | { 46 | set 47 | { 48 | if (value is TArg arg) 49 | Value = arg; 50 | else 51 | throw new ArgumentException($"Value must be of type {typeof(TArg)}"); 52 | } 53 | 54 | get => Value; 55 | } 56 | 57 | /// 58 | /// The of the value wrapped by this class. 59 | /// 60 | 61 | public Type Type { get => typeof(TArg); } 62 | 63 | public NetworkArg() { } 64 | 65 | public NetworkArg(TArg value) 66 | { 67 | this.Value = value; 68 | } 69 | 70 | /// 71 | /// Creates a "shallow" copy; meaning, if
72 | /// is a reference type, the copy will refer to the existing object. 73 | ///
74 | /// 75 | 76 | public INetworkArg Clone() => 77 | new NetworkArg(Value); 78 | 79 | /// 80 | /// Checks if the provided is of the 81 | /// same type AND
the value is equivalent according to the 82 | /// default for . 83 | ///
84 | /// 85 | /// 86 | 87 | public bool Equals(INetworkArg other) => 88 | other is NetworkArg oNetArg && 89 | EqualityComparer.Default.Equals(Value, oNetArg.Value); 90 | 91 | /// 92 | /// Checks if the provided wraps an equivalent 93 | /// . 94 | /// 95 | /// 96 | /// 97 | 98 | public bool SignatureEquals(INetworkArg other) => 99 | other is NetworkArg; 100 | 101 | /// 102 | /// Checks if the provided wraps a compatible 103 | /// .
104 | /// Compatibility is:
105 | /// - Type argument equivalence; OR
106 | /// - Direct or indirect type derivation 107 | ///
108 | /// 109 | /// 110 | 111 | public bool IsCompatibleWith(INetworkArg other) => 112 | SignatureEquals(other) || 113 | other is not null && 114 | (other.Type.IsAssignableFrom(Type) || 115 | Type.IsAssignableFrom(other.Type)); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /Source/Data/Resolvers/TimeSpanResolver.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: TimeSpanResolver.cs 3 | /// Date: September 30, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Attributes; 8 | 9 | using System; 10 | using System.Buffers.Binary; 11 | 12 | namespace EppNet.Data 13 | { 14 | 15 | [NetworkTypeResolver] 16 | public class TimeSpanResolver : Resolver 17 | { 18 | public static readonly TimeSpanResolver Instance = new(); 19 | 20 | public TimeSpanResolver() : base(sizeof(long)) { } 21 | 22 | protected override ReadResult _Internal_Read(BytePayload payload, out TimeSpan output) 23 | { 24 | Span buffer = stackalloc byte[Size]; 25 | int read = payload.Stream.Read(buffer); 26 | long ticks = 0; 27 | 28 | ReadResult result = BinaryPrimitives.TryReadInt64LittleEndian(buffer, out ticks) 29 | ? ReadResult.Success : ReadResult.Failed; 30 | 31 | output = TimeSpan.FromTicks(ticks); 32 | return result; 33 | } 34 | 35 | protected override bool _Internal_Write(BytePayload payload, TimeSpan input) 36 | => BinaryPrimitives.TryWriteInt64LittleEndian(payload.Stream.GetSpan(Size), input.Ticks); 37 | } 38 | 39 | public static class TimeSpanResolverExtensions 40 | { 41 | 42 | /// 43 | /// Writes a to the wire.
44 | /// Internally, this writes the ticks as a long 45 | ///
46 | /// 47 | /// 48 | 49 | public static void Write(this BytePayload payload, TimeSpan input) 50 | => TimeSpanResolver.Instance.Write(payload, input); 51 | 52 | /// 53 | /// Writes an array of to the wire.
54 | /// See for more information. 55 | ///
56 | /// 57 | /// 58 | public static void Write(this BytePayload payload, TimeSpan[] input) 59 | => TimeSpanResolver.Instance.Write(payload, input); 60 | 61 | /// 62 | /// Writes an array of to the wire.
63 | /// See for more information. 64 | ///
65 | /// 66 | /// 67 | public static void WriteArray(this BytePayload payload, TimeSpan[] input) 68 | => TimeSpanResolver.Instance.Write(payload, input); 69 | 70 | /// 71 | /// Reads a from the wire.
72 | ///
73 | /// 74 | 75 | public static TimeSpan Read(this BytePayload payload) 76 | { 77 | TimeSpanResolver.Instance.Read(payload, out TimeSpan result); 78 | return result; 79 | } 80 | 81 | /// 82 | /// Reads a array from the wire.
83 | ///
84 | /// 85 | 86 | public static TimeSpan[] ReadArray(this BytePayload payload) 87 | { 88 | TimeSpanResolver.Instance.Read(payload, out TimeSpan[] result); 89 | return result; 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /Source/Data/StringTypes.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: StringTypes.cs 3 | /// Date: September 14, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | 10 | namespace EppNet.Data 11 | { 12 | 13 | /// 14 | /// Wrapper around strings that signify you would 15 | /// like 16-bit strings to be used for wire communications. 16 | /// 17 | 18 | public readonly struct Str16 19 | { 20 | 21 | public readonly string Value; 22 | 23 | public Str16(string value) 24 | { 25 | if (value?.Length > byte.MaxValue) 26 | throw new ArgumentOutOfRangeException($"String16 must be between 0 and {ushort.MaxValue}"); 27 | 28 | this.Value = value; 29 | } 30 | 31 | public static implicit operator Str16(string value) 32 | => new(value); 33 | 34 | public static implicit operator string(Str16 a) 35 | => a.Value; 36 | 37 | } 38 | 39 | /// 40 | /// Wrapper around strings that signify you would 41 | /// like 8-bit strings to be used for wire communications. 42 | /// 43 | 44 | public readonly struct Str8 45 | { 46 | 47 | public readonly string Value; 48 | 49 | public Str8(string value) 50 | { 51 | if (value.Length > byte.MaxValue) 52 | throw new ArgumentOutOfRangeException($"String8 must be between 0 and {byte.MaxValue}"); 53 | 54 | this.Value = value; 55 | } 56 | 57 | public static implicit operator Str8(string value) 58 | => new(value); 59 | 60 | public static implicit operator string(Str8 a) 61 | => a.Value; 62 | 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Source/Data/Timestamp.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: Timestamp.cs 3 | /// Date: February 16, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Node; 8 | using EppNet.Utilities; 9 | 10 | using System; 11 | using System.Diagnostics.CodeAnalysis; 12 | 13 | namespace EppNet.Data 14 | { 15 | 16 | /// 17 | /// Essentially a tuple grouping the network time shown and the local monotonic time
18 | /// Comparisons are done using the monotonic time to keep timestamps unaffected by clock syncs 19 | ///
20 | public readonly struct Timestamp : IComparable, IComparable, IEquatable 21 | { 22 | 23 | public static readonly Timestamp Zero = new(TimeSpan.Zero, TimeSpan.Zero); 24 | 25 | /// 26 | /// The time displayed on the network clock 27 | /// 28 | public TimeSpan NetworkTime { get; } 29 | 30 | /// 31 | /// The time as specified by which is the time since 32 | /// the internal ENet library started (in milliseconds) 33 | /// 34 | public TimeSpan MonotonicTime { get; } 35 | 36 | public Timestamp(TimeSpan netTime) 37 | { 38 | this.NetworkTime = netTime; 39 | this.MonotonicTime = TimeSpan.FromMilliseconds(ENet.Library.Time); 40 | } 41 | 42 | public Timestamp([NotNull] NetworkNode node) 43 | { 44 | Guard.AgainstNull(node); 45 | this.NetworkTime = node.Time; 46 | this.MonotonicTime = TimeSpan.FromMilliseconds(ENet.Library.Time); 47 | } 48 | 49 | public Timestamp([NotNull] INodeDescendant nodeDescendant) 50 | { 51 | Guard.AgainstNull(nodeDescendant); 52 | Guard.AgainstNull(nodeDescendant.Node); 53 | this.NetworkTime = nodeDescendant.Node.Time; 54 | this.MonotonicTime = TimeSpan.FromMilliseconds(ENet.Library.Time); 55 | } 56 | 57 | public Timestamp(TimeSpan netTime, TimeSpan monoTime) 58 | { 59 | this.NetworkTime = netTime; 60 | this.MonotonicTime = monoTime; 61 | } 62 | 63 | public int CompareTo(object obj) 64 | => obj is Timestamp ts ? 65 | CompareTo(ts) : 66 | 0; 67 | 68 | public int CompareTo(Timestamp other) 69 | => other.MonotonicTime.CompareTo(MonotonicTime); 70 | 71 | public override bool Equals(object obj) 72 | => obj is Timestamp ts && 73 | Equals(ts); 74 | 75 | public bool Equals(Timestamp other) 76 | => other.NetworkTime.Equals(NetworkTime) && 77 | other.MonotonicTime.Equals(MonotonicTime); 78 | 79 | public override int GetHashCode() 80 | => NetworkTime.GetHashCode() ^ MonotonicTime.GetHashCode(); 81 | 82 | public override string ToString() 83 | => $"Network(ticks): {NetworkTime.Ticks}\nMonotonic(ticks): {MonotonicTime.Ticks}"; 84 | 85 | public static bool operator ==(Timestamp left, Timestamp right) 86 | => left.Equals(right); 87 | 88 | public static bool operator !=(Timestamp left, Timestamp right) 89 | => !left.Equals(right); 90 | 91 | public static bool operator >(Timestamp left, Timestamp right) 92 | => left.MonotonicTime > right.MonotonicTime; 93 | 94 | public static bool operator <(Timestamp left, Timestamp right) 95 | => left.MonotonicTime < right.MonotonicTime; 96 | 97 | public static bool operator >=(Timestamp left, Timestamp right) 98 | => left.MonotonicTime >= right.MonotonicTime; 99 | 100 | public static bool operator <=(Timestamp left, Timestamp right) 101 | => left.MonotonicTime <= right.MonotonicTime; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /Source/Distribution.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: Distribution.cs 3 | /// Date: September 22, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet 8 | { 9 | 10 | public enum Distribution 11 | { 12 | /// 13 | /// Types or network objects with this distribution setting are 14 | /// shared between the server and the client. 15 | /// 16 | Shared = 0, 17 | 18 | /// 19 | /// Labels a type or network object as server-only; i.e. intended 20 | /// for use on the server side. 21 | /// 22 | Server = 1, 23 | 24 | /// 25 | /// Labels a type or network object as client-only; i.e. intended 26 | /// for use on the client side. 27 | /// 28 | Client = 2 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /Source/Events/EventBase.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: EventBase.cs 3 | /// Date: February 14, 2025 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | using EppNet.Node; 9 | using EppNet.Time; 10 | using EppNet.Utilities; 11 | 12 | using System; 13 | using System.Diagnostics.CodeAnalysis; 14 | 15 | namespace EppNet.Events 16 | { 17 | 18 | public interface IEventBase : ITimestamped, IComparable, IComparable, IEquatable, INodeDescendant 19 | { 20 | public object Sender { get; } 21 | 22 | public object GetSubject(); 23 | 24 | } 25 | 26 | public abstract class EventBase : IEventBase, IComparable>, IEquatable> 27 | { 28 | 29 | public NetworkNode Node { get; } 30 | public TSubject Subject { get; } 31 | public Timestamp Timestamp { get; } 32 | public object Sender { get; } 33 | 34 | protected EventBase([NotNull] NetworkNode node, [NotNull] TSubject subject, object sender) 35 | { 36 | Guard.AgainstNull(node, subject); 37 | this.Node = node; 38 | this.Timestamp = node.Timestamp; 39 | this.Subject = subject; 40 | this.Sender = sender; 41 | } 42 | 43 | protected EventBase([NotNull] NetworkNode node, [NotNull] TSubject subject) : this(node, subject, null) { } 44 | 45 | public object GetSubject() => Subject; 46 | 47 | public override bool Equals(object obj) 48 | => (obj is EventBase other && 49 | Equals(other)) || 50 | (obj is IEventBase otherEB) && 51 | Equals(otherEB); 52 | 53 | public bool Equals(IEventBase other) 54 | => other is not null && 55 | other.Node == Node && 56 | other.Timestamp == Timestamp && 57 | ReferenceEquals(other.GetSubject(), Subject) && 58 | other.Sender == Sender; 59 | 60 | public bool Equals(EventBase other) 61 | => other is not null && 62 | other.Node == Node && 63 | other.Timestamp == Timestamp && 64 | ReferenceEquals(other.Subject, Subject) && 65 | other.Sender == Sender; 66 | 67 | public override int GetHashCode() 68 | => Node.GetHashCode() ^ 69 | Timestamp.GetHashCode() ^ 70 | Subject.GetHashCode() ^ 71 | ((Sender != null) ? Sender.GetHashCode() : 1); 72 | 73 | public int CompareTo(object obj) 74 | => obj is EventBase other ? 75 | CompareTo(other) : 76 | obj is IEventBase otherEB ? 77 | CompareTo(otherEB) : 78 | 0; 79 | 80 | public int CompareTo(IEventBase other) 81 | => other is not null ? 82 | Timestamp.CompareTo(other.Timestamp) : 83 | 0; 84 | 85 | public int CompareTo(EventBase other) 86 | => other is not null ? 87 | Timestamp.CompareTo(other.Timestamp) : 88 | 0; 89 | 90 | public static bool operator ==(EventBase a, EventBase b) 91 | { 92 | if (a == null && b == null) 93 | return true; 94 | 95 | else if (a == null || b == null) 96 | return false; 97 | 98 | return a.Equals(b); 99 | } 100 | 101 | public static bool operator !=(EventBase a, EventBase b) 102 | => !(a == b); 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /Source/Exceptions/BytePayloadReadException.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: BytePayloadReadException.cs 3 | /// Date: April 14, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System; 10 | 11 | namespace EppNet.Exceptions 12 | { 13 | public class BytePayloadReadException : Exception 14 | { 15 | 16 | public static BytePayloadReadException NewOutOfRangeException(BytePayload payload, long position, long bytes) 17 | { 18 | return new BytePayloadReadException(payload, position, OutOfRange, 19 | $"Cannot read {bytes} bytes at position {position} as it is out of range."); 20 | } 21 | 22 | public const int OutOfRange = 0x0; 23 | 24 | public BytePayload Payload { protected set; get; } 25 | 26 | /// 27 | /// Position we tried to read at. 28 | /// 29 | public readonly long Position; 30 | 31 | /// 32 | /// The length of the payload in bytes 33 | /// 34 | public readonly long Length; 35 | 36 | /// 37 | /// The particular read exception 38 | /// 39 | public readonly int ErrorCode; 40 | 41 | public BytePayloadReadException(BytePayload payload, int errorCode) : base() 42 | { 43 | this.Payload = payload; 44 | this.Position = Payload.Stream.Position; 45 | this.Length = Payload.Length; 46 | this.ErrorCode = errorCode; 47 | } 48 | 49 | private BytePayloadReadException(BytePayload payload, long position, int errorCode, string message) : base(message) 50 | { 51 | this.Payload = payload; 52 | this.Position = position; 53 | this.Length = payload.Length; 54 | this.ErrorCode = errorCode; 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Source/Exceptions/ExceptionStrategy.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ExceptionStrategy.cs 3 | /// Date: July 10, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | 8 | namespace EppNet.Exceptions 9 | { 10 | 11 | public enum ExceptionStrategy 12 | { 13 | 14 | /// 15 | /// All exceptions will be thrown --potentially leading to crashes or 16 | /// an unstable state. will try its 17 | /// best to clean up and politely disconnect users. 18 | /// 19 | ThrowAll = 0, 20 | 21 | /// 22 | /// Will log exceptions as they occur 23 | /// 24 | LogOnly = 1 25 | 26 | 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /Source/Exceptions/NetworkException.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkException.cs 3 | /// Date: September 5, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Exceptions 10 | { 11 | public class NetworkException : Exception 12 | { 13 | 14 | public NetworkException(string message) : base(message) 15 | { 16 | //Network.Instance?.PostException(this); 17 | } 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Source/Logging/LogLevelFlags.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: LogLevelFlags.cs 3 | /// Date: July 10, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Utilities; 8 | 9 | using Serilog.Events; 10 | 11 | using System; 12 | using System.Collections.Generic; 13 | 14 | namespace EppNet.Logging 15 | { 16 | 17 | [Flags] 18 | public enum LogLevelFlags 19 | { 20 | 21 | None = 0, 22 | Verbose = 1 << 0, 23 | Debug = 1 << 1, 24 | Info = 1 << 2, 25 | Warn = 1 << 3, 26 | Error = 1 << 4, 27 | Fatal = 1 << 5, 28 | 29 | InfoErrorFatal = Info | Error | Fatal, 30 | InfoWarnFatal = Info | Warn | Fatal, 31 | 32 | /// 33 | /// The default log levels 34 | /// 35 | InfoWarnErrorFatal = Info | Warn | Error | Fatal, 36 | 37 | All = ~(~0 << 6) 38 | } 39 | 40 | public static class LogLevelFlagsExtensions 41 | { 42 | 43 | private static readonly Dictionary _level2Flag = new Dictionary 44 | { 45 | { LogEventLevel.Verbose, LogLevelFlags.Verbose }, 46 | { LogEventLevel.Debug, LogLevelFlags.Debug }, 47 | { LogEventLevel.Information, LogLevelFlags.Info }, 48 | { LogEventLevel.Warning, LogLevelFlags.Warn }, 49 | { LogEventLevel.Error, LogLevelFlags.Error }, 50 | { LogEventLevel.Fatal, LogLevelFlags.Fatal } 51 | }; 52 | 53 | private static readonly Dictionary _flag2Level = new Dictionary 54 | { 55 | { LogLevelFlags.Verbose, LogEventLevel.Verbose }, 56 | { LogLevelFlags.Debug , LogEventLevel.Debug }, 57 | { LogLevelFlags.Info, LogEventLevel.Information }, 58 | { LogLevelFlags.Warn , LogEventLevel.Warning }, 59 | { LogLevelFlags.Error , LogEventLevel.Error }, 60 | { LogLevelFlags.Fatal , LogEventLevel.Fatal } 61 | }; 62 | 63 | /// 64 | /// Converts a Serilog to our 65 | /// 66 | /// 67 | /// 68 | public static LogLevelFlags ToFlag(this LogEventLevel level) => _level2Flag[level]; 69 | 70 | /// 71 | /// Converts a flag to a Serilog
72 | /// NOTE: MUST have only 1 bit enabled or this will return NULL. 73 | ///
74 | /// 75 | /// 76 | public static LogEventLevel? ToLevel(this LogLevelFlags flags) 77 | { 78 | // Checks to see if only one flag is set 79 | if ((flags & (flags - 1)) != 0) 80 | return _flag2Level[flags]; 81 | else 82 | return null; 83 | } 84 | 85 | public static bool IsOn(this LogLevelFlags flags, LogEventLevel level) => flags.IsFlagSet(level.ToFlag()); 86 | 87 | public static bool IsOn(this LogLevelFlags flags, LogLevelFlags level) => flags.IsFlagSet(level); 88 | 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /Source/Logging/LogService.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: LogService.cs 3 | /// Date: July 10, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Services; 8 | 9 | using Serilog; 10 | 11 | namespace EppNet.Logging 12 | { 13 | 14 | /// 15 | /// This service is essentially a Serilog wrapper. 16 | /// 17 | 18 | public class LogService : Service 19 | { 20 | 21 | protected LoggerConfiguration _loggerConfig; 22 | 23 | public LogService(ServiceManager svcMgr) : base(svcMgr) 24 | { 25 | this._loggerConfig = new(); 26 | } 27 | 28 | public override bool Tick(float dt) 29 | { 30 | if (Status == ServiceState.Starting) 31 | { 32 | // Create the logger 33 | Log.Logger = _loggerConfig.CreateLogger(); 34 | Status = ServiceState.Online; 35 | } 36 | 37 | return true; 38 | } 39 | 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /Source/Logging/TemplatedMessage.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: TemplatedMessage.cs 3 | /// Date: July 11, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | namespace EppNet.Logging 11 | { 12 | 13 | /// 14 | /// Intended to be passed to an for templated message output 15 | /// 16 | public readonly struct TemplatedMessage : IEquatable 17 | { 18 | 19 | #region Operators 20 | public static bool operator ==(TemplatedMessage left, TemplatedMessage right) => left.Equals(right); 21 | public static bool operator !=(TemplatedMessage left, TemplatedMessage right) => !left.Equals(right); 22 | 23 | #endregion 24 | 25 | public readonly string Message; 26 | public readonly object[] Objects; 27 | 28 | public TemplatedMessage(string message, params object[] objects) 29 | { 30 | this.Message = message; 31 | this.Objects = objects; 32 | } 33 | 34 | public override bool Equals([NotNullWhen(true)] object obj) 35 | { 36 | if (obj is TemplatedMessage other) 37 | return other.Message.Equals(Message) && other.Objects.Equals(Objects); 38 | 39 | return false; 40 | } 41 | 42 | public bool Equals(TemplatedMessage other) => other.Message.Equals(Message) && other.Objects == Objects; 43 | 44 | public override int GetHashCode() 45 | { 46 | int hashCode = Message.GetHashCode(); 47 | hashCode ^= Objects.GetHashCode(); 48 | return hashCode; 49 | } 50 | 51 | public bool Empty() => string.IsNullOrEmpty(Message); 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Source/Messaging/ChannelAlreadyExistsException.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: ChannelAlreadyExistsException.cs 3 | /// Date: July 11, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Messaging 10 | { 11 | 12 | public class ChannelAlreadyExistsException : Exception 13 | { 14 | 15 | public readonly byte Id; 16 | 17 | public ChannelAlreadyExistsException(byte id, string message) : base(message) 18 | { 19 | this.Id = id; 20 | } 21 | 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /Source/Messaging/ChannelFlags.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: ChannelFlags.cs 3 | /// Date: July 11, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Messaging 10 | { 11 | 12 | [Flags] 13 | public enum ChannelFlags : byte 14 | { 15 | 16 | None = 0, 17 | 18 | /// 19 | /// Messages sent to this channel are processed immediately rather 20 | /// than waiting for the next simulation tick. 21 | /// 22 | ProcessImmediately = 1 << 0, 23 | 24 | /// 25 | /// Data sent over this channel is encrypted. 26 | /// 27 | Encrypted = 1 << 1 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Source/Messaging/IMessageHandler.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: IMessageHandler.cs 3 | /// Date: August 6, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Data.Datagrams; 8 | 9 | namespace EppNet.Messaging 10 | { 11 | 12 | public interface IMessageHandler 13 | { 14 | public void Handle(IDatagram message); 15 | } 16 | 17 | public interface IMessageHandler : IMessageHandler where T : IDatagram { } 18 | 19 | public interface IMesasgeHandler : IMessageHandler where T1 : IDatagram where T2 : IDatagram { } 20 | 21 | } -------------------------------------------------------------------------------- /Source/Messaging/MalformedMessageReceivedException.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: MalformedMessageReceivedException.cs 3 | /// Date: July 12, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Connections; 8 | 9 | using System; 10 | 11 | namespace EppNet.Source.Messaging 12 | { 13 | public sealed class MalformedMessageReceivedException : Exception 14 | { 15 | 16 | public const string MalformedMessageFormat = "Received malformed message from "; 17 | 18 | public static MalformedMessageReceivedException New(Connection connection, byte[] bytes) 19 | { 20 | string message = MalformedMessageFormat + connection; 21 | return new(connection, bytes, message); 22 | } 23 | 24 | public readonly Connection Connection; 25 | public readonly byte[] Bytes; 26 | 27 | private MalformedMessageReceivedException(Connection connection, byte[] bytes, string message) : base(message) 28 | { 29 | this.Connection = connection; 30 | this.Bytes = bytes; 31 | } 32 | 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Source/Messaging/MessageDirector.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: MessageDirector.cs 3 | /// Date: July 11, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Services; 8 | 9 | using System; 10 | 11 | namespace EppNet.Messaging 12 | { 13 | 14 | public class MessageDirector : Service 15 | { 16 | 17 | public MessageDirector(ServiceManager svcMgr) : base(svcMgr) 18 | { 19 | 20 | } 21 | 22 | public bool Subscribe(T subscriber) where T : IMessageHandler 23 | { 24 | Type[] messagesTypes; 25 | 26 | // We need to extract the datagram types from the message handler 27 | foreach (Type intType in subscriber.GetType().GetInterfaces()) 28 | { 29 | 30 | // Ensure interface derives from IMessageHandler 31 | if (!typeof(IMessageHandler).IsAssignableFrom(intType)) 32 | continue; 33 | 34 | if (intType.IsGenericType) 35 | // Handler has specific datagrams it's interested in 36 | messagesTypes = intType.GetGenericArguments(); 37 | else 38 | // Subscribe to all datagram types 39 | messagesTypes = Array.Empty(); 40 | } 41 | 42 | // TODO: Subscribe 43 | 44 | return true; 45 | } 46 | } 47 | 48 | 49 | } -------------------------------------------------------------------------------- /Source/Messaging/MessageSubscriber.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: MessageSubscriber.cs 3 | /// Date: November 26, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | using System; 7 | 8 | namespace EppNet.Messaging 9 | { 10 | 11 | internal class MessageSubscriber where T : class, IMessageHandler 12 | { 13 | public readonly T Subscriber; 14 | public readonly Subscription[] Subscriptions; 15 | public readonly bool SubscribedToAll; 16 | 17 | /// 18 | /// Denotes a subscriber to specified or all datagram types
19 | /// Passing null to subs indicates the passed subscriber is subscribed to all message types 20 | ///
21 | /// 22 | /// 23 | public MessageSubscriber(T subscriber, Subscription[] subs) 24 | { 25 | this.Subscriber = subscriber; 26 | this.Subscriptions = subs; 27 | this.SubscribedToAll = Subscriptions == null; 28 | } 29 | 30 | } 31 | 32 | internal struct Subscription 33 | { 34 | readonly Type DatagramType; 35 | readonly int Priority; 36 | public Subscription(Type dgType, int priority) 37 | { 38 | this.DatagramType = dgType; 39 | this.Priority = priority; 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Source/NetworkFlags.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkFlags.cs 3 | /// Date: September 14, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System.Collections.Generic; 10 | 11 | namespace EppNet 12 | { 13 | 14 | public static class NetworkFlags 15 | { 16 | 17 | private static readonly List _flagsList = new(); 18 | 19 | /// 20 | /// Method calls orchestrated by the server. 21 | /// 22 | public static readonly SlottableEnum None = SlottableEnum._Internal_CreateAndAddTo(_flagsList, "None", 1); 23 | 24 | /// 25 | /// Method must be called to generate the object. 26 | /// 27 | public static readonly SlottableEnum Required = SlottableEnum._Internal_CreateAndAddTo(_flagsList, "Required", 2); 28 | 29 | /// 30 | /// "If a tree falls in the forest and no one's there to hear it, does it make a sound?" 31 | /// The answer in this case is no. Only parties interested in the object at the time 32 | /// of the method call will be informed. Server is solely in charge of updates. 33 | /// 34 | public static readonly SlottableEnum Broadcast = SlottableEnum._Internal_CreateAndAddTo(_flagsList, "Broadcast", 3); 35 | 36 | /// 37 | /// Method can be sent by a client. 38 | /// 39 | public static readonly SlottableEnum ClientSend = SlottableEnum._Internal_CreateAndAddTo(_flagsList, "ClientSend", 4); 40 | 41 | /// 42 | /// Method can only be sent by the object's owner. 43 | /// 44 | public static readonly SlottableEnum OwnerSend = SlottableEnum._Internal_CreateAndAddTo(_flagsList, "OwnerSend", 4); 45 | 46 | /// 47 | /// Method call is stored so it can be propagated to parties 48 | /// that gain interest in the object after the update. 49 | /// 50 | public static readonly SlottableEnum Persistent = SlottableEnum._Internal_CreateAndAddTo(_flagsList, "Persistent", 5); 51 | 52 | /// 53 | /// Data passed to this method is recorded for interpolation and extrapolation between 54 | /// ticks. In addition, the current value is queried every tick to detect changes and 55 | /// propagate them. 56 | ///
Implies as the latest snapshot value is 57 | /// sent to new parties that gain interest. 58 | ///
59 | public static readonly SlottableEnum Snapshot = SlottableEnum._Internal_CreateAndAddTo(_flagsList, "Snapshot", 5); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /Source/Node/INodeDescendant.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: INodeDescendant.cs 3 | /// Date: July 10, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Node 8 | { 9 | 10 | /// 11 | /// Indicates something that descends from and is managed by a 12 | /// 13 | 14 | public interface INodeDescendant 15 | { 16 | 17 | /// 18 | /// The this object is concerned with 19 | /// 20 | 21 | public NetworkNode Node { get; } 22 | 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /Source/Node/NetworkNodeManager.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: NetworkNodeManager.cs 3 | /// Date: July 9, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Collections; 8 | using EppNet.Logging; 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Diagnostics.CodeAnalysis; 13 | 14 | namespace EppNet.Node 15 | { 16 | 17 | public static class NetworkNodeManager 18 | { 19 | 20 | internal static OrderedDictionary _nodes = new(); 21 | 22 | /// 23 | /// Returns if we aren't managing any nodes 24 | /// 25 | /// 26 | public static bool IsEmpty() => _nodes.Count == 0; 27 | 28 | /// 29 | /// Tries to register the specified .
30 | /// Node mustn't be null and cannot be registered already.

31 | /// NOTE: If the C++ ENet library hasn't been initialized already, it will
32 | /// initialize it with default callbacks. See 33 | ///
34 | /// The node to register 35 | /// Whether or not the node was registered 36 | internal static bool _Internal_TryRegisterNode([NotNull] NetworkNode node, out int index) 37 | { 38 | index = _nodes.Count; 39 | 40 | // Absolutely no duplicates 41 | if (node == null || _nodes.ContainsKey(node.UUID)) 42 | return false; 43 | 44 | bool tryInitEnet = IsEmpty(); 45 | _nodes.Add(node.UUID, node); 46 | 47 | if (tryInitEnet) 48 | { 49 | // Try to initialize ENet with no special callbacks 50 | if (EppNet.InitializeENet()) 51 | { 52 | node.Notify.Info(new TemplatedMessage("Initialized C++ ENet library ver-{version}!", ENet.Library.version)); 53 | 54 | // Let's ensure we unregister nodes when the process ends. 55 | AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => 56 | { 57 | Iterator> iterator = _nodes.Iterator(); 58 | 59 | while (iterator.HasNext()) 60 | { 61 | var pair = iterator.Next(); 62 | NetworkNode node = pair.Value; 63 | node.Dispose(true); 64 | } 65 | }; 66 | } 67 | } 68 | 69 | return true; 70 | } 71 | 72 | /// 73 | /// Tries to unregister the specified .
74 | /// Node mustn't be null and registered already.

75 | /// NOTE: If this is the last , the C++ ENet library
76 | /// will be deinitialized. 77 | ///
78 | /// The node to register 79 | /// Whether or not the node was unregistered 80 | 81 | internal static bool _Internal_TryUnregisterNode([NotNull] NetworkNode node, bool manageENet = true) 82 | { 83 | bool removed = node != null && _nodes.Remove(node.UUID); 84 | 85 | if (removed && manageENet) 86 | EppNet.DeinitializeENet(); 87 | 88 | return removed; 89 | } 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /Source/Objects/EnumObjectState.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: EnumObjectState.cs 3 | /// Date: September 27, 2023 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | namespace EppNet.Objects 8 | { 9 | 10 | public enum EnumObjectState 11 | { 12 | 13 | /// 14 | /// State is unknown or -- more likely -- doesn't exist. 15 | /// 16 | Unknown = 0, 17 | 18 | /// 19 | /// Object is awaiting the most up-to-date state so it can generate 20 | /// 21 | WaitingForState = 1, 22 | 23 | /// 24 | /// Object is currently generating 25 | /// 26 | Generating = 2, 27 | 28 | /// 29 | /// Object has been generated 30 | /// 31 | Generated = 3, 32 | 33 | /// 34 | /// Object is disabled and isn't sending new updates 35 | /// 36 | Disabled = 4, 37 | 38 | /// 39 | /// Object is pending delete 40 | /// 41 | PendingDelete = 5, 42 | 43 | /// 44 | /// Object existed previously but has since been deleted. 45 | /// 46 | Deleted = 6 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Source/Objects/IObjectService.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: IObjectService.cs 3 | /// Date: March 23, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Commands; 8 | 9 | namespace EppNet.Objects 10 | { 11 | 12 | public interface IObjectService 13 | { 14 | 15 | public EnumCommandResult TryCreateObject(out TObject @object, long networkId = -1) where TObject: INetworkObject_Impl; 16 | 17 | public EnumCommandResult TryDeleteObject(long networkId); 18 | 19 | /// 20 | /// Tries to fetch a Network Object () by its network id 21 | /// 22 | /// 23 | /// 24 | /// 25 | 26 | public EnumCommandResult TryGetObjectById(long networkId, out TObject @object) where TObject : INetworkObject_Impl; 27 | 28 | /// 29 | /// Tries to set the state of a Network Object (captured by network id) 30 | /// 31 | /// 32 | /// 33 | /// 34 | public EnumCommandResult TrySetObjectState(long networkId, EnumObjectState state); 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Source/Objects/MyTestObject.cs: -------------------------------------------------------------------------------- 1 | using EppNet.Attributes; 2 | using EppNet.Data; 3 | 4 | using System.Collections.Generic; 5 | using System.Drawing; 6 | 7 | namespace EppNet.Objects 8 | { 9 | 10 | [NetworkObject(Dist = Distribution.Shared)] 11 | public partial class MyBaseObject : INetworkObject 12 | { 13 | 14 | [NetworkMethod] 15 | public void Goodbye() { } 16 | 17 | [NetworkMethod] 18 | public void TupleMethod((bool, Distribution) b) { } 19 | 20 | [NetworkMethod] 21 | public void YetAnotherMethod(int b) { } 22 | 23 | [NetworkMethod] 24 | public void Hello(float[] b) { } 25 | 26 | [NetworkMethod] 27 | public void Test((bool, Distribution) t, List a, MyTestObject[] arr, Dictionary b) { } 28 | } 29 | 30 | [NetworkObject(Dist = Distribution.Server)] 31 | public partial class MyTestObject : MyBaseObject 32 | { 33 | public long ID { set; get; } 34 | 35 | [NetworkMethod] 36 | public void Hello(int a) 37 | { 38 | 39 | } 40 | 41 | [NetworkMethod] 42 | public void Method(Distribution a) { } 43 | 44 | [NetworkMethod] 45 | public void Greenbeans() { } 46 | } 47 | 48 | public static class LOL 49 | { 50 | [NetworkMethod] 51 | public static void Method() { } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Source/Objects/NetworkObjectDefinition.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: NetworkObjectDefinition.cs 3 | /// Date: March 23, 2025 4 | /// Authors: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System; 10 | using System.Security.Principal; 11 | 12 | namespace EppNet.Objects 13 | { 14 | public enum EnumNetworkMemberType 15 | { 16 | None = 0, 17 | Field = 1, 18 | Property = 2, 19 | Method = 3 20 | } 21 | 22 | public class NetworkMember : INameable 23 | { 24 | 25 | public string Name { private set; get; } 26 | 27 | public int Id { private set; get; } 28 | 29 | public EnumNetworkMemberType Type { get; } 30 | 31 | public INetworkArgs Arguments { get; } 32 | 33 | } 34 | 35 | public class NetworkObjectDefinition 36 | { 37 | 38 | 39 | 40 | 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Source/Objects/ObjectEvents.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ObjectEvents.cs 3 | /// Date: July 30, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Events; 8 | using EppNet.Node; 9 | 10 | namespace EppNet.Objects 11 | { 12 | 13 | public abstract class ObjectEvent : EventBase 14 | { 15 | 16 | public ObjectEvent(ObjectSlot slot, ObjectService service) : base(service.Node, slot, service) { } 17 | } 18 | 19 | public sealed class ObjectCreatedEvent : ObjectEvent 20 | { 21 | public ObjectCreatedEvent(ObjectSlot slot, ObjectService service) : base(slot, service) { } 22 | } 23 | 24 | public sealed class ObjectDeletedEvent : ObjectEvent 25 | { 26 | public ObjectDeletedEvent(ObjectSlot slot, ObjectService service) : base(slot, service) { } 27 | } 28 | 29 | public sealed class StateChangedEvent : EventBase<(EnumObjectState, EnumObjectState)> 30 | { 31 | public EnumObjectState State { get => Subject.Item1; } 32 | public EnumObjectState OldState { get => Subject.Item2; } 33 | 34 | public StateChangedEvent(INodeDescendant nDesc, EnumObjectState newState, EnumObjectState oldState, object sender) : 35 | base(nDesc.Node, (newState, oldState), sender) 36 | { } 37 | 38 | public StateChangedEvent(INodeDescendant nDesc, EnumObjectState newState, EnumObjectState oldState) : 39 | base(nDesc.Node, (newState, oldState), null) 40 | { } 41 | } 42 | 43 | public sealed class ParentChangedEvent : EventBase<(INetworkObject_Impl, INetworkObject_Impl)> 44 | { 45 | public INetworkObject_Impl Parent { get => Subject.Item1; } 46 | public INetworkObject_Impl OldParent { get => Subject.Item2; } 47 | 48 | public ParentChangedEvent(INodeDescendant nDesc, INetworkObject_Impl newParent, INetworkObject_Impl oldParent, object sender) : 49 | base(nDesc.Node, (newParent, oldParent), sender) 50 | { } 51 | } 52 | 53 | public sealed class ChildAddedEvent : EventBase 54 | { 55 | 56 | public INetworkObject_Impl Child { get => Subject; } 57 | 58 | public ChildAddedEvent(INodeDescendant nDesc, INetworkObject_Impl child, object sender) : 59 | base(nDesc.Node, child, sender) 60 | { } 61 | } 62 | 63 | public sealed class ChildRemovedEvent : EventBase 64 | { 65 | 66 | public INetworkObject_Impl Child { get => Subject; } 67 | 68 | public ChildRemovedEvent(INodeDescendant nDesc, INetworkObject_Impl child, object sender) : 69 | base(nDesc.Node, child, sender) 70 | { } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Source/Objects/ObjectMethodDefinition.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: ObjectMethodDefinition.cs 3 | /// Date: February 17, 2025 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | namespace EppNet.Objects 8 | { 9 | 10 | public class ObjectMethodDefinition where T : struct 11 | { 12 | 13 | public string Name { get; } 14 | 15 | public T Parameters { get; } 16 | 17 | public int NumParameters { get; } 18 | 19 | public ObjectMethodDefinition(string name, T parameters, int numParameters) 20 | { 21 | this.Name = name; 22 | this.Parameters = parameters; 23 | this.NumParameters = numParameters; 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Source/Objects/ObjectSlot.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ObjectSlot.cs 3 | /// Date: July 16, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Collections; 8 | using EppNet.Commands; 9 | 10 | using System; 11 | 12 | namespace EppNet.Objects 13 | { 14 | 15 | public struct ObjectSlot : IPageable, IEquatable, ICommandTarget, IDisposable 16 | { 17 | #region Operators 18 | 19 | /// 20 | /// Implicit operator that fetches the ID associated with this slot. 21 | /// 22 | /// 23 | 24 | public static implicit operator long(ObjectSlot objectSlot) => objectSlot.ID; 25 | 26 | /// 27 | /// Explicit operator that creates a new from a positive long
28 | ///
29 | /// ID must be positive 30 | /// 31 | 32 | public static explicit operator ObjectSlot(long id) 33 | { 34 | if (id < 0) 35 | throw new ArgumentOutOfRangeException(nameof(id), "ID must be positive!"); 36 | 37 | return new() 38 | { 39 | ID = id 40 | }; 41 | } 42 | 43 | public static bool operator ==(ObjectSlot left, ObjectSlot right) 44 | => left.Equals(right); 45 | public static bool operator !=(ObjectSlot left, ObjectSlot right) 46 | => !left.Equals(right); 47 | 48 | #endregion 49 | 50 | public long ID { set; get; } 51 | 52 | public IPage Page { set; get; } 53 | 54 | public bool Allocated { set; get; } 55 | 56 | /// 57 | /// The state associated with the in this slot. 58 | /// 59 | public readonly EnumObjectState State 60 | { 61 | get => Object != null ? Object.State.Value : EnumObjectState.Unknown; 62 | } 63 | 64 | /// 65 | /// A pointer to the i.e. controller for the user object. 66 | /// 67 | public INetworkObject_Impl Object; 68 | 69 | public ObjectSlot(IPage page, long id, INetworkObject_Impl @object) 70 | { 71 | this.Page = page; 72 | this.ID = id; 73 | this.Object = @object; 74 | this.Allocated = false; 75 | } 76 | 77 | public void Dispose() 78 | { 79 | if (Object is not null && Object is IDisposable disposable) 80 | disposable.Dispose(); 81 | 82 | Object = null; 83 | } 84 | 85 | /// 86 | /// Checks if the specified object is a with the same ID 87 | /// as this one. 88 | /// 89 | /// 90 | /// The specified object is an ObjectSlot that shares our ID 91 | 92 | public readonly override bool Equals(object obj) 93 | => obj is ObjectSlot slot && 94 | Equals(slot); 95 | 96 | /// 97 | /// Checks if the other is in the equivalent slot;
98 | /// has an equivalent ID. 99 | ///
100 | /// 101 | /// Whether or not the provided ObjectSlot has an equivalent ID 102 | public readonly bool Equals(ObjectSlot other) 103 | => other.Page == Page && 104 | other.ID == ID && 105 | other.Object == Object; 106 | 107 | /// 108 | /// Fetches the hash code associated with our ID. 109 | /// 110 | /// ID#GetHashCode() 111 | 112 | public readonly override int GetHashCode() 113 | => ID.GetHashCode() ^ 114 | Page.GetHashCode() ^ 115 | (Object != null ? Object.GetHashCode() : 1); 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /Source/Objects/UpdateQueue.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: UpdateQueue.cs 3 | /// Date: September 26, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Utilities; 8 | 9 | using System.Collections.Concurrent; 10 | 11 | namespace EppNet.Objects 12 | { 13 | 14 | public class UpdateQueue : ConcurrentQueue 15 | { 16 | 17 | public readonly bool IsSnapshotQueue; 18 | 19 | public UpdateQueue(bool isSnapshotQueue = false) 20 | { 21 | this.IsSnapshotQueue = isSnapshotQueue; 22 | } 23 | 24 | public bool TryEnqueue(Update item) 25 | { 26 | if (item == null) 27 | return false; 28 | 29 | var netAttr = item.MemberDefinition.Attribute; 30 | bool snapshotUpdate = netAttr.Flags.IsFlagSet(NetworkFlags.Snapshot); 31 | 32 | // Ensure that the update is valid 33 | if ((snapshotUpdate && IsSnapshotQueue) || (!snapshotUpdate && !IsSnapshotQueue)) 34 | { 35 | Enqueue(item); 36 | return true; 37 | } 38 | 39 | return false; 40 | 41 | } 42 | 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Source/Processes/Events/PacketReceivedEvent.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: PacketReceivedEvent.cs 3 | /// Date: January 13, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Connections; 8 | using EppNet.Data; 9 | using EppNet.Data.Datagrams; 10 | using EppNet.Sockets; 11 | using EppNet.Time; 12 | 13 | using System; 14 | 15 | namespace EppNet.Processes.Events 16 | { 17 | 18 | public class PacketReceivedEvent : IBufferEvent, ITimestamped 19 | { 20 | 21 | 22 | public BaseSocket Socket { internal set; get; } 23 | public Connection Sender { internal set; get; } 24 | public byte[] Data { internal set; get; } 25 | public byte ChannelID { internal set; get; } 26 | 27 | public bool Disposed { internal set; get; } 28 | 29 | public Timestamp Timestamp { internal set; get; } 30 | 31 | public IDatagram Datagram { internal set; get; } 32 | public bool ShouldContinue { set; get; } 33 | 34 | public PacketReceivedEvent() 35 | { 36 | this.Sender = null; 37 | this.ChannelID = 0; 38 | this.Timestamp = Timestamp.Zero; 39 | } 40 | 41 | public void Initialize(Connection conn, byte[] data, byte channelID) 42 | { 43 | this.Sender = conn; 44 | this.Data = data; 45 | this.ChannelID = channelID; 46 | this.Timestamp = new(conn.Time()); 47 | } 48 | 49 | public void Initialize() 50 | { 51 | this.Disposed = false; 52 | } 53 | 54 | public void Cleanup() 55 | { 56 | this.Datagram = null; 57 | this.Sender = null; 58 | this.Data = null; 59 | this.ChannelID = 0; 60 | this.Timestamp = Timestamp.Zero; 61 | this.Disposed = true; 62 | } 63 | 64 | public bool IsDisposed() => Disposed; 65 | 66 | public void Dispose() => Cleanup(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Source/Processes/PacketDeserializer.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | /// Filename: PacketDeserializer.cs 3 | /// Date: August 6, 2024 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using ENet; 8 | 9 | using EppNet.Data.Datagrams; 10 | using EppNet.Logging; 11 | using EppNet.Processes.Events; 12 | using EppNet.Registers; 13 | using EppNet.Sockets; 14 | 15 | using System; 16 | using System.Diagnostics.CodeAnalysis; 17 | 18 | namespace EppNet.Processes 19 | { 20 | 21 | public class PacketDeserializer : IBufferEventHandler, ILoggable 22 | { 23 | 24 | public int DroppedPackets { private set; get; } 25 | public long DroppedBytes { private set; get; } 26 | 27 | public ILoggable Notify { get => this; } 28 | 29 | protected BaseSocket _socket; 30 | protected DatagramRegister _dgRegister; 31 | protected MultithreadedBuffer _buffer; 32 | 33 | public PacketDeserializer([NotNull] BaseSocket socket, int bufferSize) 34 | { 35 | this._socket = socket; 36 | 37 | MultithreadedBufferBuilder builder = new(socket.Node, bufferSize); 38 | this._buffer = builder.ThenUseHandlers(this).ThenUseHandlers(socket.ChannelService).Build(); 39 | 40 | this._dgRegister = DatagramRegister.Get(); 41 | this.DroppedBytes = 0; 42 | } 43 | 44 | public bool Handle(PacketReceivedEvent data) 45 | { 46 | byte[] bytes = data.Data; 47 | byte header = bytes[0]; 48 | 49 | IRegistration dgRegistration = _dgRegister.Get(header); 50 | 51 | if (dgRegistration == null) 52 | { 53 | Notify.Error(new TemplatedMessage("Received new datagram of unknown type with header {id}! Is it registered correctly?", header)); 54 | return false; 55 | } 56 | 57 | Datagram dg = dgRegistration.NewInstance() as Datagram; 58 | dg.Sender = data.Sender; 59 | dg.ReadFrom(data.Data); 60 | data.Datagram = dg; 61 | 62 | dg.Read(); 63 | return true; 64 | } 65 | 66 | public void Start() => _buffer.Start(); 67 | public void Cancel() => _buffer.Stop(); 68 | 69 | public void HandlePacket(Peer peer, Packet packet, byte channelID, int timeoutMs = 10) 70 | { 71 | byte[] data = new byte[packet.Length]; 72 | packet.CopyTo(data); 73 | 74 | Action setupAction = (PacketReceivedEvent @event) => SetupPacket(@event, peer, data, channelID); 75 | _buffer.CreateAndWrite(setupAction); 76 | } 77 | 78 | public void SetupPacket(PacketReceivedEvent @event, Peer peer, byte[] data, byte channelID) 79 | { 80 | if (_socket is ServerSocket srvSocket) 81 | @event.Initialize(srvSocket.ConnectionService.Get(peer.ID), data, channelID); 82 | else 83 | @event.Initialize(_socket.Companion, data, channelID); 84 | } 85 | 86 | public void DropPacket(byte[] data) 87 | { 88 | DroppedBytes += data.Length; 89 | DroppedPackets++; 90 | Notify.Warning("Dropped packet!"); 91 | } 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Source/Registers/DatagramRegister.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: DatagramRegister.cs 3 | /// Date: September 13, 2023 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using EppNet.Data.Datagrams; 8 | using EppNet.Utilities; 9 | 10 | using System; 11 | using System.Buffers.Binary; 12 | using System.Numerics; 13 | 14 | namespace EppNet.Registers 15 | { 16 | public sealed class DatagramRegister : Register 17 | { 18 | 19 | public static readonly DatagramRegister Instance = new(); 20 | public static DatagramRegister Get() => Instance; 21 | 22 | private DatagramRegister() 23 | { 24 | TryRegister(); 25 | TryRegister(); 26 | TryRegister(); 27 | } 28 | 29 | public override CompilationResult Compile() 30 | { 31 | CompilationResult result = base.Compile(); 32 | 33 | if (result.Successful) 34 | { 35 | Datagram.HeaderByteLength = (int)MathF.Ceiling(Registrations / 255f); 36 | 37 | byte check = (byte) (Registrations - 1); 38 | Datagram.AvailableHeaderBits = BitOperations.LeadingZeroCount(check); 39 | 40 | byte b = 0; 41 | 42 | for (int i = 0; i < Datagram.AvailableHeaderBits; i++) 43 | b = b.EnableBit(i); 44 | 45 | Datagram.MaxHeaderDataDecimalValue = b; 46 | } 47 | 48 | return result; 49 | } 50 | 51 | public override bool TryGetNew(out T instance) 52 | { 53 | instance = default; 54 | 55 | if (_type2Keys.TryGetValue(typeof(T), out byte key)) 56 | { 57 | IRegistration registration = _lookupTable[key]; 58 | instance = (T)registration.NewInstance(); 59 | instance.Index = key; 60 | return true; 61 | } 62 | 63 | return false; 64 | } 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Source/Registers/ICompilable.cs: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////// 2 | /// Filename: ICompilable.cs 3 | /// Date: September 17, 2023 4 | /// Author: Maverick Liberty 5 | ////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Registers 10 | { 11 | 12 | public struct CompilationResult 13 | { 14 | 15 | public bool Successful; 16 | public int NumCompiled; 17 | public Exception Error; 18 | 19 | public CompilationResult(bool success, int numCompiled, Exception error) 20 | { 21 | this.Successful = success; 22 | this.NumCompiled = numCompiled; 23 | this.Error = error; 24 | } 25 | 26 | } 27 | 28 | public interface ICompilable 29 | { 30 | 31 | public CompilationResult Compile(); 32 | public bool IsCompiled(); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Source/Registers/IRegistration.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: IRegistration.cs 3 | /// Date: September 13, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Registers 10 | { 11 | 12 | public interface IRegistration : ICompilable, IDisposable 13 | { 14 | public object NewInstance(params object[] args); 15 | 16 | public Type GetRegisteredType(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Source/Registers/ObjectRegister.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ObjectRegister.cs 3 | /// Date: September 17, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Attributes; 8 | using EppNet.Objects; 9 | using EppNet.Sim; 10 | using EppNet.Utilities; 11 | 12 | using Serilog; 13 | 14 | using System; 15 | using System.Linq; 16 | 17 | using static EppNet.Utilities.AttributeFetcher; 18 | 19 | namespace EppNet.Registers 20 | { 21 | 22 | public sealed class ObjectRegister : Register 23 | { 24 | 25 | public static readonly ObjectRegister Instance = new(); 26 | public static ObjectRegister Get() => Instance; 27 | private ObjectRegister() { } 28 | 29 | public override CompilationResult Compile() 30 | { 31 | 32 | if (_compiled) 33 | return new(); 34 | 35 | var list = AttributeFetcher.GetTypes(); 36 | AttributeWrapper[] wrappers = list.Values.ToArray(); 37 | int compiledCount = 0; 38 | 39 | try 40 | { 41 | for (int i = 0; i < wrappers.Length; i++) 42 | { 43 | AttributeWrapper wrapper = wrappers[i]; 44 | NetworkObjectAttribute attr = wrapper.Attribute as NetworkObjectAttribute; 45 | 46 | ObjectRegistration r = new ObjectRegistration(wrapper.Type, attr); 47 | Log.Verbose("[ObjectRegister#Compile()] Compiling {name}...", wrapper.Type.Name); 48 | r.Compile(); 49 | 50 | _Internal_TryRegister(i, r); 51 | compiledCount++; 52 | } 53 | 54 | _compiled = true; 55 | } 56 | catch (Exception ex) 57 | { 58 | return new(false, compiledCount, ex); 59 | } 60 | 61 | return new(_compiled, compiledCount, null); 62 | } 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Source/Services/IRunnable.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: IRunnable.cs 3 | /// Date: July 28, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Services 8 | { 9 | 10 | public interface IRunnable 11 | { 12 | 13 | public bool Started { get; } 14 | 15 | /// 16 | /// Tries to run this runnable 17 | /// 18 | /// Whether or not the start was successful 19 | bool Start(); 20 | 21 | /// 22 | /// Tries to tick this runnable
23 | /// Requires to be true 24 | ///
25 | /// 26 | /// 27 | bool Tick(float dt) 28 | { 29 | if (!Started) 30 | return false; 31 | 32 | return Started; 33 | } 34 | 35 | /// 36 | /// Tries to stop this runnable 37 | /// 38 | /// Whether or not the stop was successful 39 | bool Stop(); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /Source/Services/IService.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: IService.cs 3 | /// Date: July 9, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Services 10 | { 11 | 12 | public enum ServiceState 13 | { 14 | Offline = 0, 15 | Starting = 1, 16 | Online = 2, 17 | ShuttingDown = 3 18 | } 19 | 20 | /// 21 | /// Interface that every Service extends 22 | /// 23 | 24 | public interface IService : IRunnable 25 | { 26 | 27 | /// 28 | /// Fired when the of this service changes
29 | /// - See 30 | ///
31 | public event Action OnStateChanged; 32 | 33 | /// 34 | /// Fired when Tick is called 35 | /// 36 | public event Action OnTick; 37 | 38 | public ServiceState Status { get; } 39 | 40 | /// 41 | /// Marks the service as dirty and needing cleaned up 42 | /// 43 | public void MarkDirty(); 44 | 45 | /// 46 | /// Gets the current 47 | /// 48 | public ServiceState GetStatus(); 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Source/Services/ServiceStateChangedEvent.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ServiceStateChangedEvent.cs 3 | /// Date: July 9, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Services 10 | { 11 | 12 | public readonly struct ServiceStateChangedEvent : IEquatable 13 | { 14 | 15 | public readonly ServiceState State; 16 | public readonly ServiceState OldState; 17 | 18 | /// 19 | /// Stores a State's transformation 20 | /// 21 | 22 | public ServiceStateChangedEvent(ServiceState newState, ServiceState oldState) 23 | { 24 | this.State = newState; 25 | this.OldState = oldState; 26 | } 27 | 28 | public static bool operator ==(ServiceStateChangedEvent left, ServiceStateChangedEvent right) => left.Equals(right); 29 | public static bool operator !=(ServiceStateChangedEvent left, ServiceStateChangedEvent right) => !left.Equals(right); 30 | 31 | public bool Equals(ServiceStateChangedEvent other) => State == other.State && OldState == other.OldState; 32 | 33 | public override bool Equals(object obj) => obj is ServiceStateChangedEvent evt && Equals(evt); 34 | 35 | public override int GetHashCode() => State.GetHashCode(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Source/Settings/ConfigurationGroup.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ConfigurationGroup.cs 3 | /// Date: April 15, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text.Json; 10 | 11 | using EppNet.Logging; 12 | 13 | namespace EppNet.Settings 14 | { 15 | public class ConfigurationGroup : Writeable, ILoggable 16 | { 17 | public ILoggable Notify { get => this; } 18 | 19 | public bool IsRoot => string.IsNullOrEmpty(Key); 20 | 21 | public List Items { get; } 22 | 23 | public ConfigurationGroup(string key) : base(key) 24 | { 25 | this.WritesToFile = true; 26 | this.Items = new(); 27 | } 28 | 29 | public ConfigurationGroup(Configuration config, string key) : this(key) 30 | { 31 | this.Configuration = config; 32 | } 33 | 34 | public bool Add(Writeable item) 35 | { 36 | // To add an item to this group, the item must: 37 | // 1) Not be null 38 | // 2) Not have a parent 39 | // 3) Not be this group 40 | 41 | if (item == null || item.Parent != null || item == this) 42 | return false; 43 | 44 | if (Items.Contains(item)) 45 | return false; 46 | 47 | Items.Add(item); 48 | item.Configuration = this.Configuration; 49 | return true; 50 | } 51 | 52 | /// 53 | /// Removes the specified from this group 54 | /// 55 | /// 56 | /// 57 | 58 | public bool Remove(Writeable item) 59 | { 60 | bool removed = Items.Remove(item); 61 | item.Parent = null; 62 | 63 | if (Configuration != null) 64 | Configuration.Dirty = true; 65 | 66 | return removed; 67 | } 68 | 69 | /// 70 | /// Clears all writeables associated with this group 71 | /// 72 | 73 | public void Clear() 74 | { 75 | while (Items.Count > 0) 76 | Remove(Items[0]); 77 | } 78 | 79 | public override Writeable Clone() 80 | { 81 | ConfigurationGroup clone = new(Key) 82 | { 83 | WritesToFile = this.WritesToFile 84 | }; 85 | 86 | foreach (var item in Items) 87 | clone.Items.Add(item.Clone()); 88 | 89 | return clone; 90 | } 91 | 92 | internal override void Write(Utf8JsonWriter writer) 93 | { 94 | if (!WritesToFile) 95 | return; 96 | 97 | if (!IsRoot) 98 | writer.WriteStartObject(Key); 99 | 100 | foreach (var item in Items) 101 | { 102 | try 103 | { 104 | // The writeable itself will just return if it isn't 105 | // supposed to be written. 106 | item.Write(writer); 107 | } 108 | catch (Exception e) 109 | { 110 | // Something went wrong while trying to write this thing. 111 | // Don't break the entire configuration because of it. 112 | Notify.Fatal($"Failed to write Writeable \"{item.Key}\". Exception: \"{e.Message}\"."); 113 | } 114 | } 115 | 116 | if (!IsRoot) 117 | writer.WriteEndObject(); 118 | } 119 | 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Source/Settings/IConfigurationDescendant.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: IConfigurationDescendant.cs 3 | /// Date: September 30, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Settings 8 | { 9 | public interface IConfigurationDescendant 10 | { 11 | 12 | Configuration Configuration { get; } 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/Settings/ISetting.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ISetting.cs 3 | /// Date: April 14, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Settings 8 | { 9 | 10 | public interface ISetting 11 | { 12 | TValue Value { set; get; } 13 | 14 | bool IsAcceptable(TValue value); 15 | 16 | bool TryApply(); 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Source/Settings/SettingValueChangedEvent.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: SettingValueChangedEvent.cs 3 | /// Date: April 14, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Settings 8 | { 9 | public readonly struct SettingValueChangedEvent 10 | { 11 | 12 | public readonly ISetting Setting; 13 | public readonly TValue PreviousValue; 14 | public readonly TValue Value; 15 | 16 | public SettingValueChangedEvent(ISetting setting, TValue previousValue, TValue value) 17 | { 18 | this.Setting = setting; 19 | this.PreviousValue = previousValue; 20 | this.Value = value; 21 | } 22 | } 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Source/Settings/Writeable.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: Writeable.cs 3 | /// Date: April 15, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System.Text.Json; 8 | 9 | namespace EppNet.Settings 10 | { 11 | 12 | public abstract class Writeable : IConfigurationDescendant 13 | { 14 | 15 | public Configuration Configuration { internal set; get; } 16 | 17 | /// 18 | /// The name of this writeable 19 | /// 20 | 21 | public string Key { get; } 22 | 23 | /// 24 | /// If this setting should be written to the 25 | /// configuration file. 26 | /// 27 | /// Defaults to true 28 | 29 | public bool WritesToFile 30 | { 31 | set 32 | { 33 | if (value != _writesToFile) 34 | { 35 | 36 | _writesToFile = value; 37 | 38 | // Let's ensure our configuration is marked dirty 39 | if (Configuration != null) 40 | Configuration.Dirty = true; 41 | } 42 | } 43 | 44 | get => _writesToFile; 45 | } 46 | 47 | protected bool _writesToFile; 48 | public Writeable Parent { internal set; get; } 49 | 50 | protected Writeable(string key) 51 | { 52 | this.Key = key; 53 | this.Parent = null; 54 | this._writesToFile = true; 55 | } 56 | 57 | protected Writeable(string key, Writeable parent) : this(key) 58 | { 59 | this.Parent = parent; 60 | } 61 | 62 | internal abstract void Write(Utf8JsonWriter writer); 63 | 64 | public abstract Writeable Clone(); 65 | 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /Source/Snapshots/DesyncEvent.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: DesyncEvent.cs 3 | /// Date: September 4, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Snapshots 10 | { 11 | 12 | /// 13 | /// Stores information about a desynchronization event
14 | /// (when we stop receiving a consistent flow of snapshots to maintain the simulation) 15 | ///
16 | public class DesyncEvent 17 | { 18 | 19 | /// 20 | /// When the desynchronization event begin
21 | /// More like when we first realized we haven't received a snapshot 22 | /// in a while. 23 | ///
24 | public TimeSpan EventBegin { internal set; get; } 25 | 26 | public TimeSpan? EventEnd 27 | { 28 | internal set 29 | { 30 | if (value != null) 31 | { 32 | Duration = value.Value - EventBegin; 33 | _eventEnd = value.Value; 34 | } 35 | } 36 | 37 | get => _eventEnd; 38 | } 39 | 40 | /// 41 | /// How long this event lasted. This is 0 until the 42 | /// property is set. 43 | /// 44 | public TimeSpan Duration { private set; get; } 45 | 46 | /// 47 | /// We need a certain amount of snapshots before we're 48 | /// considered synchronized again. 49 | /// 50 | public int SnapshotsReceived { internal set; get; } 51 | 52 | protected TimeSpan? _eventEnd; 53 | 54 | public DesyncEvent(TimeSpan eventBegin) 55 | { 56 | this.EventBegin = eventBegin; 57 | this.Duration = TimeSpan.Zero; 58 | this.SnapshotsReceived = 0; 59 | this._eventEnd = null; 60 | } 61 | 62 | /// 63 | /// If the event hasn't ended yet, it's still happening. 64 | /// 65 | /// 66 | public bool IsActive() 67 | => !EventEnd.HasValue; 68 | 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /Source/Snapshots/ObjectSnapshot.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ObjectState.cs 3 | /// Date: September 24, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Commands; 8 | using EppNet.Logging; 9 | using EppNet.Objects; 10 | using EppNet.Utilities; 11 | 12 | using System; 13 | using System.Collections.Concurrent; 14 | using System.Collections.Generic; 15 | 16 | namespace EppNet.Snapshots 17 | { 18 | 19 | /// 20 | /// Represents a "snapshot" of a at a particular time in the simulation.
21 | /// Each state contains a unique, randomly generated header of characters
22 | /// Network properties and network methods with implemented getters (usually snapshot methods) are stored here 23 | ///
24 | 25 | public class ObjectSnapshot : Snapshot 26 | { 27 | 28 | public readonly ObjectAgent Object; 29 | 30 | protected ConcurrentQueue _commands; 31 | 32 | protected SortedList _method2Value; 33 | protected SortedList _prop2Value; 34 | 35 | public ObjectSnapshot(ObjectAgent @object, Snapshot parent) : base(parent) 36 | { 37 | Guard.AgainstNull(@object); 38 | this.Object = @object; 39 | 40 | this._method2Value = new(ObjectRegistration.StringSortComparer); 41 | this._prop2Value = new(ObjectRegistration.StringSortComparer); 42 | } 43 | 44 | /// 45 | /// Captures the current value of each network property and snapshot network method 46 | /// 47 | 48 | public override void RecordCurrent() 49 | { 50 | 51 | SortedList list = Object.Metadata._methods; 52 | int index = 0; 53 | 54 | while (index < list.Count) 55 | { 56 | string key = list.GetKeyAtIndex(index); 57 | ObjectMemberDefinition memDef = list[key]; 58 | 59 | if (memDef.IsProperty() || (memDef.IsMethod() && memDef.Attribute.Flags.IsFlagSet(NetworkFlags.Snapshot))) 60 | { 61 | 62 | object currentValue = memDef.InvokeGetter(Object); 63 | 64 | if (memDef.IsProperty()) 65 | _prop2Value.Add(key, currentValue); 66 | else 67 | _method2Value.Add(key, currentValue); 68 | } 69 | 70 | // After we're done looking at our method values, let's look at our properties 71 | if (++index == list.Count && ReferenceEquals(list, Object.Metadata._methods)) 72 | { 73 | list = Object.Metadata._props; 74 | index = 0; 75 | } 76 | } 77 | } 78 | 79 | /// 80 | /// See
81 | /// This function is similar but null or undefined comparisons return 1. 82 | ///
83 | /// 84 | /// 85 | 86 | public override int CompareTo(object obj) 87 | { 88 | if (obj == null) 89 | return 1; 90 | 91 | if (obj is ObjectSnapshot x) 92 | return CompareTo(x); 93 | 94 | Object.Service.Notify.Error("Invalid comparison!", new ArgumentException("Invalid comparison!", nameof(obj))); 95 | return 1; 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /Source/Snapshots/SequenceNumber.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: SequenceNumber.cs 3 | /// Date: August 18, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | 9 | namespace EppNet.Snapshots 10 | { 11 | 12 | /// 13 | /// Sequence numbers are wraparound numbers
14 | /// Current range is 0 -> 127 15 | ///
16 | 17 | public readonly struct SequenceNumber : IComparable, IFormattable, IEquatable 18 | { 19 | 20 | public const sbyte MinValue = 0; 21 | public const sbyte QuartValue = sbyte.MaxValue / 4; 22 | public const sbyte MidValue = sbyte.MaxValue / 2; 23 | public const sbyte MaxValue = sbyte.MaxValue; 24 | 25 | public readonly sbyte Value; 26 | 27 | /// 28 | /// An out of range integer is clamped 29 | /// 30 | /// 31 | 32 | public SequenceNumber(int value) 33 | { 34 | // Clamp the values 35 | if (value > MaxValue) 36 | value = MaxValue; 37 | 38 | else if (value < MinValue) 39 | value = MinValue; 40 | 41 | this.Value = (sbyte) value; 42 | } 43 | 44 | public SequenceNumber(sbyte value) 45 | { 46 | this.Value = value; 47 | } 48 | 49 | public readonly int CompareTo(object obj) 50 | { 51 | if (obj is SequenceNumber other) 52 | return CompareTo(other); 53 | 54 | return 1; 55 | } 56 | 57 | public readonly int CompareTo(SequenceNumber other) 58 | { 59 | 60 | if (Value >= MidValue && other.Value < QuartValue) 61 | return -1; 62 | 63 | if (other.Value >= MidValue && Value < QuartValue) 64 | return 1; 65 | 66 | return Value.CompareTo(other.Value); 67 | } 68 | 69 | public readonly SequenceNumber Next() 70 | { 71 | if (Value + 1 > MaxValue) 72 | return new(); 73 | 74 | return new(Value + 1); 75 | } 76 | 77 | public readonly SequenceNumber Previous() 78 | { 79 | if (Value - 1 < MinValue) 80 | return new SequenceNumber(MaxValue); 81 | 82 | return new(Value - 1); 83 | } 84 | 85 | public readonly string ToString(string format, IFormatProvider formatProvider) => Value.ToString(format, formatProvider); 86 | 87 | public override readonly bool Equals(object obj) 88 | { 89 | if (obj is SequenceNumber other) 90 | return Equals(other); 91 | 92 | return false; 93 | } 94 | 95 | public readonly bool Equals(SequenceNumber other) => Value.Equals(other.Value); 96 | 97 | public override readonly int GetHashCode() => Value.GetHashCode(); 98 | 99 | #region Operators 100 | public static SequenceNumber operator ++(SequenceNumber a) => a.Next(); 101 | public static SequenceNumber operator --(SequenceNumber a) => a.Previous(); 102 | public static bool operator ==(SequenceNumber left, SequenceNumber right) => left.Equals(right); 103 | public static bool operator !=(SequenceNumber left, SequenceNumber right) => !left.Equals(right); 104 | public static bool operator <(SequenceNumber left, SequenceNumber right) => left.CompareTo(right) < 0; 105 | public static bool operator <=(SequenceNumber left, SequenceNumber right) => left.CompareTo(right) <= 0; 106 | public static bool operator >(SequenceNumber left, SequenceNumber right) => left.CompareTo(right) > 0; 107 | public static bool operator >=(SequenceNumber left, SequenceNumber right) => left.CompareTo(right) >= 0; 108 | 109 | #endregion 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /Source/Snapshots/Snapshot.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: SnapshotBase.cs 3 | /// Date: September 3, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | namespace EppNet.Snapshots 8 | { 9 | 10 | public class Snapshot : SnapshotBase 11 | { 12 | 13 | internal Snapshot(Snapshot parent) : base(parent.Node, parent.GlobalSequence, parent.LocalSequence) { } 14 | 15 | internal Snapshot(SnapshotService service, long globalSequenceNumber, SequenceNumber seqNumber) 16 | : base(service.Node, globalSequenceNumber, seqNumber) 17 | { 18 | 19 | } 20 | 21 | public override void RecordCurrent() 22 | { 23 | throw new System.NotImplementedException(); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Source/Snapshots/SnapshotServiceBase.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: SnapshotServiceBase.cs 3 | /// Date: September 4, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Connections; 8 | using EppNet.Services; 9 | 10 | namespace EppNet.Snapshots 11 | { 12 | 13 | /// 14 | /// A lot of the logic for the snapshot service can be 15 | /// shared between the client and the server nodes. This reduces 16 | /// code copying. 17 | /// 18 | public abstract class SnapshotServiceBase : Service 19 | { 20 | 21 | /// 22 | /// This number is multiplied by the 23 | /// to create the size of the buffer 24 | /// 25 | public const float DefaultBufferMultiplier = 1.5f; 26 | 27 | /// 28 | /// How many snapshots to take per second 29 | /// 30 | 31 | public int SnapshotsPerSecond { get; } 32 | 33 | /// 34 | /// While synchronized, this is the maximum amount of 35 | /// snapshots we can keep in the buffer at one time. 36 | /// 37 | public int SnapshotBufferSize { get; } 38 | 39 | /// 40 | /// How often we should be receiving snapshots 41 | /// 42 | public float SnapshotInterval { get; } 43 | 44 | protected SnapshotServiceBase(ServiceManager svcMgr, int snapshotsPerSecond) 45 | : base(svcMgr, sortOrder: -999) 46 | { 47 | this.SnapshotsPerSecond = snapshotsPerSecond; 48 | this.SnapshotInterval = 1f / SnapshotsPerSecond; 49 | } 50 | 51 | public void OnCheckForDesync(Connection connection) 52 | { 53 | 54 | } 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Source/Sockets/ClientSocket.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ClientSocket.cs 3 | /// Date: July 27, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using ENet; 8 | 9 | using EppNet.Connections; 10 | using EppNet.Node; 11 | 12 | using System; 13 | 14 | namespace EppNet.Sockets 15 | { 16 | 17 | public class ClientSocket : BaseSocket 18 | { 19 | 20 | public ClientSocket() : base(SocketType.Client) { } 21 | 22 | public ClientSocket(NetworkNode node) : base(node, SocketType.Client) { } 23 | 24 | public bool ConnectTo(string host, ushort port) 25 | { 26 | this.HostName = host; 27 | this.Port = port; 28 | return Create(); 29 | } 30 | 31 | public void ConnectTo(string host, int port) => ConnectTo(host, Convert.ToUInt16(port)); 32 | 33 | public override void OnPeerConnected(Peer peer) 34 | { 35 | this.Companion = new ServerConnection(this, peer); 36 | } 37 | 38 | public override void OnPeerDisconnected(Peer peer, uint disconnectReasonIdx) 39 | { 40 | this.Companion.Dispose(); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Source/Sockets/ServerSocket.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: Server.cs 3 | /// Date: September 5, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using ENet; 8 | 9 | using EppNet.Connections; 10 | using EppNet.Node; 11 | 12 | namespace EppNet.Sockets 13 | { 14 | 15 | public class ServerSocket : BaseSocket 16 | { 17 | 18 | public ConnectionService ConnectionService { private set; get; } 19 | 20 | public ServerSocket(NetworkNode node) : this(node, ConnectionService.ENet_MaxClients) { } 21 | 22 | public ServerSocket(NetworkNode node, int maxClients) : base(node, SocketType.Server) 23 | { 24 | this.MaxClients = maxClients; 25 | this.ConnectionService = null; 26 | } 27 | 28 | public override void OnPeerConnected(Peer peer) 29 | { 30 | if (ConnectionService != null) 31 | ConnectionService.HandleConnectionEstablished(peer); 32 | else 33 | Companion = new ClientConnection(this, peer); 34 | } 35 | 36 | public override void OnPeerDisconnected(Peer peer, uint disconnectReasonIdx) 37 | { 38 | ConnectionService.HandleConnectionLost(peer, DisconnectReasons.GetFromID(disconnectReasonIdx)); 39 | } 40 | 41 | protected override void _Internal_ValidateDependencies() 42 | { 43 | base._Internal_ValidateDependencies(); 44 | 45 | if (MaxClients > 1) 46 | ConnectionService = Node.Services.GetOrCreate(); 47 | } 48 | 49 | public override bool CanConnect(Peer peer) 50 | { 51 | bool canConnect = base.CanConnect(peer); 52 | 53 | if (ConnectionService != null) 54 | canConnect &= ConnectionService.CanConnect(peer); 55 | else 56 | canConnect &= Companion == null; 57 | 58 | return canConnect; 59 | } 60 | 61 | 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /Source/Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.VisualStudio.TestTools.UnitTesting; -------------------------------------------------------------------------------- /Source/Tests/RegisterTests.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: RegisterTests.cs 3 | /// Date: September 13, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | using EppNet.Data.Datagrams; 7 | using EppNet.Registers; 8 | 9 | namespace EppNet.Tests 10 | { 11 | 12 | [TestClass] 13 | public class RegisterTests 14 | { 15 | 16 | private DatagramRegister _reg; 17 | 18 | public RegisterTests() 19 | { 20 | this._reg = DatagramRegister.Get(); 21 | _reg.Compile(); 22 | } 23 | 24 | [TestMethod] 25 | public void TestPingDatagramGen() 26 | { 27 | bool fetched = _reg.TryGetNew(out PingDatagram _); 28 | Assert.IsTrue(fetched, "Did not instantiate new ping datagram properly!"); 29 | } 30 | 31 | [TestMethod] 32 | public void TestAttributes() 33 | { 34 | 35 | 36 | 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Source/Tests/ResolverTests.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ResolverTests.cs 3 | /// Date: September 17, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System.Numerics; 10 | 11 | namespace EppNet.Tests 12 | { 13 | 14 | [TestClass] 15 | public class ResolverTests 16 | { 17 | 18 | [TestMethod] 19 | public void TestVec3Resolver() 20 | { 21 | byte[] buffer; 22 | 23 | Vector3 l = new(3f, 0.1f, 4.0f); 24 | 25 | using (BytePayload payloadIn = new BytePayload()) 26 | { 27 | bool written = payloadIn.WriteVector3(l); 28 | 29 | Assert.IsTrue(written, $"Failed to resolve write for {l.GetType().Name}"); 30 | buffer = payloadIn.Pack(); 31 | } 32 | 33 | using (BytePayload payloadOut = new BytePayload(buffer)) 34 | { 35 | bool read = payloadOut.TryRead(out Vector3 result); 36 | Console.WriteLine(result); 37 | Assert.IsTrue(read && l.Equals(result), $"Failed to resolve read for {l.GetType().Name}"); 38 | } 39 | 40 | } 41 | 42 | [TestMethod] 43 | public void TestVec3DeltaResolver() 44 | { 45 | byte[] buffer; 46 | 47 | Vector3 l = new(3f, 0.1f, 4.0f); 48 | Vector3 b = new(2f, 0.1f, 3.0f); 49 | 50 | using (BytePayload payloadIn = new BytePayload()) 51 | { 52 | 53 | bool written = payloadIn.WriteVector3(l, b); 54 | 55 | Assert.IsTrue(written, $"Failed to resolve write for {l.GetType().Name}"); 56 | buffer = payloadIn.Pack(); 57 | } 58 | 59 | using (BytePayload payloadOut = new BytePayload(buffer)) 60 | { 61 | Vector3 result = payloadOut.ReadVector3(b); 62 | Console.WriteLine(result); 63 | Assert.IsTrue(l.Equals(result), $"Failed to resolve read for {l.GetType().Name}"); 64 | } 65 | } 66 | 67 | [TestMethod] 68 | public void TestVec3ZeroResolver() 69 | { 70 | byte[] buffer; 71 | 72 | Vector3 l = Vector3.Zero; 73 | 74 | using (BytePayload payloadIn = new BytePayload()) 75 | { 76 | bool written = payloadIn.TryWrite(l); 77 | 78 | Assert.IsTrue(written, $"Failed to resolve write for {l.GetType().Name}"); 79 | buffer = payloadIn.Pack(); 80 | } 81 | 82 | using (BytePayload payloadOut = new BytePayload(buffer)) 83 | { 84 | bool read = payloadOut.TryRead(out Vector3 result); 85 | 86 | Console.WriteLine($"Buffer size: {buffer.Length}"); 87 | Assert.IsTrue(read && l.Equals(result) && buffer.Length == 1, $"Failed to resolve read for {l.GetType().Name}"); 88 | } 89 | 90 | } 91 | 92 | 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Source/Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | EppNet.Tests.SocketTest 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ..\..\EppNet-Unity\UnityEngine.dll 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Source/Time/ITimestamped.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ITimestamped.cs 3 | /// Date: September 30, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Data; 8 | 9 | using System; 10 | 11 | namespace EppNet.Time 12 | { 13 | 14 | public interface ITimestamped 15 | { 16 | 17 | Timestamp Timestamp { get; } 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Source/Time/TimeExtensions.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: TimeExtensions.cs 3 | /// Date: July 30, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using EppNet.Node; 8 | 9 | using System; 10 | using System.Runtime.CompilerServices; 11 | 12 | namespace EppNet.Time 13 | { 14 | 15 | public static class TimeExtensions 16 | { 17 | 18 | /// 19 | /// Fetches the current monotonic time from the ENet library in milliseconds 20 | /// 21 | /// 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | public static TimeSpan MonoTime() 24 | => TimeSpan.FromMilliseconds(ENet.Library.Time); 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public static TimeSpan Time(this INodeDescendant obj) 28 | => obj.Node.Time; 29 | 30 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 31 | public static TimeSpan DetermineCurrentTime(params object[] objects) 32 | { 33 | 34 | foreach (var obj in objects) 35 | { 36 | if (obj is not null && obj is INodeDescendant nDesc) 37 | return nDesc.Time(); 38 | } 39 | 40 | return TimeSpan.FromMilliseconds(ENet.Library.Time); 41 | } 42 | 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /Source/Utilities/ActionExtensions.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ActionExtensions.cs 3 | /// Date: August 3, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Reflection.Metadata; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace EppNet.Utilities 13 | { 14 | 15 | public static class ActionExtensions 16 | { 17 | 18 | public static void GlobalInvoke(this Action action, T parameter) 19 | { 20 | SynchronizationContext context = SynchronizationContext.Current; 21 | 22 | if (context != null) 23 | // Call on the main thread synchronously 24 | context.Send(_ => action.Invoke(parameter), null); 25 | else 26 | Task.Factory.StartNew(() => action.Invoke(parameter)); 27 | } 28 | 29 | public static void GlobalInvoke(this Action action, T p1, T p2) 30 | { 31 | SynchronizationContext context = SynchronizationContext.Current; 32 | 33 | if (context != null) 34 | // Call on the main thread synchronously 35 | context.Send(_ => action.Invoke(p1, p2), null); 36 | else 37 | Task.Factory.StartNew(() => action.Invoke(p1, p2)); 38 | } 39 | 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /Source/Utilities/AttributeFetcher.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: AttributeFetcher.cs 3 | /// Date: September 19, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Reflection; 10 | 11 | namespace EppNet.Utilities 12 | { 13 | 14 | public static class AttributeFetcher 15 | { 16 | 17 | public class AttributeWrapper 18 | { 19 | public readonly Type Type; 20 | public readonly Attribute Attribute; 21 | 22 | public AttributeWrapper(Type type, Attribute attribute) 23 | { 24 | this.Type = type; 25 | this.Attribute = attribute; 26 | } 27 | } 28 | 29 | private static Dictionary> _dict = new Dictionary>(); 30 | private static Dictionary> _checkers = new Dictionary>(); 31 | private static bool _fetched = false; 32 | 33 | public static int Count => _dict.Count; 34 | 35 | public static bool AddType(Func action = null) where T : Attribute 36 | { 37 | if (_dict.ContainsKey(typeof(T))) 38 | return false; 39 | 40 | _dict.Add(typeof(T), new SortedList()); 41 | _checkers.Add(typeof(T), action); 42 | return true; 43 | } 44 | 45 | public static SortedList GetTypes() where T : Attribute 46 | { 47 | TryFetchAll(); 48 | _dict.TryGetValue(typeof(T), out var list); 49 | return list; 50 | } 51 | 52 | public static void TryFetchAll() 53 | { 54 | if (_fetched) 55 | return; 56 | 57 | foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) 58 | { 59 | foreach (Attribute attr in type.GetCustomAttributes(false)) 60 | { 61 | _dict.TryGetValue(attr.GetType(), out var list); 62 | 63 | // If there is no list that means we don't care 64 | // about this particular attribute 65 | if (list == null) 66 | continue; 67 | 68 | // Fetches the "checker" function 69 | _checkers.TryGetValue(attr.GetType(), out var f); 70 | 71 | // Call the "checker" function if it exists and 72 | // sees if the input passes. Otherwise, add by 73 | // default. 74 | if (f == null || (f != null && f.Invoke(type))) 75 | list.Add(type.Name, new AttributeWrapper(type, attr)); 76 | } 77 | } 78 | 79 | _fetched = true; 80 | } 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Source/Utilities/ByteExtensions.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: ByteExtensions.cs 3 | /// Date: September 25, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System; 8 | using System.Runtime.CompilerServices; 9 | 10 | namespace EppNet.Utilities 11 | { 12 | 13 | public static class ByteExtensions 14 | { 15 | 16 | /// 17 | /// Checks if the bit at the specified index is on. 18 | /// 19 | /// 20 | /// 21 | /// 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | public static bool IsBitOn(this byte b, int bitIndex) 24 | { 25 | if (-1 < bitIndex && bitIndex < 8) 26 | return (b & (1 << bitIndex)) != 0; 27 | 28 | _IndexWarning("IsBitOn", bitIndex); 29 | return false; 30 | } 31 | 32 | /// 33 | /// Sets the bit at the specified index to 1. 34 | /// 35 | /// 36 | /// 37 | /// The updated byte or the same byte if index was out of range. 38 | 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | public static byte EnableBit(this byte b, int bitIndex) 41 | { 42 | if (-1 < bitIndex && bitIndex < 8) 43 | b = (byte) (b | (1 << bitIndex)); 44 | else 45 | _IndexWarning("EnableBit", bitIndex); 46 | 47 | return b; 48 | } 49 | 50 | /// 51 | /// Sets the bit at the specified index to 0. 52 | /// 53 | /// 54 | /// 55 | /// 56 | 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | public static byte ResetBit(this byte b, int bitIndex) 59 | { 60 | if (-1 < bitIndex && bitIndex < 8) 61 | b = (byte)(b & ~(1 << bitIndex)); 62 | else 63 | _IndexWarning("ResetBit", bitIndex); 64 | 65 | return b; 66 | } 67 | 68 | /// 69 | /// Fetches the binary representation of a byte. 70 | /// 71 | /// 72 | /// 73 | 74 | public static string AsBinaryString(this byte b) => Convert.ToString(b, 2).PadLeft(8, '0'); 75 | 76 | private static void _IndexWarning(string methodName, int bitIndex) 77 | { 78 | Serilog.Log.Warning($"[byte#{methodName}()] was provided an invalid bit index of {bitIndex}. Range is 0-7."); 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Source/Utilities/FastMath.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: FastMath.cs 3 | /// Date: September 10, 2023 4 | /// Author: Maverick Liberty, Will Calderwood 5 | /// Link: Modified adaptation of 6 | /// https://stackoverflow.com/a/48448292 7 | /////////////////////////////////////////////////////// 8 | using System; 9 | using System.Numerics; 10 | 11 | namespace EppNet.Utilities 12 | { 13 | public static class FastMath 14 | { 15 | /// 16 | /// How many decimal places to cache 17 | /// 18 | public const int CachedDecimals = 15; 19 | 20 | private static readonly double[] _roundLookup = CreateRoundLookup(); 21 | 22 | /// 23 | /// Essentially a wrapper around with 10^index
24 | /// Indices from 0 to - 1 are cached. 25 | ///
26 | /// 27 | /// Math.Pow(10, index) 28 | public static double GetTenPow(int index) 29 | { 30 | if (0 <= index && index < CachedDecimals) 31 | return _roundLookup[index]; 32 | 33 | return Math.Pow(10, index); 34 | } 35 | 36 | private static double[] CreateRoundLookup() 37 | { 38 | double[] result = new double[CachedDecimals]; 39 | 40 | for (int i = 0; i < result.Length; i++) 41 | result[i] = Math.Pow(10, i); 42 | 43 | return result; 44 | } 45 | 46 | /// 47 | /// Rounds the specified number to the specified decimal places
48 | /// - Rounding to 0 decimal places returns 1 49 | /// - Rounding to <0 decimal places returns the provided number 50 | ///
51 | /// 52 | public static double Round(this float f, int decimals) => ((double)f).Round(decimals); 53 | 54 | 55 | /// 56 | /// Rounds the specified number to the specified decimal places
57 | /// - Rounding to 0 decimal places returns 1 58 | /// - Rounding to <0 decimal places returns the provided number 59 | ///
60 | /// 61 | 62 | public static double Round(this double d, int decimals) 63 | { 64 | double result; 65 | 66 | if (decimals == 0) 67 | { 68 | // Just return 1 as 10^0 is 1. 69 | result = 1d; 70 | } 71 | else if (decimals < 0) 72 | { 73 | // We don't support rounding to negative decimal places. 74 | result = d; 75 | } 76 | else 77 | { 78 | double adjustment = GetTenPow(decimals); 79 | result = Math.Floor((d * adjustment) + 0.5d) / adjustment; 80 | } 81 | 82 | return result; 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Source/Utilities/SortedListExtensions.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: SortedListExtensions.cs 3 | /// Date: September 2, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System.Collections.Generic; 8 | 9 | namespace EppNet.Utilities 10 | { 11 | 12 | public static class SortedListExtensions 13 | { 14 | 15 | public static TKey GetKeyAtIndex(this SortedList list, int index) 16 | { 17 | if (index < 0 || index >= list.Count) 18 | throw new System.ArgumentOutOfRangeException(nameof(index)); 19 | 20 | return list.Keys[index]; 21 | } 22 | 23 | public static TValue GetValueAtIndex(this SortedList list, int index) 24 | { 25 | if (index < 0 || index >= list.Count) 26 | throw new System.ArgumentOutOfRangeException(nameof(index)); 27 | 28 | return list.Values[index]; 29 | } 30 | 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /Source/Utilities/StringUtilities.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: StringUtilities.cs 3 | /// Date: September 29, 2024 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System.IO; 8 | 9 | namespace EppNet.Utilities 10 | { 11 | 12 | public static class StringUtilities 13 | { 14 | 15 | /// 16 | /// Platform agnostic way to get the index of the last path separator 17 | /// 18 | /// 19 | /// 20 | public static int GetLastPathSeparatorIndex(string path) 21 | { 22 | int sepIndex; 23 | int attempts = 0; 24 | char[] separators = { '/', Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; 25 | 26 | do 27 | { 28 | sepIndex = path.LastIndexOf(separators[attempts++]); 29 | } while (sepIndex == -1 && attempts < separators.Length); 30 | 31 | return sepIndex; 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Source/Zones/IZone.cs: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////// 2 | /// Filename: IZone.cs 3 | /// Date: September 17, 2023 4 | /// Author: Maverick Liberty 5 | /////////////////////////////////////////////////////// 6 | 7 | using System.Numerics; 8 | 9 | namespace EppNet.Zones 10 | { 11 | 12 | public interface IZone 13 | { 14 | 15 | public Vector2 GetCenter() => (GetMinBounds() + GetMaxBounds()) / 2f; 16 | 17 | public Vector2 GetCellSize() => Vector2.Zero; 18 | public Vector2 GetMaxBounds() => GetMinBounds() + GetCellSize(); 19 | public Vector2 GetMinBounds() => Vector2.Zero; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /packages/MSTest.TestAdapter.2.2.10/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestAdapter.2.2.10/.signature.p7s -------------------------------------------------------------------------------- /packages/MSTest.TestAdapter.2.2.10/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestAdapter.2.2.10/Icon.png -------------------------------------------------------------------------------- /packages/MSTest.TestAdapter.2.2.10/MSTest.TestAdapter.2.2.10.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestAdapter.2.2.10/MSTest.TestAdapter.2.2.10.nupkg -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/.signature.p7s -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/Icon.png -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/MSTest.TestFramework.2.2.10.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/MSTest.TestFramework.2.2.10.nupkg -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/lib/net45/Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/lib/net45/Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/lib/net45/Microsoft.VisualStudio.TestPlatform.TestFramework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/lib/net45/Microsoft.VisualStudio.TestPlatform.TestFramework.dll -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/lib/net5.0/Microsoft.VisualStudio.TestPlatform.TestFramework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/lib/net5.0/Microsoft.VisualStudio.TestPlatform.TestFramework.dll -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/lib/netstandard1.0/Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/lib/netstandard1.0/Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/lib/netstandard1.0/Microsoft.VisualStudio.TestPlatform.TestFramework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/lib/netstandard1.0/Microsoft.VisualStudio.TestPlatform.TestFramework.dll -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/lib/uap10.0/Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/lib/uap10.0/Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll -------------------------------------------------------------------------------- /packages/MSTest.TestFramework.2.2.10/lib/uap10.0/Microsoft.VisualStudio.TestPlatform.TestFramework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/MSTest.TestFramework.2.2.10/lib/uap10.0/Microsoft.VisualStudio.TestPlatform.TestFramework.dll -------------------------------------------------------------------------------- /packages/Microsoft.CodeCoverage.17.10.0/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.CodeCoverage.17.10.0/.signature.p7s -------------------------------------------------------------------------------- /packages/Microsoft.CodeCoverage.17.10.0/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.CodeCoverage.17.10.0/Icon.png -------------------------------------------------------------------------------- /packages/Microsoft.CodeCoverage.17.10.0/Microsoft.CodeCoverage.17.10.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.CodeCoverage.17.10.0/Microsoft.CodeCoverage.17.10.0.nupkg -------------------------------------------------------------------------------- /packages/Microsoft.CodeCoverage.17.10.0/ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | CodeCoverage 2 | 3 | THIRD-PARTY SOFTWARE NOTICES AND INFORMATION 4 | Do Not Translate or Localize 5 | 6 | This software incorporates components from the projects listed below. The original copyright notices 7 | and the licenses under which Microsoft received such components are set forth below and are provided for 8 | informational purposes only. Microsoft reserves all rights not expressly granted herein, whether by 9 | implication, estoppel or otherwise. 10 | 11 | 1. Mono.Cecil version 0.11.3 (https://github.com/jbevain/cecil) 12 | 13 | 14 | 15 | %% Mono.Cecil NOTICES AND INFORMATION BEGIN HERE 16 | ========================================= 17 | Copyright (c) 2008 - 2015 Jb Evain 18 | Copyright (c) 2008 - 2011 Novell, Inc. 19 | 20 | Permission is hereby granted, free of charge, to any person obtaining 21 | a copy of this software and associated documentation files (the 22 | "Software"), to deal in the Software without restriction, including 23 | without limitation the rights to use, copy, modify, merge, publish, 24 | distribute, sublicense, and/or sell copies of the Software, and to 25 | permit persons to whom the Software is furnished to do so, subject to 26 | the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be 29 | included in all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 34 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 35 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 36 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | ========================================= 39 | END OF Mono.Cecil NOTICES AND INFORMATION 40 | -------------------------------------------------------------------------------- /packages/Microsoft.CodeCoverage.17.10.0/lib/net462/Microsoft.VisualStudio.CodeCoverage.Shim.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.CodeCoverage.17.10.0/lib/net462/Microsoft.VisualStudio.CodeCoverage.Shim.dll -------------------------------------------------------------------------------- /packages/Microsoft.CodeCoverage.17.10.0/lib/netcoreapp3.1/Microsoft.VisualStudio.CodeCoverage.Shim.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.CodeCoverage.17.10.0/lib/netcoreapp3.1/Microsoft.VisualStudio.CodeCoverage.Shim.dll -------------------------------------------------------------------------------- /packages/Microsoft.NET.Test.Sdk.17.10.0/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.NET.Test.Sdk.17.10.0/.signature.p7s -------------------------------------------------------------------------------- /packages/Microsoft.NET.Test.Sdk.17.10.0/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.NET.Test.Sdk.17.10.0/Icon.png -------------------------------------------------------------------------------- /packages/Microsoft.NET.Test.Sdk.17.10.0/Microsoft.NET.Test.Sdk.17.10.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.NET.Test.Sdk.17.10.0/Microsoft.NET.Test.Sdk.17.10.0.nupkg -------------------------------------------------------------------------------- /packages/Microsoft.NET.Test.Sdk.17.10.0/buildMultiTargeting/Microsoft.NET.Test.Sdk.props: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | true 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/Microsoft.NET.Test.Sdk.17.10.0/lib/net462/_._: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.NET.Test.Sdk.17.10.0/lib/net462/_._ -------------------------------------------------------------------------------- /packages/Microsoft.NET.Test.Sdk.17.10.0/lib/netcoreapp3.1/_._: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EppNet-Networking/EppNet/13a4393491254500bec059b572cf30baf59dc63c/packages/Microsoft.NET.Test.Sdk.17.10.0/lib/netcoreapp3.1/_._ --------------------------------------------------------------------------------