Compare commits

...

47 commits

Author SHA1 Message Date
9286c9fe98
ci: test something
Signed-off-by: Timothy Schenk <admin@rainote.dev>
2025-01-16 14:34:17 +01:00
3a24dabdf2
chore: formatting and slnx
Signed-off-by: Timothy Schenk <admin@rainote.dev>
2025-01-16 14:30:40 +01:00
09f8dda990
chore: hexpats + MemCasting benchmarks 2024-09-19 05:09:25 +02:00
863332b806
chore: more information for skills 2024-09-18 17:31:42 +02:00
1f04c5d115
chore: more information for skills 2024-09-18 17:13:38 +02:00
2b85ba672a
chore: Powershell does not set the working dir properly 2024-09-18 16:40:23 +02:00
931d6c39a4
chore: more hexpats 2024-09-18 16:34:58 +02:00
d70041f064
chore: hexpat for baseitemdata 2024-09-18 10:42:21 +02:00
209cc3d8f5
docs: example usage scripts 2024-09-17 06:00:40 +02:00
66ca9c6fa9
fix(auth): otel export logging 2024-09-16 21:36:50 +02:00
50caf0464b
chore: OTEL service name 2024-09-16 21:19:09 +02:00
b0cb649dff
chore: dependency updates 2024-09-16 21:15:50 +02:00
7c1793e9a2
chore: pre-commit update 2024-09-16 21:13:03 +02:00
72026a8f99
fix(auth): exception initial character after login + otel 2024-09-16 21:11:40 +02:00
4e9e5f4730
fix(auth): exception initial character after login + otel 2024-09-16 21:10:52 +02:00
2aedc9dae9
refactor(wonderking): item data adjustments 2024-09-16 19:33:26 +02:00
4042654856
refactor(wonderking): item reader cleanup 2024-09-09 17:37:13 +02:00
a5cd7da487
chore: PacketMediator renaming 2024-04-04 16:57:42 +02:00
4c87d4f429
chore: ignore CA1810 2024-04-04 16:52:25 +02:00
d203c0bbe4
chore: add IDisposableAnalyzer (IDISP007) 2024-04-04 16:52:25 +02:00
f1f22ce1f2
fix: properly dispose ChannelSession 2024-04-04 16:52:24 +02:00
2ec47ae884
fix: seal and adjust async usage 2024-04-04 16:52:24 +02:00
7df83c9877
fix: autodispose argon2id 2024-04-04 16:52:24 +02:00
aff98c4144
chore: add IDisposableAnalyzer (IDISP007) 2024-04-04 16:52:22 +02:00
18345e775e
refactor: required fields 2024-04-04 16:52:18 +02:00
b78c7adf39
chore: minor adjustments to benchmarks 2024-04-04 16:52:17 +02:00
eb0b81cf59
chore: remove develop from basebranches 2024-04-04 16:52:17 +02:00
cc1a0acc30
chore: removal of PacketMediator 2024-04-04 16:52:16 +02:00
7ac4c8c0ef
chore: file header adjustment 2024-04-04 16:52:10 +02:00
6147a76d16
chore: adjustments to build scripts 2024-04-04 16:51:48 +02:00
2e70bf0772
chore: code quality adjustments 2024-04-04 16:51:48 +02:00
ca67b62f52
chore: ignore .run 2024-04-04 16:51:48 +02:00
60247ce42c
refactor: packet differentiation + packet id mapping 2024-04-04 16:51:48 +02:00
4982db6609
chore: otlp adjustments 2024-04-04 16:51:47 +02:00
efd261be77
chore: adjustments 2024-04-04 16:51:47 +02:00
63ddbd707f
chore: minor adjustments 2024-04-04 16:51:47 +02:00
0c2accd72c
feat: Parallel loops for initialization 2024-04-04 16:51:46 +02:00
0d0b27ba49
chore: cleanup dependencies 2024-04-04 16:51:46 +02:00
23aef12502
feat: cancellation token support 2024-04-04 16:51:46 +02:00
b9144a3ff0
chore: cleanup opentelemetry 2024-04-04 16:51:46 +02:00
bae1f812cf
chore: explicit generic Types 2024-04-04 16:51:45 +02:00
1668f0f73d
feat: switching to channels 2024-04-04 16:51:45 +02:00
3615059a96
chore: annotations 2024-04-04 16:51:45 +02:00
e2eb461fb3
chore: make everything proper async 2024-04-04 16:51:45 +02:00
c378ddba07
chore: cleanup of Masstransit library 2024-04-04 16:51:44 +02:00
e305ce49aa
chore: cleanup leftovers 2024-04-04 16:51:44 +02:00
e8b331059d
feat: extraction of generic Packet distribution service 2024-04-04 16:51:44 +02:00
115 changed files with 30445 additions and 1705 deletions

View file

@ -1,4 +1,4 @@
; EditorConfig to support per-solution formatting. ; EditorConfig to support per-solution formatting.
; Use the EditorConfig VS add-in to make this work. ; Use the EditorConfig VS add-in to make this work.
; http://editorconfig.org/ ; http://editorconfig.org/
; ;
@ -23,10 +23,13 @@ indent_style = space
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
file_header_template = Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
[*.(yaml|yml)]
indent_size = 2
[*.cs] [*.cs]
dotnet_diagnostic.MA0004.severity = none dotnet_diagnostic.MA0004.severity = none
file_header_template = Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License.
indent_size = 4 indent_size = 4
dotnet_sort_system_directives_first = true dotnet_sort_system_directives_first = true
dotnet_diagnostic.MA0007.severity = none dotnet_diagnostic.MA0007.severity = none
@ -50,7 +53,7 @@ csharp_style_var_elsewhere = true:suggestion
csharp_style_throw_expression = false:suggestion csharp_style_throw_expression = false:suggestion
# Newline settings # Newline settings
csharp_new_line_before_open_brace = all csharp_new_line_before_open_brace = none
csharp_new_line_before_else = true csharp_new_line_before_else = true
csharp_new_line_before_catch = true csharp_new_line_before_catch = true
csharp_new_line_before_finally = true csharp_new_line_before_finally = true

View file

@ -76,7 +76,7 @@ jobs:
build-docs-container: build-docs-container:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a
needs: [docs, preprocess] needs: [ docs, preprocess ]
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: https://github.com/actions/checkout@v3 uses: https://github.com/actions/checkout@v3
@ -120,7 +120,7 @@ jobs:
deploy-wiki: deploy-wiki:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a
needs: [build-docs-container, docs, preprocess] needs: [ build-docs-container, docs, preprocess ]
steps: steps:
- name: Deploy Image to CapRrover - name: Deploy Image to CapRrover
run: | run: |

View file

@ -188,8 +188,8 @@ jobs:
uses: https://github.com/actions/setup-dotnet@v3 uses: https://github.com/actions/setup-dotnet@v3
with: with:
dotnet-version: | dotnet-version: |
7.0 9.0
8.0 dotnet-quality: preview
- name: Install dependencies - name: Install dependencies
run: | run: |
dotnet restore dotnet restore

View file

@ -1,4 +1,4 @@
name: PR Workflow name: PR Workflow
run-name: ${{ gitea.actor }} PR related workflow run-name: ${{ gitea.actor }} PR related workflow
on: on:
pull_request: pull_request:
@ -64,7 +64,7 @@ jobs:
container-build: container-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a
needs: [ build, preprocess ] needs: [build, preprocess]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup dotnet - name: Setup dotnet
@ -102,7 +102,7 @@ jobs:
platforms: linux/amd64, linux/arm64 platforms: linux/amd64, linux/arm64
container-sbom-scan: container-sbom-scan:
needs: [ container-build, preprocess ] needs: [container-build, preprocess]
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a
steps: steps:
@ -143,7 +143,7 @@ jobs:
bomFilename: ${{ github.workspace }}/container-bom.json bomFilename: ${{ github.workspace }}/container-bom.json
generate-licences: generate-licences:
needs: [ build, preprocess ] needs: [build, preprocess]
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a container: catthehacker/ubuntu:act-latest@sha256:5f2ff408985b10de9da4a8ea735b7f07d4f98c61608180ebb8964deb37f7580a
steps: steps:
@ -152,8 +152,8 @@ jobs:
uses: https://github.com/actions/setup-dotnet@v3 uses: https://github.com/actions/setup-dotnet@v3
with: with:
dotnet-version: | dotnet-version: |
7.0 9.0
8.0 dotnet-quality: preview
- name: Install dependencies - name: Install dependencies
run: | run: |
dotnet restore dotnet restore

3
.gitignore vendored
View file

@ -4,7 +4,8 @@
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
licenses licenses
.run
BenchmarkDotNet.Artifacts
# User-specific files # User-specific files
*.rsuser *.rsuser
*.suo *.suo

View file

@ -5,15 +5,20 @@ repos:
- id: dotnet-format - id: dotnet-format
name: dotnet-format name: dotnet-format
language: system language: system
entry: dotnet format --include entry: dotnet format --include Continuity.slnx
types_or: [c#, vb] types_or: [c#, vb]
- repo: https://github.com/Mateusz-Grzelinski/actionlint-py - repo: https://github.com/Mateusz-Grzelinski/actionlint-py
rev: v1.6.26.11 rev: v1.7.6.22
hooks: hooks:
- id: actionlint - id: actionlint
additional_dependencies: [pyflakes>=3.0.1, shellcheck-py>=0.9.0.5] additional_dependencies: [pyflakes>=3.0.1, shellcheck-py>=0.9.0.5]
- repo: https://github.com/hadolint/hadolint - repo: https://github.com/hadolint/hadolint
rev: v2.12.0 rev: v2.13.1-beta
hooks: hooks:
- id: hadolint-docker - id: hadolint-docker
args: [--ignore, SC2086] args: [--ignore, SC2086]
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.14.0
hooks:
- id: pretty-format-yaml
args: [--autofix, --indent, '2']

View file

@ -1,16 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Server/Dockerfile" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="continuity" />
<option name="buildCliOptions" value="--platform linux/amd64" />
<option name="buildKitEnabled" value="true" />
<option name="buildOnly" value="true" />
<option name="contextFolderPath" value="." />
<option name="sourceFilePath" value="Server/Dockerfile" />
</settings>
</deployment>
<EXTENSION ID="com.jetbrains.rider.docker.debug" isFastModeEnabled="true" isSslEnabled="false" />
<method v="2" />
</configuration>
</component>

View file

@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="build images" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="C:\Users\Timot\Projects\Continuity\build-image.ps1" workingDirectory="C:\Users\Timot\Projects\Continuity" executablePath="C:\Users\Timot\scoop\apps\pwsh\current\pwsh.exe"> <configuration default="false" name="build images" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$\build-image.ps1" workingDirectory="$PROJECT_DIR$">
<envs /> <envs />
<method v="2" /> <method v="2" />
</configuration> </configuration>

View file

@ -4,12 +4,14 @@
<settings> <settings>
<option name="buildKitEnabledForCompose" value="true" /> <option name="buildKitEnabledForCompose" value="true" />
<option name="envFilePath" value="" /> <option name="envFilePath" value="" />
<option name="removeImagesOnComposeDown" value="LOCAL" />
<option name="sourceFilePath" value="docker-compose.yml" /> <option name="sourceFilePath" value="docker-compose.yml" />
<option name="upDetach" value="false" /> <option name="upDetach" value="false" />
<option name="upForceRecreate" value="true" />
<option name="upRemoveOrphans" value="true" /> <option name="upRemoveOrphans" value="true" />
</settings> </settings>
</deployment> </deployment>
<EXTENSION ID="com.jetbrains.rider.docker.debug" isFastModeEnabled="true" isSslEnabled="false" /> <EXTENSION ID="com.jetbrains.rider.docker.debug" isFastModeEnabled="false" isSslEnabled="false" />
<method v="2"> <method v="2">
<option name="Build" default="false" projectName="Wonderking" projectPath="$PROJECT_DIR$/Wonderking/Wonderking.csproj" /> <option name="Build" default="false" projectName="Wonderking" projectPath="$PROJECT_DIR$/Wonderking/Wonderking.csproj" />
<option name="Build" default="false" projectName="Server" projectPath="$PROJECT_DIR$/Server/Server.csproj" /> <option name="Build" default="false" projectName="Server" projectPath="$PROJECT_DIR$/Server/Server.csproj" />

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Security.Cryptography; using System.Security.Cryptography;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
@ -11,69 +11,59 @@ namespace Benchmarks;
[Orderer(SummaryOrderPolicy.FastestToSlowest)] [Orderer(SummaryOrderPolicy.FastestToSlowest)]
[Config(typeof(GenericConfig))] [Config(typeof(GenericConfig))]
public class Argon2Benchmarks public class Argon2Benchmarks {
{ private byte[] _additionalData = null!;
[Params(2, 4)] public int _iterations; [Params(2, 4)] public int _iterations;
[Params(16)] public int _length;
[Params(16, 32)] public int _memory; [Params(16, 32)] public int _memory;
[Params(1, 2)] public int _parallelism; [Params(1, 2)] public int _parallelism;
private byte[] _salt = null!;
private byte[] _additionalData = null!;
[Params(16)] public int _length;
private byte[] _password = null!; private byte[] _password = null!;
private byte[] _salt = null!;
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup() {
{
_salt = RandomNumberGenerator.GetBytes(16); _salt = RandomNumberGenerator.GetBytes(16);
_additionalData = RandomNumberGenerator.GetBytes(16); _additionalData = RandomNumberGenerator.GetBytes(16);
_password = RandomNumberGenerator.GetBytes(16); _password = RandomNumberGenerator.GetBytes(16);
} }
[Benchmark] [Benchmark]
public byte[] Argon2i() public byte[] Argon2i() {
{ return new Argon2i(_password) {
return new Argon2i(_password)
{
Iterations = _iterations, Iterations = _iterations,
MemorySize = 1024 * _memory, MemorySize = 1024 * _memory,
DegreeOfParallelism = _parallelism, DegreeOfParallelism = _parallelism,
AssociatedData = _additionalData, AssociatedData = _additionalData,
Salt = _salt, Salt = _salt
}.GetBytes(_length); }.GetBytes(_length);
} }
[Benchmark] [Benchmark]
public byte[] Argon2d() public byte[] Argon2d() {
{ return new Argon2d(_password) {
return new Argon2d(_password)
{
Iterations = _iterations, Iterations = _iterations,
MemorySize = 1024 * _memory, MemorySize = 1024 * _memory,
DegreeOfParallelism = _parallelism, DegreeOfParallelism = _parallelism,
AssociatedData = _additionalData, AssociatedData = _additionalData,
Salt = _salt, Salt = _salt
}.GetBytes(_length); }.GetBytes(_length);
} }
[Benchmark] [Benchmark]
public byte[] Argon2id() public byte[] Argon2id() {
{ return new Argon2id(_password) {
return new Argon2id(_password)
{
Iterations = _iterations, Iterations = _iterations,
MemorySize = 1024 * _memory, MemorySize = 1024 * _memory,
DegreeOfParallelism = _parallelism, DegreeOfParallelism = _parallelism,
AssociatedData = _additionalData, AssociatedData = _additionalData,
Salt = _salt, Salt = _salt
}.GetBytes(_length); }.GetBytes(_length);
} }
[Benchmark] [Benchmark]
public byte[] IsopohArgon2DD() public byte[] IsopohArgon2DD() {
{ var config = new Argon2Config {
var config = new Argon2Config
{
Type = Argon2Type.DataDependentAddressing, Type = Argon2Type.DataDependentAddressing,
Version = Argon2Version.Nineteen, Version = Argon2Version.Nineteen,
MemoryCost = 1024 * _memory, MemoryCost = 1024 * _memory,
@ -82,17 +72,15 @@ public class Argon2Benchmarks
HashLength = _length, HashLength = _length,
Salt = _salt, Salt = _salt,
AssociatedData = _additionalData, AssociatedData = _additionalData,
Password = _password, Password = _password
}; };
var argon2 = new Argon2(config); var argon2 = new Argon2(config);
return argon2.Hash().Buffer; return argon2.Hash().Buffer;
} }
[Benchmark] [Benchmark]
public byte[] IsopohArgon2DI() public byte[] IsopohArgon2DI() {
{ var config = new Argon2Config {
var config = new Argon2Config
{
Type = Argon2Type.DataIndependentAddressing, Type = Argon2Type.DataIndependentAddressing,
Version = Argon2Version.Nineteen, Version = Argon2Version.Nineteen,
MemoryCost = 1024 * _memory, MemoryCost = 1024 * _memory,
@ -101,17 +89,15 @@ public class Argon2Benchmarks
HashLength = _length, HashLength = _length,
Salt = _salt, Salt = _salt,
AssociatedData = _additionalData, AssociatedData = _additionalData,
Password = _password, Password = _password
}; };
var argon2 = new Argon2(config); var argon2 = new Argon2(config);
return argon2.Hash().Buffer; return argon2.Hash().Buffer;
} }
[Benchmark] [Benchmark]
public byte[] IsopohArgon2Hybrid() public byte[] IsopohArgon2Hybrid() {
{ var config = new Argon2Config {
var config = new Argon2Config
{
Type = Argon2Type.HybridAddressing, Type = Argon2Type.HybridAddressing,
Version = Argon2Version.Nineteen, Version = Argon2Version.Nineteen,
MemoryCost = 1024 * _memory, MemoryCost = 1024 * _memory,
@ -120,7 +106,7 @@ public class Argon2Benchmarks
HashLength = _length, HashLength = _length,
Salt = _salt, Salt = _salt,
AssociatedData = _additionalData, AssociatedData = _additionalData,
Password = _password, Password = _password
}; };
var argon2 = new Argon2(config); var argon2 = new Argon2(config);
return argon2.Hash().Buffer; return argon2.Hash().Buffer;

View file

@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>12</LangVersion> <LangVersion>12</LangVersion>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -13,15 +13,15 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" /> <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<PackageReference Include="DotNext.Unsafe" Version="0.14.0" /> <PackageReference Include="DotNext.Unsafe" Version="0.14.0"/>
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" /> <PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0"/>
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.0" /> <PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.146"> <PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.9.28"> <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Security.Cryptography; using System.Security.Cryptography;
@ -9,29 +9,25 @@ namespace Benchmarks;
[Orderer(SummaryOrderPolicy.FastestToSlowest)] [Orderer(SummaryOrderPolicy.FastestToSlowest)]
[Config(typeof(GenericConfig))] [Config(typeof(GenericConfig))]
public class BinaryConversionBenchmarks public class BinaryConversionBenchmarks {
{
private byte[] _data = null!; private byte[] _data = null!;
private int _offset; private int _offset;
private int _writeBuffer; private int _writeBuffer;
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup() {
{
_data = RandomNumberGenerator.GetBytes(4000); _data = RandomNumberGenerator.GetBytes(4000);
_offset = RandomNumberGenerator.GetInt32(0, 3500); _offset = RandomNumberGenerator.GetInt32(0, 3500);
_writeBuffer = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue); _writeBuffer = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
} }
[Benchmark] [Benchmark]
public short BitConverterParseTest() public short BitConverterParseTest() {
{
return BitConverter.ToInt16(_data, _offset); return BitConverter.ToInt16(_data, _offset);
} }
[Benchmark] [Benchmark]
public short BinaryReader() public short BinaryReader() {
{
using var ms = new MemoryStream(_data); using var ms = new MemoryStream(_data);
using var reader = new BinaryReader(ms); using var reader = new BinaryReader(ms);
reader.BaseStream.Position = _offset; reader.BaseStream.Position = _offset;
@ -39,28 +35,24 @@ public class BinaryConversionBenchmarks
} }
[Benchmark] [Benchmark]
public short BinaryPrimitivesRead() public short BinaryPrimitivesRead() {
{
return BinaryPrimitives.ReadInt16LittleEndian( return BinaryPrimitives.ReadInt16LittleEndian(
new ArraySegment<byte>(_data, _offset, sizeof(short))); new ArraySegment<byte>(_data, _offset, sizeof(short)));
} }
[Benchmark] [Benchmark]
public void BinaryPrimitivesWrite() public void BinaryPrimitivesWrite() {
{
BinaryPrimitives.WriteInt32LittleEndian(_data.AsSpan(_offset, 4), BinaryPrimitives.WriteInt32LittleEndian(_data.AsSpan(_offset, 4),
_writeBuffer); _writeBuffer);
} }
[Benchmark] [Benchmark]
public void BitConverterCopy() public void BitConverterCopy() {
{
BitConverter.GetBytes(_writeBuffer).CopyTo(_data, _offset); BitConverter.GetBytes(_writeBuffer).CopyTo(_data, _offset);
} }
[Benchmark] [Benchmark]
public void BitConverterAssignment() public void BitConverterAssignment() {
{
var bytes = BitConverter.GetBytes(_writeBuffer); var bytes = BitConverter.GetBytes(_writeBuffer);
_data[_offset] = bytes[0]; _data[_offset] = bytes[0];
_data[_offset + 1] = bytes[1]; _data[_offset + 1] = bytes[1];

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Immutable; using System.Collections.Immutable;
@ -9,17 +9,15 @@ namespace Benchmarks;
[Config(typeof(GenericConfig))] [Config(typeof(GenericConfig))]
[Orderer(SummaryOrderPolicy.FastestToSlowest)] [Orderer(SummaryOrderPolicy.FastestToSlowest)]
public class DataCacheBenchmark public class DataCacheBenchmark {
{ private ConcurrentDictionary<int, int> _concurrentDictionary = null!;
private ConcurrentDictionary<int, int> _concurrentDictionary; private Dictionary<int, int> _dictionary = null!;
private Dictionary<int, int> _dictionary; private HashSet<int> _hashSet = null!;
private HashSet<int> _hashSet; private ImmutableHashSet<int> _immutableHashSet = null!;
private ImmutableHashSet<int> _immutableHashSet;
[Params(1000, 100000, 1000000)] public int N; [Params(1000, 100000, 1000000)] public int N;
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup() {
{
_hashSet = new HashSet<int>(); _hashSet = new HashSet<int>();
_dictionary = new Dictionary<int, int>(); _dictionary = new Dictionary<int, int>();
_concurrentDictionary = new ConcurrentDictionary<int, int>(); _concurrentDictionary = new ConcurrentDictionary<int, int>();
@ -32,8 +30,7 @@ public class DataCacheBenchmark
_hashSet.EnsureCapacity(N); _hashSet.EnsureCapacity(N);
_dictionary.EnsureCapacity(N); _dictionary.EnsureCapacity(N);
for (var i = 0; i < N; i++) for (var i = 0; i < N; i++) {
{
_immutableHashSet = _immutableHashSet.Add(i); _immutableHashSet = _immutableHashSet.Add(i);
_hashSet.Add(i); _hashSet.Add(i);
_dictionary.Add(i, i); _dictionary.Add(i, i);
@ -42,45 +39,38 @@ public class DataCacheBenchmark
} }
[Benchmark] [Benchmark]
public void HashSetAdd() public void HashSetAdd() {
{
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _hashSet.Add(i)); ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _hashSet.Add(i));
} }
[Benchmark] [Benchmark]
public void DictionaryAdd() public void DictionaryAdd() {
{
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _dictionary.Add(N + i, i)); ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _dictionary.Add(N + i, i));
} }
[Benchmark] [Benchmark]
public void ConcurrentDictionaryAddOrUpdate() public void ConcurrentDictionaryAddOrUpdate() {
{
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => ParallelEnumerable.Range(0, N).AsParallel().ForAll(i =>
_concurrentDictionary.AddOrUpdate(N + i, i, (key, oldValue) => oldValue + i)); _concurrentDictionary.AddOrUpdate(N + i, i, (_, oldValue) => oldValue + i));
} }
[Benchmark] [Benchmark]
public void ImmutableHashSetLookup() public void ImmutableHashSetLookup() {
{ ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => { _ = _immutableHashSet.Contains(i); });
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _immutableHashSet.Contains(i));
} }
[Benchmark] [Benchmark]
public void HashSetLookup() public void HashSetLookup() {
{ ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => { _ = _hashSet.Contains(i); });
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _hashSet.Contains(i));
} }
[Benchmark] [Benchmark]
public void DictionaryLookup() public void DictionaryLookup() {
{ ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => { _ = _dictionary.ContainsKey(i); });
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _dictionary.ContainsKey(i));
} }
[Benchmark] [Benchmark]
public void ConcurrentDictionaryLookup() public void ConcurrentDictionaryLookup() {
{ ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => { _ = _concurrentDictionary.ContainsKey(i); });
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _concurrentDictionary.ContainsKey(i));
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns; using BenchmarkDotNet.Columns;
@ -11,10 +11,8 @@ using BenchmarkDotNet.Jobs;
namespace Benchmarks; namespace Benchmarks;
public class GenericConfig : ManualConfig public class GenericConfig : ManualConfig {
{ public GenericConfig() {
public GenericConfig()
{
AddJob(Job.Default AddJob(Job.Default
.WithRuntime(CoreRuntime.Core80)) .WithRuntime(CoreRuntime.Core80))
.AddDiagnoser(ThreadingDiagnoser.Default, MemoryDiagnoser.Default, .AddDiagnoser(ThreadingDiagnoser.Default, MemoryDiagnoser.Default,

163
Benchmarks/MemCasting.cs Normal file
View file

@ -0,0 +1,163 @@
// Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Order;
namespace Benchmarks;
[Config(typeof(GenericConfig))]
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
public class MemCasting {
private byte[] _data = null!;
private byte[] _arrayData = null!;
[Params(1000, 100000, 1000000)] public int N;
[GlobalSetup]
public void Setup() {
_data = new byte[64];
_arrayData = new byte[N * 64];
Random.Shared.NextBytes(_data);
Random.Shared.NextBytes(_arrayData);
}
private static ElementalStats ReadElementalStatsMemoryMarshal(ref Span<byte> data) {
return MemoryMarshal.Cast<byte, ElementalStats>(data.Slice(0, 64))[0];
}
private static ElementalStats ReadElementalStatsMemoryMarshal2(ref Span<byte> data) {
return MemoryMarshal.Read<ElementalStats>(data.Slice(0, 64));
}
private static ElementalStats ReadElementalStatsNew(ref Span<byte> data) {
return new ElementalStats {
MinimumFireDamage = BitConverter.ToInt32(data.Slice(0, 4)), // 140 -> 144
MinimumWaterDamage = BitConverter.ToInt32(data.Slice(4, 4)), // 144 -> 148
MinimumDarkDamage = BitConverter.ToInt32(data.Slice(8, 4)), // 148 -> 152
MinimumHolyDamage = BitConverter.ToInt32(data.Slice(12, 4)), // 152 -> 156
MaximumFireDamage = BitConverter.ToInt32(data.Slice(16, 4)), // 156 -> 160
MaximumWaterDamage = BitConverter.ToInt32(data.Slice(20, 4)), // 160 -> 164
MaximumDarkDamage = BitConverter.ToInt32(data.Slice(24, 4)), // 164 -> 168
MaximumHolyDamage = BitConverter.ToInt32(data.Slice(28, 4)), // 168 -> 172
ElementFire = BitConverter.ToUInt32(data.Slice(32, 4)), // 172 -> 176
ElementWater = BitConverter.ToUInt32(data.Slice(36, 4)), // 176 -> 180
ElementDark = BitConverter.ToUInt32(data.Slice(40, 4)), // 180 -> 184
ElementHoly = BitConverter.ToUInt32(data.Slice(44, 4)), // 184 -> 188
FireResistance = BitConverter.ToInt32(data.Slice(48, 4)), // 188 -> 192
WaterResistance = BitConverter.ToInt32(data.Slice(52, 4)), // 192 -> 196
DarkResistance = BitConverter.ToInt32(data.Slice(56, 4)), // 196 -> 200
HolyResistance = BitConverter.ToInt32(data.Slice(60, 4)) // 200 -> 204
};
}
[Benchmark]
public Span<ElementalStats> MemoryMarshalCastArray() {
var data = _arrayData.AsSpan();
var elements = MemoryMarshal.Cast<byte, ElementalStats>(data);
return elements;
}
[Benchmark]
public Span<ElementalStats> MemoryMarshalReadArray() {
Span<ElementalStats> statsArr = stackalloc ElementalStats[N + 1];
var data = _data.AsSpan();
for (int i = 0; i <= N; i++) {
statsArr[i] = ReadElementalStatsMemoryMarshal2(ref data);
}
return statsArr.ToArray();
}
[Benchmark]
public Span<ElementalStats> ManualSpanSlicingArray() {
Span<ElementalStats> statsArr = stackalloc ElementalStats[N + 1];
var data = _data.AsSpan();
for (int i = 0; i <= N; i++) {
statsArr[i] = ReadElementalStatsNew(ref data);
}
return statsArr.ToArray();
}
[Benchmark]
public ElementalStats MemoryMarshalCastSingle() {
var data = _data.AsSpan();
return ReadElementalStatsMemoryMarshal(ref data);
}
[Benchmark]
public ElementalStats MemoryMarshalReadSingle() {
var data = _data.AsSpan();
return ReadElementalStatsMemoryMarshal2(ref data);
}
[Benchmark]
public ElementalStats ManualSpanSlicingSingle() {
var data = _data.AsSpan();
return ReadElementalStatsNew(ref data);
}
[StructLayout(LayoutKind.Explicit, Size = 64)]
public struct ElementalStats {
[FieldOffset(0)]
[MarshalAs(UnmanagedType.I4)]
public int MinimumFireDamage;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.I4)]
public int MinimumWaterDamage;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.I4)]
public int MinimumDarkDamage;
[FieldOffset(12)]
[MarshalAs(UnmanagedType.I4)]
public int MinimumHolyDamage;
[FieldOffset(16)]
[MarshalAs(UnmanagedType.I4)]
public int MaximumFireDamage;
[FieldOffset(20)]
[MarshalAs(UnmanagedType.I4)]
public int MaximumWaterDamage;
[FieldOffset(24)]
[MarshalAs(UnmanagedType.I4)]
public int MaximumDarkDamage;
[FieldOffset(28)]
[MarshalAs(UnmanagedType.I4)]
public int MaximumHolyDamage;
[FieldOffset(32)]
[MarshalAs(UnmanagedType.U4)]
public uint ElementFire;
[FieldOffset(36)]
[MarshalAs(UnmanagedType.U4)]
public uint ElementWater;
[FieldOffset(40)]
[MarshalAs(UnmanagedType.U4)]
public uint ElementDark;
[FieldOffset(44)]
[MarshalAs(UnmanagedType.U4)]
public uint ElementHoly;
[FieldOffset(48)]
[MarshalAs(UnmanagedType.I4)]
public int FireResistance;
[FieldOffset(52)]
[MarshalAs(UnmanagedType.I4)]
public int WaterResistance;
[FieldOffset(56)]
[MarshalAs(UnmanagedType.I4)]
public int DarkResistance;
[FieldOffset(60)]
[MarshalAs(UnmanagedType.I4)]
public int HolyResistance;
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Reflection; using System.Reflection;
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;

View file

@ -1,46 +1,37 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Net.Sockets; using System.Net.Sockets;
using System.Reflection;
using Continuity.AuthServer.Packets; using Continuity.AuthServer.Packets;
using MassTransit.Mediator; using MassTransit.Mediator;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using NetCoreServer; using NetCoreServer;
using RaiNote.PacketMediator;
using Wonderking.Packets; using Wonderking.Packets;
namespace Continuity.AuthServer; namespace Continuity.AuthServer;
public class AuthSession : TcpSession public class AuthSession : TcpSession {
{ private readonly PacketDistributorService<OperationCode, AuthSession> _distributorService;
private readonly ILogger<AuthSession> _logger; private readonly ILogger<AuthSession> _logger;
private readonly IMediator _mediator; private readonly IMediator _mediator;
public AuthSession(TcpServer public AuthSession(TcpServer
server, IMediator mediator, ILogger<AuthSession> logger) : base(server) server, IMediator mediator, ILogger<AuthSession> logger,
{ PacketDistributorService<OperationCode, AuthSession> distributorService) : base(server) {
_mediator = mediator; _mediator = mediator;
_logger = logger; _logger = logger;
_distributorService = distributorService;
} }
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
public override long Send(byte[] buffer) public override long Send(byte[] buffer) {
{
_logger.LogInformation("Data being sent is: {Data}", BitConverter.ToString(buffer)); _logger.LogInformation("Data being sent is: {Data}", BitConverter.ToString(buffer));
return base.Send(buffer); return base.Send(buffer);
} }
public Task SendAsync(IPacket packet) public Task SendAsync(IOutgoingPacket packet) {
{ var opcode = _distributorService.GetOperationCodeByPacketType(packet);
var type = packet.GetType();
_logger.LogInformation("Packet of type {Type} is being serialized", type.Name);
var packetIdAttribute = type.GetCustomAttribute<PacketIdAttribute>();
if (packetIdAttribute == null)
{
return Task.CompletedTask;
}
var opcode = packetIdAttribute.Code;
Span<byte> packetData = packet.Serialize(); Span<byte> packetData = packet.Serialize();
var length = (ushort)(packetData.Length + 8); var length = (ushort)(packetData.Length + 8);
@ -51,13 +42,11 @@ public class AuthSession : TcpSession
var bytesOfLength = BitConverter.GetBytes(length); var bytesOfLength = BitConverter.GetBytes(length);
var bytesOfOpcode = BitConverter.GetBytes((ushort)opcode); var bytesOfOpcode = BitConverter.GetBytes((ushort)opcode);
for (var i = 0; i < bytesOfLength.Length || i < 2; i++) for (var i = 0; i < bytesOfLength.Length || i < 2; i++) {
{
buffer[i] = bytesOfLength[i]; buffer[i] = bytesOfLength[i];
} }
for (var i = 0; i < bytesOfOpcode.Length || i < 2; i++) for (var i = 0; i < bytesOfOpcode.Length || i < 2; i++) {
{
buffer[2 + i] = bytesOfOpcode[i]; buffer[2 + i] = bytesOfOpcode[i];
} }
@ -68,8 +57,7 @@ public class AuthSession : TcpSession
return Task.CompletedTask; return Task.CompletedTask;
} }
protected override void OnReceived(byte[] buffer, long offset, long size) protected override void OnReceived(byte[] buffer, long offset, long size) {
{
_logger.LogDebug("Length: {Size} & offset: {Offset}", size, offset); _logger.LogDebug("Length: {Size} & offset: {Offset}", size, offset);
Span<byte> decryptedBuffer = stackalloc byte[(int)size]; Span<byte> decryptedBuffer = stackalloc byte[(int)size];
@ -98,18 +86,15 @@ public class AuthSession : TcpSession
base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length); base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length);
} }
private static Span<byte> Decrypt(Span<byte> buffer) private static Span<byte> Decrypt(Span<byte> buffer) {
{ for (var i = 0; i < buffer.Length; ++i) {
for (var i = 0; i < buffer.Length; ++i)
{
buffer[i] = (byte)(buffer[i] ^ i ^ (3 * (0xFE - i))); buffer[i] = (byte)(buffer[i] ^ i ^ (3 * (0xFE - i)));
} }
return buffer; return buffer;
} }
protected override void OnError(SocketError error) protected override void OnError(SocketError error) {
{
_logger.LogWarning("An error has occured: {Error}", error); _logger.LogWarning("An error has occured: {Error}", error);
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
@ -8,8 +8,7 @@ using NetCoreServer;
namespace Continuity.AuthServer; namespace Continuity.AuthServer;
public class ChannelSession : TcpSession public class ChannelSession : TcpSession {
{
private static readonly byte[] _key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7 } private static readonly byte[] _key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7 }
.Reverse().ToArray(); .Reverse().ToArray();
@ -23,12 +22,12 @@ public class ChannelSession : TcpSession
private readonly ICryptoTransform _encryptor; private readonly ICryptoTransform _encryptor;
private readonly ILogger<ChannelSession> _logger; private readonly ILogger<ChannelSession> _logger;
private readonly IMediator _mediator; private readonly IMediator _mediator;
private bool _disposed;
public ChannelSession(TcpServer server, IMediator mediator, ILogger<ChannelSession> logger) : base(server) public ChannelSession(TcpServer server, IMediator mediator, ILogger<ChannelSession> logger) : base(server) {
{
_mediator = mediator; _mediator = mediator;
_logger = logger; _logger = logger;
var aes = Aes.Create(); using var aes = Aes.Create();
aes.Key = _key; aes.Key = _key;
aes.IV = _iv; aes.IV = _iv;
aes.Padding = PaddingMode.None; aes.Padding = PaddingMode.None;
@ -40,33 +39,44 @@ public class ChannelSession : TcpSession
_encryptor = aes.CreateEncryptor(aes.Key, aes.IV); _encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
} }
protected override void OnReceived(byte[] buffer, long offset, long size) protected override void OnReceived(byte[] buffer, long offset, long size) {
{ try {
try
{
using (var ms = new MemoryStream(Decrypt(buffer))) using (var ms = new MemoryStream(Decrypt(buffer)))
using (var cs = new CryptoStream(ms, _decryptor, CryptoStreamMode.Read)) using (var cs = new CryptoStream(ms, _decryptor, CryptoStreamMode.Read)) {
{
var amountOfReadBytes = cs.Read(buffer); var amountOfReadBytes = cs.Read(buffer);
if (amountOfReadBytes != buffer.Length) if (amountOfReadBytes != buffer.Length) {
{
_logger.LogError("Amount of read bytes is not equal to buffer length"); _logger.LogError("Amount of read bytes is not equal to buffer length");
} }
} }
base.OnReceived(buffer, offset, size); base.OnReceived(buffer, offset, size);
} }
catch (CryptographicException ex) catch (CryptographicException ex) {
{
_logger.LogError("An error has occured while decrypting: {ErrorMessage}", ex.Message); _logger.LogError("An error has occured while decrypting: {ErrorMessage}", ex.Message);
_logger.LogError("Default buffer message: {Message}", Encoding.ASCII.GetString(buffer)); _logger.LogError("Default buffer message: {Message}", Encoding.ASCII.GetString(buffer));
} }
} }
private static byte[] Decrypt(byte[] buffer) protected override void Dispose(bool disposingManagedResources) {
{ if (_disposed) {
for (var i = 0; i < buffer.Length; ++i) return;
{ }
_disposed = true;
if (disposingManagedResources) {
_decryptor.Dispose();
_encryptor.Dispose();
}
base.Dispose(disposingManagedResources);
}
protected virtual void ThrowIfDisposed() {
ObjectDisposedException.ThrowIf(_disposed, GetType());
}
private static byte[] Decrypt(byte[] buffer) {
for (var i = 0; i < buffer.Length; ++i) {
buffer[i] = (byte)(buffer[i] ^ i ^ (3 * (0xFE - i))); buffer[i] = (byte)(buffer[i] ^ i ^ (3 * (0xFE - i)));
} }

View file

@ -1,23 +1,35 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Diagnostics;
using Continuity.AuthServer.Packets; using Continuity.AuthServer.Packets;
using Continuity.AuthServer.Services; using JetBrains.Annotations;
using MassTransit; using MassTransit;
using RaiNote.PacketMediator;
using Wonderking.Packets;
namespace Continuity.AuthServer.Consumers; namespace Continuity.AuthServer.Consumers;
public class PacketConsumer : IConsumer<RawPacket> [UsedImplicitly]
{ public sealed class PacketConsumer : IConsumer<RawPacket>, IDisposable {
private readonly PacketDistributorService _distributorService; private readonly ActivitySource _activitySource;
private readonly PacketDistributorService<OperationCode, AuthSession> _distributorService;
public PacketConsumer(PacketDistributorService distributorService) public PacketConsumer(PacketDistributorService<OperationCode, AuthSession> distributorService) {
{
_distributorService = distributorService; _distributorService = distributorService;
_activitySource = new ActivitySource(nameof(PacketConsumer));
} }
public Task Consume(ConsumeContext<RawPacket> context) public async Task Consume(ConsumeContext<RawPacket> context) {
{ using var activity = _activitySource?.StartActivity("PacketConsumption");
_distributorService.AddPacket(context.Message); activity?.SetTag("PacketId", context.Message.OperationCode.ToString());
return Task.CompletedTask; activity?.SetTag("SessionId", context.Message.Session.Id.ToString());
activity?.SetTag("PacketSize", context.Message.MessageBody.Length);
await _distributorService.AddPacketAsync(context.Message.MessageBody, context.Message.OperationCode,
context.Message.Session);
}
public void Dispose() {
_activitySource.Dispose();
} }
} }

View file

@ -18,7 +18,7 @@
<ProduceReferenceAssembly>True</ProduceReferenceAssembly> <ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<AnalysisLevel>latest-recommended</AnalysisLevel> <AnalysisLevel>latest-recommended</AnalysisLevel>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
@ -40,66 +40,68 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="DotNext" Version="5.3.1" /> <PackageReference Include="DotNext" Version="5.17.2" />
<PackageReference Include="DotNext.IO" Version="5.3.0" /> <PackageReference Include="DotNext.IO" Version="5.17.2" />
<PackageReference Include="DotNext.Metaprogramming" Version="5.3.0" /> <PackageReference Include="DotNext.Metaprogramming" Version="5.17.2" />
<PackageReference Include="DotNext.Threading" Version="5.3.0" /> <PackageReference Include="DotNext.Threading" Version="5.17.2" />
<PackageReference Include="DotNext.Unsafe" Version="5.3.0" /> <PackageReference Include="DotNext.Unsafe" Version="5.17.2" />
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0"/> <PackageReference Include="IDisposableAnalyzers" Version="4.0.8">
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.0"/>
<PackageReference Include="MassTransit" Version="8.1.3" />
<PackageReference Include="MassTransit.Analyzers" Version="8.1.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.146"> <PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="MassTransit" Version="8.3.4" />
<PackageReference Include="MassTransit.Analyzers" Version="8.3.4">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0"> <PackageReference Include="Meziantou.Analyzer" Version="2.0.186">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.3" /> <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0"/> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0"/> <PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0"/> <PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.1">
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"/> <PrivateAssets>all</PrivateAssets>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0"/> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0"/> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.9.28"> <PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.12.19">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="NetCoreServer" Version="8.0.7" /> <PackageReference Include="NetCoreServer" Version="8.0.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
<PackageReference Include="Npgsql.OpenTelemetry" Version="8.0.2" /> <PackageReference Include="Npgsql.OpenTelemetry" Version="9.0.2" />
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.15.6169"> <PackageReference Include="Nullable.Extended.Analyzer" Version="1.15.6495">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="OpenTelemetry" Version="1.7.0" /> <PackageReference Include="OpenTelemetry" Version="1.11.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.7.0" /> <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.11.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0" /> <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.11.0" />
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.7.0" /> <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.11.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.0.0-beta.12" />
<PackageReference Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.0.0-beta.10"/>
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.7.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="1.0.0-alpha.6" /> <PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="1.0.0-alpha.6" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.7.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.10.0" />
<PackageReference Include="OpenTelemetry.PersistentStorage.FileSystem" Version="1.0.0"/> <PackageReference Include="OpenTelemetry.PersistentStorage.FileSystem" Version="1.0.0" />
<PackageReference Include="OpenTelemetry.ResourceDetectors.Container" Version="1.0.0-beta.6"/> <PackageReference Include="OpenTelemetry.ResourceDetectors.Container" Version="1.0.0-beta.7" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0"/> <PackageReference Include="RaiNote.PacketMediator" Version="0.0.7" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/> <PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -116,6 +118,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Wonderking\Wonderking.csproj"/> <ProjectReference Include="..\Wonderking\Wonderking.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -8,10 +8,8 @@ namespace Continuity.AuthServer.DB.Documents;
[Index(nameof(Username), IsUnique = true)] [Index(nameof(Username), IsUnique = true)]
[Index(nameof(Id), IsUnique = true)] [Index(nameof(Id), IsUnique = true)]
public class Account public class Account {
{ public Account(string username, byte[] password, string email, byte permissionLevel, byte[] salt) {
public Account(string username, byte[] password, string email, byte permissionLevel, byte[] salt)
{
Username = username; Username = username;
Password = password; Password = password;
Email = email; Email = email;

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -10,8 +10,7 @@ namespace Continuity.AuthServer.DB.Documents;
[Index(nameof(Name), IsUnique = true)] [Index(nameof(Name), IsUnique = true)]
[Index(nameof(Id), IsUnique = true)] [Index(nameof(Id), IsUnique = true)]
public class Character public class Character {
{
[DeleteBehavior(DeleteBehavior.Cascade)] [DeleteBehavior(DeleteBehavior.Cascade)]
[Required] [Required]
public virtual Account Account { get; set; } public virtual Account Account { get; set; }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -8,8 +8,7 @@ namespace Continuity.AuthServer.DB.Documents;
[Index(nameof(Name), IsUnique = true)] [Index(nameof(Name), IsUnique = true)]
[Index(nameof(Id), IsUnique = true)] [Index(nameof(Id), IsUnique = true)]
public class Guild public class Guild {
{
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; } public Guid Id { get; set; }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -7,8 +7,7 @@ using Microsoft.EntityFrameworkCore;
namespace Continuity.AuthServer.DB.Documents; namespace Continuity.AuthServer.DB.Documents;
[Index(nameof(Id), IsUnique = true)] [Index(nameof(Id), IsUnique = true)]
public class GuildMember public class GuildMember {
{
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; } public Guid Id { get; set; }

View file

@ -1,9 +1,8 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
namespace Continuity.AuthServer.DB.Documents; namespace Continuity.AuthServer.DB.Documents;
public enum GuildRank : byte public enum GuildRank : byte {
{
Initiate = 0, Initiate = 0,
Member = 1, Member = 1,
Veteran = 2, Veteran = 2,

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -6,8 +6,7 @@ using Microsoft.EntityFrameworkCore;
namespace Continuity.AuthServer.DB.Documents; namespace Continuity.AuthServer.DB.Documents;
public class InventoryItem public class InventoryItem {
{
[DeleteBehavior(DeleteBehavior.Restrict)] [DeleteBehavior(DeleteBehavior.Restrict)]
[Required] [Required]
public virtual Character Character { get; set; } public virtual Character Character { get; set; }

View file

@ -1,9 +1,8 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
namespace Continuity.AuthServer.DB.Documents; namespace Continuity.AuthServer.DB.Documents;
public enum InventoryTab : byte public enum InventoryTab : byte {
{
WornEquipment = 0, WornEquipment = 0,
WornCashEquipment = 1, WornCashEquipment = 1,
Equipment = 2, Equipment = 2,

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
#nullable disable #nullable disable
@ -6,13 +6,11 @@ namespace Server.DB.Migrations;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class Initial : Migration public partial class Initial : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) => migrationBuilder.CreateTable( protected override void Up(MigrationBuilder migrationBuilder) => migrationBuilder.CreateTable(
name: "Accounts", name: "Accounts",
columns: table => new columns: table => new {
{
Id = table.Column<Guid>(type: "uuid", nullable: false), Id = table.Column<Guid>(type: "uuid", nullable: false),
Username = table.Column<string>(type: "varchar(20)", nullable: false), Username = table.Column<string>(type: "varchar(20)", nullable: false),
Password = table.Column<byte[]>(type: "bytea", nullable: false), Password = table.Column<byte[]>(type: "bytea", nullable: false),

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
#nullable disable #nullable disable
@ -6,11 +6,9 @@ namespace Server.DB.Migrations;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class CharacterDataDraft : Migration public partial class CharacterDataDraft : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.AlterColumn<string>( migrationBuilder.AlterColumn<string>(
name: "Username", name: "Username",
table: "Accounts", table: "Accounts",
@ -45,8 +43,7 @@ public partial class CharacterDataDraft : Migration
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Characters", name: "Characters",
columns: table => new columns: table => new {
{
Id = table.Column<Guid>(type: "uuid", nullable: false), Id = table.Column<Guid>(type: "uuid", nullable: false),
ServerId = table.Column<byte>(type: "smallint", nullable: false), ServerId = table.Column<byte>(type: "smallint", nullable: false),
AccountId = table.Column<Guid>(type: "uuid", nullable: false), AccountId = table.Column<Guid>(type: "uuid", nullable: false),
@ -59,8 +56,7 @@ public partial class CharacterDataDraft : Migration
Experience = table.Column<long>(type: "bigint", nullable: false), Experience = table.Column<long>(type: "bigint", nullable: false),
Level = table.Column<byte>(type: "smallint", nullable: false) Level = table.Column<byte>(type: "smallint", nullable: false)
}, },
constraints: table => constraints: table => {
{
table.PrimaryKey("PK_Characters", x => x.Id); table.PrimaryKey("PK_Characters", x => x.Id);
table.ForeignKey( table.ForeignKey(
name: "FK_Characters_Accounts_AccountId", name: "FK_Characters_Accounts_AccountId",
@ -77,8 +73,7 @@ public partial class CharacterDataDraft : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Characters"); name: "Characters");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,15 +7,12 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class AddInventoryToCharacter : Migration public partial class AddInventoryToCharacter : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "InventoryItem", name: "InventoryItem",
columns: table => new columns: table => new {
{
Id = table.Column<Guid>(type: "uuid", nullable: false), Id = table.Column<Guid>(type: "uuid", nullable: false),
CharacterId = table.Column<Guid>(type: "uuid", nullable: false), CharacterId = table.Column<Guid>(type: "uuid", nullable: false),
ItemId = table.Column<short>(type: "smallint", nullable: false), ItemId = table.Column<short>(type: "smallint", nullable: false),
@ -31,8 +28,7 @@ public partial class AddInventoryToCharacter : Migration
Option2 = table.Column<short>(type: "smallint", nullable: false), Option2 = table.Column<short>(type: "smallint", nullable: false),
Option3 = table.Column<short>(type: "smallint", nullable: false) Option3 = table.Column<short>(type: "smallint", nullable: false)
}, },
constraints: table => constraints: table => {
{
table.PrimaryKey("PK_InventoryItem", x => x.Id); table.PrimaryKey("PK_InventoryItem", x => x.Id);
table.ForeignKey( table.ForeignKey(
name: "FK_InventoryItem_Characters_CharacterId", name: "FK_InventoryItem_Characters_CharacterId",
@ -49,8 +45,7 @@ public partial class AddInventoryToCharacter : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "InventoryItem"); name: "InventoryItem");
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class AdditionalCharacterData : Migration public partial class AdditionalCharacterData : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.AlterColumn<int>( migrationBuilder.AlterColumn<int>(
name: "ItemId", name: "ItemId",
table: "InventoryItem", table: "InventoryItem",
@ -96,8 +94,7 @@ public partial class AdditionalCharacterData : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropColumn( migrationBuilder.DropColumn(
name: "BaseStats_Dexterity", name: "BaseStats_Dexterity",
table: "Characters"); table: "Characters");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class AddGuildData : Migration public partial class AddGuildData : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.AddColumn<Guid>( migrationBuilder.AddColumn<Guid>(
name: "GuildId", name: "GuildId",
table: "Characters", table: "Characters",
@ -21,28 +19,24 @@ public partial class AddGuildData : Migration
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Guild", name: "Guild",
columns: table => new columns: table => new {
{
Id = table.Column<Guid>(type: "uuid", nullable: false), Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "text", nullable: true), Name = table.Column<string>(type: "text", nullable: true),
Notice = table.Column<string>(type: "text", nullable: true) Notice = table.Column<string>(type: "text", nullable: true)
}, },
constraints: table => constraints: table => {
{
table.PrimaryKey("PK_Guild", x => x.Id); table.PrimaryKey("PK_Guild", x => x.Id);
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "GuildMember", name: "GuildMember",
columns: table => new columns: table => new {
{
Id = table.Column<Guid>(type: "uuid", nullable: false), Id = table.Column<Guid>(type: "uuid", nullable: false),
CharacterId = table.Column<Guid>(type: "uuid", nullable: false), CharacterId = table.Column<Guid>(type: "uuid", nullable: false),
GuildId = table.Column<Guid>(type: "uuid", nullable: false), GuildId = table.Column<Guid>(type: "uuid", nullable: false),
Rank = table.Column<byte>(type: "smallint", nullable: false) Rank = table.Column<byte>(type: "smallint", nullable: false)
}, },
constraints: table => constraints: table => {
{
table.PrimaryKey("PK_GuildMember", x => x.Id); table.PrimaryKey("PK_GuildMember", x => x.Id);
table.ForeignKey( table.ForeignKey(
name: "FK_GuildMember_Characters_CharacterId", name: "FK_GuildMember_Characters_CharacterId",
@ -95,8 +89,7 @@ public partial class AddGuildData : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Guild_GuildId", name: "FK_Characters_Guild_GuildId",
table: "Characters"); table: "Characters");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class GuildIsNotRequired : Migration public partial class GuildIsNotRequired : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.RenameColumn( migrationBuilder.RenameColumn(
name: "ItemType", name: "ItemType",
table: "InventoryItem", table: "InventoryItem",
@ -19,8 +17,7 @@ public partial class GuildIsNotRequired : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.RenameColumn( migrationBuilder.RenameColumn(
name: "InventoryTab", name: "InventoryTab",
table: "InventoryItem", table: "InventoryItem",

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class SwitchToDataAnnotations : Migration public partial class SwitchToDataAnnotations : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Accounts_AccountId", name: "FK_Characters_Accounts_AccountId",
table: "Characters"); table: "Characters");
@ -113,8 +111,7 @@ public partial class SwitchToDataAnnotations : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Accounts_AccountId", name: "FK_Characters_Accounts_AccountId",
table: "Characters"); table: "Characters");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class DBPoolingAndLazyLoadingSupport : Migration public partial class DBPoolingAndLazyLoadingSupport : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.AlterColumn<string>( migrationBuilder.AlterColumn<string>(
name: "Name", name: "Name",
table: "Characters", table: "Characters",
@ -36,8 +34,7 @@ public partial class DBPoolingAndLazyLoadingSupport : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropIndex( migrationBuilder.DropIndex(
name: "IX_Characters_Id", name: "IX_Characters_Id",
table: "Characters"); table: "Characters");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class FixInventoryItemAsDbset : Migration public partial class FixInventoryItemAsDbset : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Guild_GuildId", name: "FK_Characters_Guild_GuildId",
table: "Characters"); table: "Characters");
@ -86,8 +84,7 @@ public partial class FixInventoryItemAsDbset : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Guilds_GuildId", name: "FK_Characters_Guilds_GuildId",
table: "Characters"); table: "Characters");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class MissingCascadeDeletionOnCharacter : Migration public partial class MissingCascadeDeletionOnCharacter : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_InventoryItems_Characters_CharacterId", name: "FK_InventoryItems_Characters_CharacterId",
table: "InventoryItems"); table: "InventoryItems");
@ -26,8 +24,7 @@ public partial class MissingCascadeDeletionOnCharacter : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_InventoryItems_Characters_CharacterId", name: "FK_InventoryItems_Characters_CharacterId",
table: "InventoryItems"); table: "InventoryItems");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class IndexingAndVariousOtherAnnotations : Migration public partial class IndexingAndVariousOtherAnnotations : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Accounts_AccountId", name: "FK_Characters_Accounts_AccountId",
table: "Characters"); table: "Characters");
@ -112,8 +110,7 @@ public partial class IndexingAndVariousOtherAnnotations : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Accounts_AccountId", name: "FK_Characters_Accounts_AccountId",
table: "Characters"); table: "Characters");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class VariousDeletionBehaviours : Migration public partial class VariousDeletionBehaviours : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Characters_Accounts_AccountId", name: "FK_Characters_Accounts_AccountId",
table: "Characters"); table: "Characters");
@ -45,8 +43,7 @@ public partial class VariousDeletionBehaviours : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Accounts_GuildMember_GuildMemberId", name: "FK_Accounts_GuildMember_GuildMemberId",
table: "Accounts"); table: "Accounts");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -7,11 +7,9 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Server.DB.Migrations; namespace Server.DB.Migrations;
/// <inheritdoc /> /// <inheritdoc />
public partial class FixEntityRelationships : Migration public partial class FixEntityRelationships : Migration {
{
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropForeignKey( migrationBuilder.DropForeignKey(
name: "FK_Accounts_GuildMember_GuildMemberId", name: "FK_Accounts_GuildMember_GuildMemberId",
table: "Accounts"); table: "Accounts");
@ -88,8 +86,7 @@ public partial class FixEntityRelationships : Migration
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) {
{
migrationBuilder.DropIndex( migrationBuilder.DropIndex(
name: "IX_GuildMember_CharacterId", name: "IX_GuildMember_CharacterId",
table: "GuildMember"); table: "GuildMember");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Continuity.AuthServer.DB.Documents; using Continuity.AuthServer.DB.Documents;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -6,14 +6,21 @@ using Microsoft.EntityFrameworkCore;
namespace Continuity.AuthServer.DB; namespace Continuity.AuthServer.DB;
public class WonderkingContext : DbContext public class WonderkingContext : DbContext {
{ public WonderkingContext([NotNull] DbContextOptions options) : base(options) {
public WonderkingContext([NotNull] DbContextOptions options) : base(options)
{
} }
public DbSet<Account> Accounts { get; set; } public DbSet<Account> Accounts { get; set; }
public DbSet<Character> Characters { get; set; } public DbSet<Character> Characters { get; set; }
public DbSet<InventoryItem> InventoryItems { get; set; } public DbSet<InventoryItem> InventoryItems { get; set; }
public DbSet<Guild> Guilds { get; set; } public DbSet<Guild> Guilds { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Account>(entity => {
entity.HasIndex(e => e.Username).IsUnique();
entity.HasIndex(e => e.Id).IsUnique();
entity.Property(e => e.Id).HasColumnType("uuid");
});
}
} }

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Continuity.AuthServer.PacketHandlers; using Continuity.AuthServer.PacketHandlers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Continuity.AuthServer.LoggerMessages; namespace Continuity.AuthServer.LoggerMessages;
internal static partial class LoginHandlerLoggerMessages internal static partial class LoginHandlerLoggerMessages {
{
[LoggerMessage(EventId = 0, Level = LogLevel.Information, [LoggerMessage(EventId = 0, Level = LogLevel.Information,
Message = "Login data: Username {Username} & Password {Password}")] Message = "Login data: Username {Username} & Password {Password}")]
public static partial void LoginData(this ILogger<LoginHandler> logger, string username, string password); public static partial void LoginData(this ILogger<LoginHandler> logger, string username, string password);

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -6,8 +6,7 @@ using Wonderking.Packets;
namespace Continuity.AuthServer.LoggerMessages; namespace Continuity.AuthServer.LoggerMessages;
internal static partial class PacketLoggerMessages internal static partial class PacketLoggerMessages {
{
[LoggerMessage(EventId = 0, Level = LogLevel.Information, [LoggerMessage(EventId = 0, Level = LogLevel.Information,
Message = "Packet creation function created for {PacketID}")] Message = "Packet creation function created for {PacketID}")]
public static partial void PacketCreationFunctionCreated(this ILogger logger, OperationCode packetId); public static partial void PacketCreationFunctionCreated(this ILogger logger, OperationCode packetId);

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Continuity.AuthServer.DB.Documents; using Continuity.AuthServer.DB.Documents;
using Wonderking.Game.Data.Character; using Wonderking.Game.Data.Character;
@ -6,8 +6,7 @@ using Wonderking.Packets.Outgoing.Data;
namespace Continuity.AuthServer.PacketHandlers; namespace Continuity.AuthServer.PacketHandlers;
public partial class ChannelSelectionHandler public partial class ChannelSelectionHandler {
{
private sealed record InventoryItemProjection(ushort ItemId, byte Slot, InventoryTab InventoryTab); private sealed record InventoryItemProjection(ushort ItemId, byte Slot, InventoryTab InventoryTab);
private sealed record CharacterDataProjection( private sealed record CharacterDataProjection(

View file

@ -1,67 +1,56 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Continuity.AuthServer.DB; using Continuity.AuthServer.DB;
using Continuity.AuthServer.DB.Documents; using Continuity.AuthServer.DB.Documents;
using DotNext.Collections.Generic; using DotNext.Collections.Generic;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NetCoreServer; using RaiNote.PacketMediator;
using Wonderking.Packets.Incoming; using Wonderking.Packets.Incoming;
using Wonderking.Packets.Outgoing; using Wonderking.Packets.Outgoing;
using Wonderking.Packets.Outgoing.Data; using Wonderking.Packets.Outgoing.Data;
namespace Continuity.AuthServer.PacketHandlers; namespace Continuity.AuthServer.PacketHandlers;
public partial class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket> public partial class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket, AuthSession> {
{
private readonly WonderkingContext _wonderkingContext; private readonly WonderkingContext _wonderkingContext;
public ChannelSelectionHandler(WonderkingContext wonderkingContext) public ChannelSelectionHandler(WonderkingContext wonderkingContext) {
{
_wonderkingContext = wonderkingContext; _wonderkingContext = wonderkingContext;
} }
public async Task HandleAsync(ChannelSelectionPacket packet, TcpSession session) public async Task HandleAsync(ChannelSelectionPacket packet, AuthSession session,
{ CancellationToken cancellationToken) {
if (session is not AuthSession authSession)
{
return;
}
ChannelSelectionResponsePacket responsePacket; ChannelSelectionResponsePacket responsePacket;
var guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { GuildNames = Array.Empty<string>() }; var guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { GuildNames = Array.Empty<string>() };
var accountExists = var accountExists =
await _wonderkingContext.Accounts.AsNoTracking().AnyAsync(a => a.Id == authSession.AccountId); await _wonderkingContext.Accounts.AsNoTracking()
.AnyAsync(a => a.Id == session.AccountId, cancellationToken);
var amountOfCharacter = await _wonderkingContext.Characters.AsNoTracking().Include(c => c.Account) var amountOfCharacter = await _wonderkingContext.Characters.AsNoTracking().Include(c => c.Account)
.Where(c => c.Account.Id == authSession.AccountId).Take(3) .Where(c => c.Account.Id == session.AccountId).Take(3)
.CountAsync(); .CountAsync(cancellationToken);
if (!accountExists) if (!accountExists) {
{
return; return;
} }
if (amountOfCharacter > 0) if (amountOfCharacter > 0) {
{ responsePacket = new ChannelSelectionResponsePacket {
responsePacket = new ChannelSelectionResponsePacket
{
ChannelIsFullFlag = 0, ChannelIsFullFlag = 0,
Endpoint = "127.0.0.1", Endpoint = "127.0.0.1",
Port = 2000, Port = 2000,
Characters = await GetCharacterDataAsync(authSession.AccountId).ToArrayAsync() Characters = await GetCharacterDataAsync(session.AccountId).ToArrayAsync(token: cancellationToken)
}; };
guildNameResponsePacket.GuildNames = guildNameResponsePacket.GuildNames =
await _wonderkingContext.Characters.AsNoTracking().Include(c => c.Account).Include(c => c.GuildMember) await _wonderkingContext.Characters.AsNoTracking().Include(c => c.Account).Include(c => c.GuildMember)
.ThenInclude(gm => gm.Guild) .ThenInclude(gm => gm.Guild)
.Where(c => c.Account.Id == authSession.AccountId && c.GuildMember.Guild != null) .Where(c => c.Account.Id == session.AccountId && c.GuildMember.Guild != null)
.Select(c => c.GuildMember.Guild.Name).Take(3).ToArrayAsync(); .Select(c => c.GuildMember.Guild.Name).Take(3).ToArrayAsync(cancellationToken);
} }
else else {
{ responsePacket = new ChannelSelectionResponsePacket {
responsePacket = new ChannelSelectionResponsePacket
{
ChannelIsFullFlag = 0, ChannelIsFullFlag = 0,
Endpoint = "127.0.0.1", Endpoint = "127.0.0.1",
Port = 2000, Port = 2000,
@ -69,23 +58,19 @@ public partial class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPa
}; };
} }
await authSession.SendAsync(responsePacket); await session.SendAsync(responsePacket);
if (guildNameResponsePacket.GuildNames.Length > 0 && if (guildNameResponsePacket.GuildNames.Length > 0 &&
guildNameResponsePacket.GuildNames.Select(n => n != string.Empty).Any()) guildNameResponsePacket.GuildNames.Select(n => n != string.Empty).Any()) {
{ await session.SendAsync(guildNameResponsePacket);
await authSession.SendAsync(guildNameResponsePacket);
} }
} }
private static ushort[] GetItemIDsByInventoryTab(IEnumerable<InventoryItemProjection> items) private static ushort[] GetItemIDsByInventoryTab(IEnumerable<InventoryItemProjection> items) {
{
var ids = new ushort[20]; var ids = new ushort[20];
ids.AsSpan().Clear(); ids.AsSpan().Clear();
foreach (var item in items) foreach (var item in items) {
{ if (item.Slot > 20) {
if (item.Slot > 20)
{
continue; continue;
} }
@ -95,8 +80,7 @@ public partial class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPa
return ids; return ids;
} }
private async IAsyncEnumerable<CharacterData> GetCharacterDataAsync(Guid accountId) private async IAsyncEnumerable<CharacterData> GetCharacterDataAsync(Guid accountId) {
{
var characterDataProjections = _wonderkingContext.Characters.AsNoTracking().AsSplitQuery() var characterDataProjections = _wonderkingContext.Characters.AsNoTracking().AsSplitQuery()
.Include(c => c.InventoryItems).Include(c => c.Account) .Include(c => c.InventoryItems).Include(c => c.Account)
.Where(c => c.Account.Id == accountId) .Where(c => c.Account.Id == accountId)
@ -113,10 +97,8 @@ public partial class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPa
new InventoryItemProjection(i.ItemId, i.Slot, i.InventoryTab)) new InventoryItemProjection(i.ItemId, i.Slot, i.InventoryTab))
)).Take(3); )).Take(3);
await foreach (var c in characterDataProjections) await foreach (var c in characterDataProjections) {
{ yield return new CharacterData {
yield return new CharacterData
{
Name = c.Name, Name = c.Name,
Job = c.JobData, Job = c.JobData,
Gender = c.Gender, Gender = c.Gender,

View file

@ -1,10 +1,10 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Continuity.AuthServer.DB; using Continuity.AuthServer.DB;
using Continuity.AuthServer.DB.Documents; using Continuity.AuthServer.DB.Documents;
using Continuity.AuthServer.Services; using Continuity.AuthServer.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NetCoreServer; using RaiNote.PacketMediator;
using Wonderking.Game.Data.Character; using Wonderking.Game.Data.Character;
using Wonderking.Game.Mapping; using Wonderking.Game.Mapping;
using Wonderking.Packets.Incoming; using Wonderking.Packets.Incoming;
@ -13,31 +13,24 @@ using Wonderking.Packets.Outgoing.Data;
namespace Continuity.AuthServer.PacketHandlers; namespace Continuity.AuthServer.PacketHandlers;
public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket> public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket, AuthSession> {
{
private readonly CharacterStatsMappingConfiguration _characterStatsMapping; private readonly CharacterStatsMappingConfiguration _characterStatsMapping;
private readonly ItemObjectPoolService _itemObjectPoolService; private readonly ItemObjectPoolService _itemObjectPoolService;
private readonly WonderkingContext _wonderkingContext; private readonly WonderkingContext _wonderkingContext;
public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService, public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService,
CharacterStatsMappingConfiguration characterStatsMappingConfiguration) CharacterStatsMappingConfiguration characterStatsMappingConfiguration) {
{
_wonderkingContext = wonderkingContext; _wonderkingContext = wonderkingContext;
_itemObjectPoolService = itemObjectPoolService; _itemObjectPoolService = itemObjectPoolService;
_characterStatsMapping = characterStatsMappingConfiguration; _characterStatsMapping = characterStatsMappingConfiguration;
} }
public async Task HandleAsync(CharacterCreationPacket packet, TcpSession session) public async Task HandleAsync(CharacterCreationPacket packet, AuthSession session,
{ CancellationToken cancellationToken) {
if (session is not AuthSession authSession) var account =
{ await _wonderkingContext.Accounts.Include(a => a.Characters).FirstOrDefaultAsync(a => a.Id == session.AccountId, cancellationToken);
return;
}
var account = await _wonderkingContext.Accounts.FirstOrDefaultAsync(a => a.Id == authSession.AccountId); if (account is null) {
if (account is null)
{
return; return;
} }
@ -46,11 +39,10 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
var toBeAddedCharacter = CreateDefaultCharacter(packet, account, items, firstJobConfig); var toBeAddedCharacter = CreateDefaultCharacter(packet, account, items, firstJobConfig);
account.Characters.Add(toBeAddedCharacter); account.Characters.Add(toBeAddedCharacter);
await _wonderkingContext.SaveChangesAsync(); await _wonderkingContext.SaveChangesAsync(cancellationToken);
var character = var character =
new CharacterData new CharacterData {
{
Name = toBeAddedCharacter.Name, Name = toBeAddedCharacter.Name,
Job = toBeAddedCharacter.JobData, Job = toBeAddedCharacter.JobData,
Gender = toBeAddedCharacter.Gender, Gender = toBeAddedCharacter.Gender,
@ -68,16 +60,14 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
.Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).AsEnumerable()) .Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).AsEnumerable())
}; };
await authSession.SendAsync(new CharacterCreationResponsePacket await session.SendAsync(new CharacterCreationResponsePacket {
{
Character = character, Character = character,
Slot = packet.Slot, Slot = packet.Slot,
isDuplicate = false isDuplicate = false
}); });
} }
private InventoryItem[] CreateDefaultItems(CharacterCreationPacket packet, JobSpecificMapping firstJobConfig) private InventoryItem[] CreateDefaultItems(CharacterCreationPacket packet, JobSpecificMapping firstJobConfig) {
{
var mappedDefaultItems = _characterStatsMapping.DefaultCharacterMapping.Items var mappedDefaultItems = _characterStatsMapping.DefaultCharacterMapping.Items
.Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray(); .Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray();
@ -94,10 +84,8 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
} }
private static Character CreateDefaultCharacter(CharacterCreationPacket packet, Account account, private static Character CreateDefaultCharacter(CharacterCreationPacket packet, Account account,
InventoryItem[] items, JobSpecificMapping firstJobConfig) InventoryItem[] items, JobSpecificMapping firstJobConfig) {
{ return new Character {
return new Character()
{
Account = account, Account = account,
MapId = 300, MapId = 300,
Name = packet.Name, Name = packet.Name,
@ -115,10 +103,8 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
}; };
} }
private JobSpecificMapping SelectFirstJobConfig(byte firstJob) private JobSpecificMapping SelectFirstJobConfig(byte firstJob) {
{ return firstJob switch {
return firstJob switch
{
1 => _characterStatsMapping.Swordsman, 1 => _characterStatsMapping.Swordsman,
2 => _characterStatsMapping.Mage, 2 => _characterStatsMapping.Mage,
3 => _characterStatsMapping.Thief, 3 => _characterStatsMapping.Thief,
@ -127,8 +113,7 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
}; };
} }
private InventoryItem[] CreateChosenItems(CharacterCreationPacket packet) private InventoryItem[] CreateChosenItems(CharacterCreationPacket packet) {
{
return new[] return new[]
{ {
_itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 + _itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 +
@ -144,27 +129,22 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
}; };
} }
private static int CalculateCurrentHealth(ushort level, JobSpecificMapping firstJobConfig) private static int CalculateCurrentHealth(ushort level, JobSpecificMapping firstJobConfig) {
{
return (int)((level - 1) * firstJobConfig.DynamicStats.HealthPerLevel + return (int)((level - 1) * firstJobConfig.DynamicStats.HealthPerLevel +
firstJobConfig.BaseStats.Vitality * firstJobConfig.DynamicStats.HealthPerVitality); firstJobConfig.BaseStats.Vitality * firstJobConfig.DynamicStats.HealthPerVitality);
} }
private static int CalculateCurrentMana(ushort level, JobSpecificMapping firstJobConfig) private static int CalculateCurrentMana(ushort level, JobSpecificMapping firstJobConfig) {
{
return (int)((level - 1) * firstJobConfig.DynamicStats.ManaPerLevel + return (int)((level - 1) * firstJobConfig.DynamicStats.ManaPerLevel +
firstJobConfig.BaseStats.Wisdom * firstJobConfig.DynamicStats.ManaPerWisdom); firstJobConfig.BaseStats.Wisdom * firstJobConfig.DynamicStats.ManaPerWisdom);
} }
private static ushort[] GetItemIDsByInventoryTab(IEnumerable<Tuple<ushort, byte>> items) private static ushort[] GetItemIDsByInventoryTab(IEnumerable<Tuple<ushort, byte>> items) {
{
Span<ushort> ids = stackalloc ushort[20]; Span<ushort> ids = stackalloc ushort[20];
ids.Clear(); ids.Clear();
foreach (var item in items) foreach (var item in items) {
{ if (item.Item2 > 20) {
if (item.Item2 > 20)
{
continue; continue;
} }

View file

@ -1,43 +1,34 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Continuity.AuthServer.DB; using Continuity.AuthServer.DB;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NetCoreServer; using RaiNote.PacketMediator;
using Wonderking.Packets.Incoming; using Wonderking.Packets.Incoming;
using Wonderking.Packets.Outgoing; using Wonderking.Packets.Outgoing;
namespace Continuity.AuthServer.PacketHandlers; namespace Continuity.AuthServer.PacketHandlers;
public class CharacterDeletionHandler : IPacketHandler<CharacterDeletePacket> public class CharacterDeletionHandler : IPacketHandler<CharacterDeletePacket, AuthSession> {
{
private readonly WonderkingContext _wonderkingContext; private readonly WonderkingContext _wonderkingContext;
public CharacterDeletionHandler(WonderkingContext wonderkingContext) public CharacterDeletionHandler(WonderkingContext wonderkingContext) {
{
_wonderkingContext = wonderkingContext; _wonderkingContext = wonderkingContext;
} }
public async Task HandleAsync(CharacterDeletePacket packet, TcpSession session) public async Task HandleAsync(CharacterDeletePacket packet, AuthSession session,
{ CancellationToken cancellationToken) {
if (session is not AuthSession authSession)
{
session.Disconnect();
return;
}
var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name && var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name &&
x.Account.Id == authSession.AccountId); x.Account.Id == session.AccountId, cancellationToken);
var response = new CharacterDeleteResponsePacket { HasToBeZero = 0 }; var response = new CharacterDeleteResponsePacket { HasToBeZero = 0 };
if (character == null) if (character == null) {
{ await session.SendAsync(response);
await authSession.SendAsync(response);
return; return;
} }
_wonderkingContext.Characters.Remove(character); _wonderkingContext.Characters.Remove(character);
await _wonderkingContext.SaveChangesAsync(); await _wonderkingContext.SaveChangesAsync(cancellationToken);
await authSession.SendAsync(response); await session.SendAsync(response);
} }
} }

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Continuity.AuthServer.PacketHandlers; namespace Continuity.AuthServer.PacketHandlers;
[StructLayout(LayoutKind.Auto)] [StructLayout(LayoutKind.Auto)]
public struct CharacterMappingItemEntry public struct CharacterMappingItemEntry {
{
public required ushort Id { get; set; } public required ushort Id { get; set; }
public required ushort Quantity { get; set; } public required ushort Quantity { get; set; }
} }

View file

@ -1,29 +1,27 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Continuity.AuthServer.DB; using Continuity.AuthServer.DB;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NetCoreServer; using RaiNote.PacketMediator;
using Wonderking.Packets.Incoming; using Wonderking.Packets.Incoming;
using Wonderking.Packets.Outgoing; using Wonderking.Packets.Outgoing;
namespace Continuity.AuthServer.PacketHandlers; namespace Continuity.AuthServer.PacketHandlers;
public class CharacterNameCheckHandler : IPacketHandler<CharacterNameCheckPacket> public class CharacterNameCheckHandler : IPacketHandler<CharacterNameCheckPacket, AuthSession> {
{
private readonly WonderkingContext _wonderkingContext; private readonly WonderkingContext _wonderkingContext;
public CharacterNameCheckHandler(WonderkingContext wonderkingContext) public CharacterNameCheckHandler(WonderkingContext wonderkingContext) {
{
_wonderkingContext = wonderkingContext; _wonderkingContext = wonderkingContext;
} }
public async Task HandleAsync(CharacterNameCheckPacket packet, TcpSession session) public async Task HandleAsync(CharacterNameCheckPacket packet, AuthSession session,
{ CancellationToken cancellationToken) {
var isTaken = await _wonderkingContext.Characters.AnyAsync(c => c.Name == packet.Name); var isTaken =
await _wonderkingContext.Characters.AnyAsync(c => c.Name == packet.Name,
cancellationToken);
var responsePacket = new CharacterNameCheckPacketResponse { IsTaken = isTaken }; var responsePacket = new CharacterNameCheckPacketResponse { IsTaken = isTaken };
if (session is AuthSession authSession)
{ await session.SendAsync(responsePacket);
await authSession.SendAsync(responsePacket);
}
} }
} }

View file

@ -1,37 +0,0 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License.
using System.Diagnostics;
using JetBrains.Annotations;
using NetCoreServer;
using Wonderking.Packets;
namespace Continuity.AuthServer.PacketHandlers;
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
public interface IPacketHandler<in T> : IPacketHandler where T : IPacket
{
[UsedImplicitly]
public Task HandleAsync(T packet, TcpSession session);
async Task<bool> IPacketHandler.TryHandleAsync(IPacket packet, TcpSession session)
{
if (packet is not T tPacket)
{
return false;
}
using (var activity = new ActivitySource(nameof(Server)).StartActivity("HandleAsync"))
{
activity?.SetTag("Handler", this.ToString());
activity?.SetTag("PacketId", packet.ToString());
await HandleAsync(tPacket, session);
}
return true;
}
}
public interface IPacketHandler
{
Task<bool> TryHandleAsync(IPacket packet, TcpSession session);
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Diagnostics; using System.Diagnostics;
using System.Security.Cryptography; using System.Security.Cryptography;
@ -10,48 +10,44 @@ using Konscious.Security.Cryptography;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using NetCoreServer; using RaiNote.PacketMediator;
using Wonderking.Packets.Incoming; using Wonderking.Packets.Incoming;
using Wonderking.Packets.Outgoing; using Wonderking.Packets.Outgoing;
using Wonderking.Packets.Outgoing.Data; using Wonderking.Packets.Outgoing.Data;
namespace Continuity.AuthServer.PacketHandlers; namespace Continuity.AuthServer.PacketHandlers;
public class LoginHandler : IPacketHandler<LoginInfoPacket> public class LoginHandler : IPacketHandler<LoginInfoPacket, AuthSession> {
{ private static readonly ActivitySource _activitySource = new(nameof(Server));
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly ILogger<LoginHandler> _logger; private readonly ILogger<LoginHandler> _logger;
private readonly WonderkingContext _wonderkingContext; private readonly WonderkingContext _wonderkingContext;
private static readonly ActivitySource _activitySource = new ActivitySource(nameof(Server));
public LoginHandler(ILogger<LoginHandler> logger, WonderkingContext wonderkingContext, IConfiguration configuration) public LoginHandler(ILogger<LoginHandler> logger, WonderkingContext wonderkingContext, IConfiguration configuration) {
{
_logger = logger; _logger = logger;
_wonderkingContext = wonderkingContext; _wonderkingContext = wonderkingContext;
_configuration = configuration; _configuration = configuration;
} }
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session) public async Task HandleAsync(LoginInfoPacket packet, AuthSession session, CancellationToken cancellationToken) {
{
LoginResponseReason loginResponseReason; LoginResponseReason loginResponseReason;
_logger.LoginData(packet.Username, packet.Password); _logger.LoginData(packet.Username, packet.Password);
var account = await _wonderkingContext.Accounts.FirstOrDefaultAsync(a => a.Username == packet.Username); var account =
await _wonderkingContext.Accounts.FirstOrDefaultAsync(a => a.Username == packet.Username,
cancellationToken);
if (account == null) if (account == null) {
{ if (_configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin")) {
if (_configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
{
loginResponseReason = await CreateAccountOnLoginAsync(packet.Username, packet.Password); loginResponseReason = await CreateAccountOnLoginAsync(packet.Username, packet.Password);
account = await _wonderkingContext.Accounts.FirstOrDefaultAsync(a => a.Username == packet.Username); account = await _wonderkingContext.Accounts.FirstOrDefaultAsync(a => a.Username == packet.Username,
cancellationToken);
} }
else else {
{
_logger.RequestedAccountDoesNotExist(packet.Username); _logger.RequestedAccountDoesNotExist(packet.Username);
loginResponseReason = LoginResponseReason.AccountDoesNotExit; loginResponseReason = LoginResponseReason.AccountDoesNotExit;
} }
} }
else else {
{
var salt = account.Salt; var salt = account.Salt;
var tempPasswordBytes = await GetPasswordHashAsync(packet.Password, salt, account.Id); var tempPasswordBytes = await GetPasswordHashAsync(packet.Password, salt, account.Id);
loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password) loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password)
@ -61,50 +57,41 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
var channelData = new ServerChannelData[1]; var channelData = new ServerChannelData[1];
channelData[0] = new ServerChannelData { ChannelId = 0, LoadPercentage = 0, ServerId = 0 }; channelData[0] = new ServerChannelData { ChannelId = 0, LoadPercentage = 0, ServerId = 0 };
var loginResponsePacket = new LoginResponsePacket var loginResponsePacket = new LoginResponsePacket {
{
ResponseReason = loginResponseReason, ResponseReason = loginResponseReason,
ChannelData = channelData.ToArray(), ChannelData = channelData.ToArray(),
UnknownFlag = 1, UnknownFlag = 1,
IsGameMaster = true IsGameMaster = true
}; };
var authSession = session as AuthSession; if (account != null) {
if (account != null && authSession != null) session.AccountId = account.Id;
{
authSession.AccountId = account.Id;
} }
_logger.LogInformation("LoginResponsePacket: {@LoginResponsePacket}", loginResponsePacket); _logger.LogInformation("LoginResponsePacket: {@LoginResponsePacket}", loginResponsePacket);
_ = authSession?.SendAsync(loginResponsePacket); _ = session.SendAsync(loginResponsePacket);
} }
private static async Task<byte[]> GetPasswordHashAsync(string password, byte[] salt, Guid userId) private static async Task<byte[]> GetPasswordHashAsync(string password, byte[] salt, Guid userId) {
{ using var activity = _activitySource.StartActivity();
using var activity = _activitySource.StartActivity("GetPasswordHashAsync");
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Chea1t_Sheet.html#argon2id // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Chea1t_Sheet.html#argon2id
// "Use Argon2id with a minimum configuration of 19 MiB of memory, an iteration count of 2, and 1 degree of parallelism." // "Use Argon2id with a minimum configuration of 19 MiB of memory, an iteration count of 2, and 1 degree of parallelism."
var argon2Id = new Argon2id(Encoding.ASCII.GetBytes(password)) using var argon2Id = new Argon2id(Encoding.ASCII.GetBytes(password));
{ argon2Id.MemorySize = 1024 * 19;
MemorySize = 1024 * 19, argon2Id.Iterations = 2;
Iterations = 2, argon2Id.DegreeOfParallelism = 1;
DegreeOfParallelism = 1, argon2Id.Salt = salt;
Salt = salt, argon2Id.AssociatedData = userId.ToByteArray();
AssociatedData = userId.ToByteArray()
};
return await argon2Id.GetBytesAsync(16); return await argon2Id.GetBytesAsync(16);
} }
private async Task<LoginResponseReason> CreateAccountOnLoginAsync(string username, string password) private async Task<LoginResponseReason> CreateAccountOnLoginAsync(string username, string password) {
{ using var activity = _activitySource.StartActivity();
using var activity = _activitySource.StartActivity("CreateAccountOnLoginAsync");
LoginResponseReason loginResponseReason; LoginResponseReason loginResponseReason;
var transaction = var transaction =
await _wonderkingContext.Database.BeginTransactionAsync(); await _wonderkingContext.Database.BeginTransactionAsync();
await using (transaction) await using (transaction) {
{ try {
try
{
var salt = RandomNumberGenerator.GetBytes(16); var salt = RandomNumberGenerator.GetBytes(16);
var finalAccount = await _wonderkingContext.Accounts.AddAsync(new Account(username, var finalAccount = await _wonderkingContext.Accounts.AddAsync(new Account(username,
Array.Empty<byte>(), "", 0, salt)); Array.Empty<byte>(), "", 0, salt));
@ -117,8 +104,7 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
await transaction.CommitAsync(); await transaction.CommitAsync();
} }
catch (Exception) catch (Exception) {
{
await transaction.RollbackAsync(); // Rollback the transaction on error await transaction.RollbackAsync(); // Rollback the transaction on error
throw; throw;
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using MassTransit; using MassTransit;
using Wonderking.Packets; using Wonderking.Packets;
@ -6,11 +6,9 @@ using Wonderking.Packets;
namespace Continuity.AuthServer.Packets; namespace Continuity.AuthServer.Packets;
[MessageUrn("packets")] [MessageUrn("packets")]
public class RawPacket public class RawPacket {
{
public RawPacket(OperationCode operationCode, Span<byte> messageBody, uint aliveTime, byte unknownValue2, public RawPacket(OperationCode operationCode, Span<byte> messageBody, uint aliveTime, byte unknownValue2,
byte unknownValue, Guid sessionId, AuthSession session) byte unknownValue, Guid sessionId, AuthSession session) {
{
MessageBody = messageBody.ToArray(); MessageBody = messageBody.ToArray();
UnknownValue2 = unknownValue2; UnknownValue2 = unknownValue2;
UnknownValue = unknownValue; UnknownValue = unknownValue;

View file

@ -1,9 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using Continuity.AuthServer;
using Continuity.AuthServer.DB; using Continuity.AuthServer.DB;
using Continuity.AuthServer.PacketHandlers;
using Continuity.AuthServer.Services; using Continuity.AuthServer.Services;
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -17,7 +19,9 @@ using OpenTelemetry.Logs;
using OpenTelemetry.Metrics; using OpenTelemetry.Metrics;
using OpenTelemetry.Resources; using OpenTelemetry.Resources;
using OpenTelemetry.Trace; using OpenTelemetry.Trace;
using RaiNote.PacketMediator;
using Wonderking.Game.Mapping; using Wonderking.Game.Mapping;
using Wonderking.Packets;
var builder = Host.CreateApplicationBuilder(); var builder = Host.CreateApplicationBuilder();
#if DEBUG #if DEBUG
@ -31,70 +35,43 @@ builder.Configuration.AddJsonFile("settings.json", true, true)
.AddEnvironmentVariables().Build(); .AddEnvironmentVariables().Build();
builder.Services.AddLogging(); builder.Services.AddLogging();
var loggerFactory = LoggerFactory.Create(loggingBuilder => var loggerFactory = LoggerFactory.Create(loggingBuilder => {
{
loggingBuilder.AddFile("logs/Continuity.AuthServer-{Date}.log", LogLevel.Trace); loggingBuilder.AddFile("logs/Continuity.AuthServer-{Date}.log", LogLevel.Trace);
loggingBuilder.AddFile("logs/Continuity.AuthServer-{Date}.json.log", LogLevel.Trace, isJson: true); loggingBuilder.AddFile("logs/Continuity.AuthServer-{Date}.json.log", LogLevel.Trace, isJson: true);
loggingBuilder.AddConsole(); loggingBuilder.AddConsole();
}); loggingBuilder.AddOpenTelemetry(logging => {
logging.AddOtlpExporter();
var configuration = builder.Configuration;
if (configuration.GetValue<bool>("Tracing:Enabled"))
{
Action<ResourceBuilder> resourceBuilderAction = r => r
.AddService("Continuity", serviceInstanceId: Environment.MachineName);
builder.Services.AddOpenTelemetry()
.ConfigureResource(resourceBuilderAction)
.WithTracing(tracing =>
{
tracing.AddSource(nameof(Server));
//tracing.AddSource("MassTransit");
tracing.AddEntityFrameworkCoreInstrumentation(options => options.SetDbStatementForText = true);
tracing.AddNpgsql();
})
.WithMetrics(metrics =>
{
metrics.AddRuntimeInstrumentation();
metrics.AddProcessInstrumentation();
});
builder.Logging.AddOpenTelemetry(logging =>
{
var resourceBuilder = ResourceBuilder.CreateDefault();
resourceBuilderAction(resourceBuilder);
logging.SetResourceBuilder(resourceBuilder);
logging.IncludeFormattedMessage = true; logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true; logging.IncludeScopes = true;
}); });
builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => });
{
logging.AddOtlpExporter(options => var configuration = builder.Configuration;
{ builder.Services.AddOpenTelemetry()
options.Endpoint = new Uri(configuration["OTLP:Logging:Endpoint"] ?? string.Empty); .WithMetrics(metrics => {
metrics.AddRuntimeInstrumentation().AddProcessInstrumentation().AddMeter("Microsoft.AspNetCore.Hosting", nameof(Continuity.AuthServer))
.ConfigureResource(resourceBuilder => resourceBuilder.AddService("Continuity", serviceNamespace: "Wonderking", serviceVersion: "0.0.1"));
})
.WithTracing(tracing => {
if (builder.Environment.IsDevelopment()) {
tracing.SetSampler<AlwaysOnSampler>();
}
tracing.AddEntityFrameworkCoreInstrumentation(options => options.SetDbStatementForText = true).AddNpgsql();
}); });
});
builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => builder.Services.AddMetrics();
{
metrics.AddOtlpExporter(options => builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter());
options.Endpoint = new Uri(configuration["OTLP:Metrics:Endpoint"] ?? string.Empty)); builder.Services.ConfigureOpenTelemetryMeterProvider(meter => meter.AddOtlpExporter());
}); builder.Services.ConfigureOpenTelemetryTracerProvider(tracer => tracer.AddOtlpExporter());
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing =>
{
tracing.AddZipkinExporter(options =>
options.Endpoint = new Uri(configuration["Zipkin:Endpoint"] ?? string.Empty));
tracing.AddOtlpExporter(options => options.Endpoint = new Uri(configuration["OTLP:Tracing:Endpoint"] ?? string.Empty));
});
}
builder.Services.AddHealthChecks() builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
builder.Services.AddDbContextPool<WonderkingContext>(o => builder.Services.AddDbContextPool<WonderkingContext>(o => {
{
o.UseNpgsql( o.UseNpgsql(
$"Host={configuration["DB:Host"]};Username={configuration["DB:Username"]};Password={configuration["DB:Password"]};Database={configuration["DB:Database"]};Port={configuration["DB:Port"]}") $"Host={configuration["DB:Host"]};Username={configuration["DB:Username"]};Password={configuration["DB:Password"]};Database={configuration["DB:Database"]};Port={configuration["DB:Port"]}")
.EnableSensitiveDataLogging().UseLazyLoadingProxies().UseLoggerFactory(loggerFactory); .EnableSensitiveDataLogging().UseLoggerFactory(loggerFactory);
}); });
builder.Services.AddSingleton<CharacterStatsMappingConfiguration>( builder.Services.AddSingleton<CharacterStatsMappingConfiguration>(
@ -102,24 +79,33 @@ builder.Services.AddSingleton<CharacterStatsMappingConfiguration>(
File.ReadAllText("config/character-stats.mapping.json")) ?? throw new InvalidOperationException()); File.ReadAllText("config/character-stats.mapping.json")) ?? throw new InvalidOperationException());
builder.Services.AddSingleton<ILoggerFactory>(loggerFactory); builder.Services.AddSingleton<ILoggerFactory>(loggerFactory);
builder.Services.AddSingleton<PacketDistributorService>(); builder.Services.AddSingleton(provider =>
new PacketDistributorService<OperationCode, AuthSession>(
provider.GetRequiredService<IServiceProvider>(),
new List<Assembly> { Assembly.GetAssembly(typeof(OperationCode)) }.AsReadOnly(),
new List<Assembly> { Assembly.GetAssembly(typeof(LoginHandler)) }.AsReadOnly()
));
builder.Services.AddSingleton<ItemObjectPoolService>(); builder.Services.AddSingleton<ItemObjectPoolService>();
builder.Services.AddHostedService(provider =>
provider.GetService<ItemObjectPoolService>() ?? throw new InvalidOperationException());
builder.Services.AddHostedService(provider =>
provider.GetService<PacketDistributorService>() ?? throw new InvalidOperationException());
builder.Services.AddMassTransit(x =>
{
x.UsingInMemory((context, configurator) => configurator.ConfigureEndpoints(context));
x.AddMediator(cfg => cfg.AddConsumers(Assembly.GetExecutingAssembly()));
});
builder.Services.AddHostedService(provider => new WonderkingAuthServer(IPAddress.Any, 10001, builder.Services.AddHostedService(provider => new WonderkingAuthServer(IPAddress.Any, 10001,
provider.GetService<ILogger<WonderkingAuthServer>>() ?? throw new InvalidOperationException(), provider.GetService<ILogger<WonderkingAuthServer>>() ?? throw new InvalidOperationException(),
provider.GetService<IServiceProvider>() ?? throw new InvalidOperationException())); provider.GetService<IServiceProvider>() ?? throw new InvalidOperationException()));
builder.Services.AddHostedService(provider =>
provider.GetService<ItemObjectPoolService>() ?? throw new InvalidOperationException());
builder.Services.AddHostedService(provider =>
provider.GetService<PacketDistributorService<OperationCode, AuthSession>>() ??
throw new InvalidOperationException());
builder.Services.AddMassTransit(x => {
x.UsingInMemory((context, configurator) => configurator.ConfigureEndpoints(context));
x.AddMediator(cfg => cfg.AddConsumers(Assembly.GetExecutingAssembly()));
});
using var host = builder.Build(); using var host = builder.Build();
using (var scope = host.Services.CreateScope())
{ await using (var scope = host.Services.CreateAsyncScope()) {
var db = scope.ServiceProvider.GetRequiredService<WonderkingContext>(); var db = scope.ServiceProvider.GetRequiredService<WonderkingContext>();
await db.Database.MigrateAsync(); await db.Database.MigrateAsync();
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Continuity.AuthServer.DB.Documents; using Continuity.AuthServer.DB.Documents;
@ -10,14 +10,12 @@ using Wonderking.Game.Reader;
namespace Continuity.AuthServer.Services; namespace Continuity.AuthServer.Services;
public class ItemObjectPoolService : IHostedService public class ItemObjectPoolService : IHostedService {
{
private readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool; private readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool;
private readonly ItemReader _itemReader; private readonly ItemReader _itemReader;
private readonly ILogger<ItemObjectPoolService> _logger; private readonly ILogger<ItemObjectPoolService> _logger;
public ItemObjectPoolService(IConfiguration configuration, ILogger<ItemObjectPoolService> logger) public ItemObjectPoolService(IConfiguration configuration, ILogger<ItemObjectPoolService> logger) {
{
_logger = logger; _logger = logger;
_itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue<string>("Path") ?? _itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue<string>("Path") ??
string.Empty); string.Empty);
@ -25,58 +23,48 @@ public class ItemObjectPoolService : IHostedService
_itemObjectPool = new ConcurrentDictionary<uint, ItemObject>(); _itemObjectPool = new ConcurrentDictionary<uint, ItemObject>();
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken) {
{
var amountOfEntries = _itemReader.GetAmountOfEntries(); var amountOfEntries = _itemReader.GetAmountOfEntries();
Parallel.For(0, (int)amountOfEntries, i => Parallel.For(0, (int)amountOfEntries, i => {
{
var itemObject = _itemReader.GetEntry((uint)i); var itemObject = _itemReader.GetEntry((uint)i);
var result = _itemObjectPool.TryAdd(itemObject.ItemID, itemObject); var result = _itemObjectPool.TryAdd(itemObject.ItemID, itemObject);
if (!result) if (!result) {
{
throw new KeyNotFoundException($"Failed to add item {itemObject.ItemID} to the item object pool"); throw new KeyNotFoundException($"Failed to add item {itemObject.ItemID} to the item object pool");
} }
_logger.LogTrace("Item with id:{ID} and name: '{Name}' has been added", itemObject.ItemID, itemObject.Name);
_logger.LogTrace("Item with {ID} has been added", itemObject.ItemID);
}); });
_logger.LogInformation("A total of {AmountOfEntries} items have been added to the item object pool", _logger.LogInformation("A total of {AmountOfEntries} items have been added to the item object pool",
_itemObjectPool.Count); _itemObjectPool.Count);
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken) {
{
return Task.CompletedTask; return Task.CompletedTask;
} }
public ItemObject GetItem(ushort itemId) public ItemObject GetItem(ushort itemId) {
{
_ = _itemObjectPool.TryGetValue(itemId, out var itemObject); _ = _itemObjectPool.TryGetValue(itemId, out var itemObject);
return itemObject; return itemObject;
} }
public bool ContainsItem(ushort itemId) public bool ContainsItem(ushort itemId) {
{
return _itemObjectPool.ContainsKey(itemId); return _itemObjectPool.ContainsKey(itemId);
} }
public IQueryable<ItemObject> QueryItems() public IQueryable<ItemObject> QueryItems() {
{
return _itemObjectPool.AsReadOnly().Values.AsQueryable(); return _itemObjectPool.AsReadOnly().Values.AsQueryable();
} }
public InventoryItem GetBaseInventoryItem(ushort itemId, ushort count = 1, bool isWorn = false) public InventoryItem GetBaseInventoryItem(ushort itemId, ushort count = 1, bool isWorn = false) {
{
var item = GetItem(itemId); var item = GetItem(itemId);
return new InventoryItem return new InventoryItem {
{
ItemId = itemId, ItemId = itemId,
Count = count, Count = count,
Slot = (byte)item.SlotNo1, Slot = (byte)item.SlotNo1,
InventoryTab = InventoryTab =
item.ItemType switch item.ItemType switch {
{
1 => InventoryTab.WornCashEquipment, 1 => InventoryTab.WornCashEquipment,
2 => isWorn ? InventoryTab.WornEquipment : InventoryTab.Equipment, 2 => isWorn ? InventoryTab.WornEquipment : InventoryTab.Equipment,
3 => InventoryTab.Etc, 3 => InventoryTab.Etc,

View file

@ -1,187 +0,0 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License.
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection;
using Continuity.AuthServer.LoggerMessages;
using Continuity.AuthServer.PacketHandlers;
using Continuity.AuthServer.Packets;
using DotNext.Collections.Generic;
using DotNext.Linq.Expressions;
using DotNext.Metaprogramming;
using MassTransit.Internals;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.VisualBasic.CompilerServices;
using Newtonsoft.Json;
using Wonderking.Packets;
namespace Continuity.AuthServer.Services;
using static CodeGenerator;
using static ExpressionBuilder;
public class PacketDistributorService : IHostedService, IDisposable
{
private readonly ConcurrentQueue<RawPacket> _concurrentQueue;
private readonly ILogger<PacketDistributorService> _logger;
private readonly IServiceProvider _serviceProvider;
private ImmutableDictionary<OperationCode,
Func<byte[], IPacket>> _deserializationMap;
private ConcurrentDictionary<OperationCode, IPacketHandler> _packetHandlersInstantiation;
private readonly ActivitySource _activitySource;
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
{
_concurrentQueue = new ConcurrentQueue<RawPacket>();
_logger = logger;
_serviceProvider = serviceProvider;
_activitySource = new ActivitySource(nameof(Server));
}
public Task StartAsync(CancellationToken cancellationToken)
{
var tempDeserializationMap =
new Dictionary<OperationCode, Func<byte[], IPacket>>();
var wonderkingAssembly = Assembly.GetAssembly(typeof(IPacket));
var packetsTypes = GetPacketsWithId(wonderkingAssembly);
var packetHandlers = GetAllPacketHandlersWithId(Assembly.GetExecutingAssembly());
_packetHandlersInstantiation = new ConcurrentDictionary<OperationCode, IPacketHandler>();
packetHandlers.ForEach(x =>
{
var packetHandler =
ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider,
x.Value);
_packetHandlersInstantiation.TryAdd(x.Key, packetHandler as IPacketHandler);
});
foreach (var packetsType in packetsTypes)
{
var lambda = Lambda<Func<byte[], IPacket>>(fun =>
{
var argPacketData = fun[0];
var newPacket = packetsType.Value.New();
var packetVariable = DeclareVariable(packetsType.Value, "packet");
Assign(packetVariable, newPacket);
Call(packetVariable, nameof(IPacket.Deserialize), argPacketData);
Return(packetVariable);
}).Compile();
_logger.PacketCreationFunctionCreated(packetsType.Key);
tempDeserializationMap.Add(packetsType.Key, lambda);
}
_deserializationMap = tempDeserializationMap.ToImmutableDictionary();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private Dictionary<OperationCode, Type> GetPacketsWithId(Assembly executingAssembly)
{
// ! : We are filtering if types that don't have an instance of the required Attribute
var packetsWithId = executingAssembly.GetTypes().AsParallel()
.Where(type => type.HasInterface(typeof(IPacket)) && type is { IsInterface: false, IsAbstract: false })
.Where(type => type.Namespace?.Contains("Incoming") ?? false)
.Select(type => new { Type = type, Attribute = type.GetCustomAttribute<PacketIdAttribute>() })
.Where(item => item.Attribute is not null)
.ToDictionary(item => item.Attribute!.Code, item => item.Type);
if (packetsWithId is not { Count: 0 })
{
packetsWithId.AsParallel()
.ForAll(packet => _logger.PacketWithIdAdded(packet.Key, packet.Value.FullName));
return packetsWithId;
}
_logger.NoPacketsFound();
throw new IncompleteInitialization();
}
private Dictionary<OperationCode, Type> GetAllPacketHandlersWithId(Assembly assembly)
{
// ! : We are filtering if types that don't have an instance of the required Attribute
var packetHandlersWithId = assembly.GetTypes().AsParallel()
.Where(t =>
t is { IsClass: true, IsAbstract: false } && Array.Exists(t
.GetInterfaces(), i =>
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketHandler<>)))
.Select(type => new
{
Type = type,
PacketId = type
.GetInterfaces().First(t1 =>
t1 is { IsGenericType: true } && t1.GetGenericTypeDefinition() == typeof(IPacketHandler<>))
.GetGenericArguments()[0].GetCustomAttribute<PacketIdAttribute>()?.Code
})
.Where(x => x.PacketId is not null)
.ToDictionary(
x => x.PacketId!.Value, x => x.Type
);
if (packetHandlersWithId is not { Count: 0 })
{
packetHandlersWithId.AsParallel().ForAll(packetHandler =>
_logger.PacketHandlerWithIdAdded(packetHandler.Key, packetHandler.Value.FullName));
return packetHandlersWithId;
}
_logger.NoPacketHandlersFound();
throw new IncompleteInitialization();
}
public void AddPacket(RawPacket rawPacket)
{
_concurrentQueue.Enqueue(rawPacket);
DequeueRawPacket();
_logger.PacketReceived(rawPacket.OperationCode);
}
private void DequeueRawPacket()
{
if (_concurrentQueue.TryDequeue(out var item))
{
ThreadPool.QueueUserWorkItem(InvokePacketHandler, item, preferLocal: false);
}
}
private void InvokePacketHandler(RawPacket item)
{
IPacket packet;
_logger.PacketDequeued(item.Session.Id, item.OperationCode);
if (!_deserializationMap.TryGetValue(item.OperationCode, out var value))
{
_logger.PacketTypeNotFound(item.OperationCode);
return;
}
using (var packetParsingActivity = _activitySource.StartActivity("PacketParsing"))
{
packetParsingActivity?.SetTag("PacketId", item.OperationCode);
packet = value(item.MessageBody);
}
using (var packetHandlerActivity = _activitySource.StartActivity("PacketHandler"))
{
packetHandlerActivity?.SetTag("PacketId", item.OperationCode);
_ = _packetHandlersInstantiation[item.OperationCode].TryHandleAsync(packet, item.Session);
}
_logger.PacketData(JsonConvert.SerializeObject(packet));
_logger.PacketFinished(item.Session.Id, item.OperationCode);
}
public void Dispose()
{
GC.SuppressFinalize(this);
_activitySource.Dispose();
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
@ -9,74 +9,62 @@ using NetCoreServer;
namespace Continuity.AuthServer.Services; namespace Continuity.AuthServer.Services;
public class WonderkingAuthServer : TcpServer, IHostedService public class WonderkingAuthServer : TcpServer, IHostedService {
{
private readonly ILogger<WonderkingAuthServer> _logger; private readonly ILogger<WonderkingAuthServer> _logger;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
public WonderkingAuthServer(IPAddress address, int port, ILogger<WonderkingAuthServer> logger, public WonderkingAuthServer(IPAddress address, int port, ILogger<WonderkingAuthServer> logger,
IServiceProvider serviceProvider) : base(address, port) IServiceProvider serviceProvider) : base(address, port) {
{
_logger = logger; _logger = logger;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken) {
{
Start(); Start();
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken) {
{
DisconnectAll(); DisconnectAll();
Stop(); Stop();
return Task.CompletedTask; return Task.CompletedTask;
} }
protected override TcpSession CreateSession() protected override TcpSession CreateSession() {
{
return ActivatorUtilities.CreateInstance<AuthSession>(_serviceProvider, this); return ActivatorUtilities.CreateInstance<AuthSession>(_serviceProvider, this);
} }
protected override void OnStarting() protected override void OnStarting() {
{
_logger.LogInformation("Starting"); _logger.LogInformation("Starting");
base.OnStarting(); base.OnStarting();
} }
protected override void OnStarted() protected override void OnStarted() {
{
_logger.LogInformation("Started"); _logger.LogInformation("Started");
base.OnStarted(); base.OnStarted();
} }
protected override void OnStopping() protected override void OnStopping() {
{
_logger.LogInformation("Stopping"); _logger.LogInformation("Stopping");
base.OnStopping(); base.OnStopping();
} }
protected override void OnStopped() protected override void OnStopped() {
{
_logger.LogInformation("Stopped"); _logger.LogInformation("Stopped");
base.OnStopped(); base.OnStopped();
} }
protected override void OnConnected(TcpSession session) protected override void OnConnected(TcpSession session) {
{
_logger.LogInformation("Client connected {Session}", session.Id); _logger.LogInformation("Client connected {Session}", session.Id);
base.OnConnected(session); base.OnConnected(session);
} }
protected override void OnDisconnected(TcpSession session) protected override void OnDisconnected(TcpSession session) {
{
_logger.LogInformation("Client disconnected {Session}", session.Id); _logger.LogInformation("Client disconnected {Session}", session.Id);
base.OnDisconnected(session); base.OnDisconnected(session);
} }
protected override void OnError(SocketError error) protected override void OnError(SocketError error) {
{
_logger.LogError("An error has occured {Error}", error); _logger.LogError("An error has occured {Error}", error);
} }
} }

View file

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IDisposableAnalyzers" Version="4.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wonderking\Wonderking.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,21 @@
// Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices;
using System.Text.Json;
Span<byte> buffer = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
Console.WriteLine(buffer.Length == Marshal.SizeOf<TestStruct>());
var tstruct = MemoryMarshal.Cast<byte, TestStruct>(buffer)[0];
Console.WriteLine(JsonSerializer.Serialize(tstruct));
Console.WriteLine(tstruct.b);
[StructLayout(LayoutKind.Explicit, Size = 20)]
public struct TestStruct {
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 4)]
public int[] arr;
[FieldOffset(16)]
[MarshalAs(UnmanagedType.I4)]
public int b;
}

View file

@ -1,28 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Continuity.AuthServer", "Continuity.AuthServer\Continuity.AuthServer.csproj", "{7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{7D560FA1-A61C-4B67-8300-835CA5814621}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wonderking", "Wonderking\Wonderking.csproj", "{6B53A10B-C397-4347-BB00-A12272D0528E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Release|Any CPU.Build.0 = Release|Any CPU
{7D560FA1-A61C-4B67-8300-835CA5814621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D560FA1-A61C-4B67-8300-835CA5814621}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D560FA1-A61C-4B67-8300-835CA5814621}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D560FA1-A61C-4B67-8300-835CA5814621}.Release|Any CPU.Build.0 = Release|Any CPU
{6B53A10B-C397-4347-BB00-A12272D0528E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B53A10B-C397-4347-BB00-A12272D0528E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B53A10B-C397-4347-BB00-A12272D0528E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B53A10B-C397-4347-BB00-A12272D0528E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

6
Continuity.slnx Normal file
View file

@ -0,0 +1,6 @@
<Solution>
<Project Path="Benchmarks/Benchmarks.csproj" />
<Project Path="Continuity.AuthServer/Continuity.AuthServer.csproj" />
<Project Path="Continuity.WorldServer/Continuity.WorldServer.csproj" />
<Project Path="Wonderking/Wonderking.csproj" />
</Solution>

View file

@ -4,3 +4,11 @@ This is a continuation and rewrite of the original Project Infinity for Wonderki
## Important notes for developers ## Important notes for developers
* Avoid using statements with TcpSession, AuthSession or any type that inherits Session outside the session itself. * Avoid using statements with TcpSession, AuthSession or any type that inherits Session outside the session itself.
# Scripts example usage
**split_dat_file.ps1**
``.\scripts\split_dat_file.ps1 .\wk-data\baseitemdata_clean.dat 932 9``
**dexor_binary_file.ps1**
``.\scripts\dexor_binary_file.ps1 .\wk-data\baseitemdata.dat .\wk-data\baseitemdata_clean.dat 0xc5``

View file

@ -1,9 +1,8 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
namespace Wonderking.Game.Data.Character; namespace Wonderking.Game.Data.Character;
public enum Gender : byte public enum Gender : byte {
{
None = 0, None = 0,
Male = 1, Male = 1,
Female = 2 Female = 2

View file

@ -1,9 +1,8 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
namespace Wonderking.Game.Data.Character; namespace Wonderking.Game.Data.Character;
public enum PvPLevel : byte public enum PvPLevel : byte {
{
None = 0, None = 0,
Dualer = 1, Dualer = 1,
Challenger = 2, Challenger = 2,

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Wonderking.Game.Data.Item; namespace Wonderking.Game.Data.Item;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ContainedItem public struct ContainedItem {
{
public short ID { get; internal set; } public short ID { get; internal set; }
public float ObtainChance { get; internal set; } public float ObtainChance { get; internal set; }
} }

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Wonderking.Game.Data.Item; namespace Wonderking.Game.Data.Item;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct CraftMaterial public struct CraftMaterial {
{
public uint ID; public uint ID;
public uint Amount; public uint Amount;
} }

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Wonderking.Game.Data.Item; namespace Wonderking.Game.Data.Item;
[StructLayout(LayoutKind.Explicit, Size = 64)] [StructLayout(LayoutKind.Explicit, Size = 64)]
public struct ElementalStats public struct ElementalStats {
{
[FieldOffset(0)] [FieldOffset(0)]
[MarshalAs(UnmanagedType.I4)] [MarshalAs(UnmanagedType.I4)]
public int MinimumFireDamage; public int MinimumFireDamage;

View file

@ -1,9 +1,13 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices;
namespace Wonderking.Game.Data.Item; namespace Wonderking.Game.Data.Item;
public struct ItemOptions [StructLayout(LayoutKind.Sequential, Pack = 1)]
{ public struct ItemOptions {
public uint[] OptionIDs { get; internal set; } [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)]
public bool OptionAvailable { get; internal set; } public uint[] OptionIDs;
public bool OptionAvailable;
} }

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Wonderking.Game.Data.Item; namespace Wonderking.Game.Data.Item;
[StructLayout(LayoutKind.Explicit, Size = 24)] [StructLayout(LayoutKind.Explicit, Size = 24)]
public struct Stats public struct Stats {
{
[FieldOffset(0)] [FieldOffset(0)]
[MarshalAs(UnmanagedType.I4)] [MarshalAs(UnmanagedType.I4)]
public int Strength; public int Strength;

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Wonderking.Game.Data.Item; using Wonderking.Game.Data.Item;
@ -7,8 +7,7 @@ using Wonderking.Utils;
namespace Wonderking.Game.Data; namespace Wonderking.Game.Data;
[GameDataMetadata(932, "baseitemdata.dat", 197)] [GameDataMetadata(932, "baseitemdata.dat", 197)]
public struct ItemObject public struct ItemObject {
{
public uint ItemID { get; set; } public uint ItemID { get; set; }
public bool Disabled { get; set; } public bool Disabled { get; set; }
public uint ItemType { get; set; } public uint ItemType { get; set; }
@ -128,10 +127,10 @@ public struct ItemObject
[JsonConverter(typeof(ByteArrayConverter))] [JsonConverter(typeof(ByteArrayConverter))]
public byte[] R16C { get; set; } public byte[] R16C { get; set; }
public int InventoryX { get; set; } public int SheetX { get; set; }
public int InventoryY { get; set; } public int SheetY { get; set; }
public int InventoryWidth { get; set; } public int SheetWidth { get; set; }
public int InventoryHeight { get; set; } public int SheetHeight { get; set; }
public int SheetID { get; set; } public int SheetID { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
@ -169,4 +168,7 @@ public struct ItemObject
[JsonConverter(typeof(ByteArrayConverter))] [JsonConverter(typeof(ByteArrayConverter))]
public byte[] Unknown21_2 { get; set; } public byte[] Unknown21_2 { get; set; }
public int RearWearItemIndex { get; set; }
public int FrontWearItemIndex { get; set; }
} }

View file

@ -1,18 +1,16 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Reflection; using System.Reflection;
namespace Wonderking.Game; namespace Wonderking.Game;
public abstract class DataReader<T> public abstract class DataReader<T> {
{
private readonly string _datFileName; private readonly string _datFileName;
private readonly byte _xorKey; private readonly byte _xorKey;
protected readonly ushort SizeOfEntry; protected readonly ushort SizeOfEntry;
protected DataReader(string path) protected DataReader(string path) {
{
Path = path; Path = path;
_xorKey = GetXorKey(); _xorKey = GetXorKey();
SizeOfEntry = GetSizeOfEntry(); SizeOfEntry = GetSizeOfEntry();
@ -27,31 +25,26 @@ public abstract class DataReader<T>
public abstract uint GetAmountOfEntries(); public abstract uint GetAmountOfEntries();
public abstract T GetEntry(uint entryId); public abstract T GetEntry(uint entryId);
private static ushort GetSizeOfEntry() private static ushort GetSizeOfEntry() {
{
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DataEntrySize ?? return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DataEntrySize ??
throw new NotSupportedException("DataEntrySize is null"); throw new NotSupportedException("DataEntrySize is null");
} }
private static string GetDatFileName() private static string GetDatFileName() {
{
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DatFileName ?? return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DatFileName ??
throw new NotSupportedException("DatFileName is null"); throw new NotSupportedException("DatFileName is null");
} }
private static byte GetXorKey() private static byte GetXorKey() {
{
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.XorKey ?? return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.XorKey ??
throw new NotSupportedException("XorKey is null"); throw new NotSupportedException("XorKey is null");
} }
private Span<byte> GetDatFileContent(string path) private ReadOnlySpan<byte> GetDatFileContent(string path) {
{
var fileData = File.ReadAllBytes(path + _datFileName); var fileData = File.ReadAllBytes(path + _datFileName);
var data = new byte[fileData.Length]; var data = new byte[fileData.Length];
for (var i = 0; i < fileData.Length; i++) for (var i = 0; i < fileData.Length; i++) {
{
data[i] = (byte)(fileData[i] ^ _xorKey); data[i] = (byte)(fileData[i] ^ _xorKey);
} }

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Wonderking.Game; namespace Wonderking.Game;
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
public class GameDataMetadataAttribute(ushort dataEntrySize, string datFileName, byte xorKey) : Attribute public class GameDataMetadataAttribute(ushort dataEntrySize, string datFileName, byte xorKey) : Attribute {
{
[UsedImplicitly] public byte XorKey { get; init; } = xorKey; [UsedImplicitly] public byte XorKey { get; init; } = xorKey;
[UsedImplicitly] public ushort DataEntrySize { get; init; } = dataEntrySize; [UsedImplicitly] public ushort DataEntrySize { get; init; } = dataEntrySize;

View file

@ -1,11 +1,10 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Wonderking.Game.Mapping; namespace Wonderking.Game.Mapping;
public class CharacterStatsMappingConfiguration public class CharacterStatsMappingConfiguration {
{
[JsonPropertyName("default")] public required DefaultCharacterMapping DefaultCharacterMapping { get; set; } [JsonPropertyName("default")] public required DefaultCharacterMapping DefaultCharacterMapping { get; set; }
[JsonPropertyName("1")] public required JobSpecificMapping Swordsman { get; set; } [JsonPropertyName("1")] public required JobSpecificMapping Swordsman { get; set; }

View file

@ -1,10 +1,9 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Wonderking.Game.Mapping; namespace Wonderking.Game.Mapping;
public class DefaultCharacterMapping public class DefaultCharacterMapping {
{
[JsonPropertyName("items")] public required ICollection<Item> Items { get; set; } [JsonPropertyName("items")] public required ICollection<Item> Items { get; set; }
} }

View file

@ -1,11 +1,10 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Wonderking.Game.Mapping; namespace Wonderking.Game.Mapping;
public class DynamicStats public class DynamicStats {
{
[JsonPropertyName("healthPerLevel")] public int HealthPerLevel { get; set; } [JsonPropertyName("healthPerLevel")] public int HealthPerLevel { get; set; }
[JsonPropertyName("manaPerLevel")] public int ManaPerLevel { get; set; } [JsonPropertyName("manaPerLevel")] public int ManaPerLevel { get; set; }

View file

@ -1,11 +1,10 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Wonderking.Game.Mapping; namespace Wonderking.Game.Mapping;
public class Item public class Item {
{
[JsonPropertyName("id")] public ushort Id { get; set; } [JsonPropertyName("id")] public ushort Id { get; set; }
[JsonPropertyName("quantity")] public ushort Quantity { get; set; } [JsonPropertyName("quantity")] public ushort Quantity { get; set; }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -7,8 +7,7 @@ using Wonderking.Packets.Outgoing.Data;
namespace Wonderking.Game.Mapping; namespace Wonderking.Game.Mapping;
[UsedImplicitly] [UsedImplicitly]
public class JobSpecificMapping public class JobSpecificMapping {
{
[JsonPropertyName("items")] public required ICollection<Item> Items { get; set; } [JsonPropertyName("items")] public required ICollection<Item> Items { get; set; }
[JsonPropertyName("baseStats")] public required BaseStats BaseStats { get; set; } [JsonPropertyName("baseStats")] public required BaseStats BaseStats { get; set; }

View file

@ -1,75 +1,61 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Wonderking.Game.Reader; namespace Wonderking.Game.Reader;
public static class BinaryReader<T> where T : new() public static class BinaryReader<T> where T : new() {
{
public static readonly Func<BinaryReader, T> Read; public static readonly Func<BinaryReader, T> Read;
#pragma warning disable MA0051, CA1810
static BinaryReader() static BinaryReader()
#pragma warning restore MA0051, CA1810
{ {
var type = typeof(T); var type = typeof(T);
if (type == typeof(bool)) if (type == typeof(bool)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, bool>)(p => p.ReadBoolean()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, bool>)(p => p.ReadBoolean());
} }
else if (type == typeof(char)) else if (type == typeof(char)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, char>)(p => p.ReadChar()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, char>)(p => p.ReadChar());
} }
else if (type == typeof(string)) else if (type == typeof(string)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, string>)(p => p.ReadString()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, string>)(p => p.ReadString());
} }
else if (type == typeof(sbyte)) else if (type == typeof(sbyte)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, sbyte>)(p => p.ReadSByte()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, sbyte>)(p => p.ReadSByte());
} }
else if (type == typeof(short)) else if (type == typeof(short)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, short>)(p => p.ReadInt16()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, short>)(p => p.ReadInt16());
} }
else if (type == typeof(int)) else if (type == typeof(int)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, int>)(p => p.ReadInt32()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, int>)(p => p.ReadInt32());
} }
else if (type == typeof(long)) else if (type == typeof(long)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, long>)(p => p.ReadInt64()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, long>)(p => p.ReadInt64());
} }
else if (type == typeof(byte)) else if (type == typeof(byte)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, byte>)(p => p.ReadByte()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, byte>)(p => p.ReadByte());
} }
else if (type == typeof(ushort)) else if (type == typeof(ushort)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ushort>)(p => p.ReadUInt16()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ushort>)(p => p.ReadUInt16());
} }
else if (type == typeof(uint)) else if (type == typeof(uint)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, uint>)(p => p.ReadUInt32()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, uint>)(p => p.ReadUInt32());
} }
else if (type == typeof(ulong)) else if (type == typeof(ulong)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ulong>)(p => p.ReadUInt64()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ulong>)(p => p.ReadUInt64());
} }
else if (type == typeof(float)) else if (type == typeof(float)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, float>)(p => p.ReadSingle()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, float>)(p => p.ReadSingle());
} }
else if (type == typeof(double)) else if (type == typeof(double)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, double>)(p => p.ReadDouble()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, double>)(p => p.ReadDouble());
} }
else if (type == typeof(decimal)) else if (type == typeof(decimal)) {
{
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, decimal>)(p => p.ReadDecimal()); Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, decimal>)(p => p.ReadDecimal());
} }
else else {
{
Read = (Func<BinaryReader, T>)(p => Read = (Func<BinaryReader, T>)(p =>
(T)(object)p.ReadBytes(Marshal.SizeOf(new T()))); (T)(object)p.ReadBytes(Marshal.SizeOf(new T())));
} }

View file

@ -1,38 +1,25 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
/* Nicht gemergte Änderung aus Projekt "Wonderking(net7.0)"
Vor:
using System.Runtime.InteropServices;
using System.Text;
Nach:
using System.Text;
*/
using System.Text; using System.Text;
namespace Wonderking.Game.Reader; namespace Wonderking.Game.Reader;
public static class GenericReaderExtensions public static class GenericReaderExtensions {
{ public static string ReadString(this BinaryReader reader, int length) {
public static string ReadString(this BinaryReader reader, int length) var ret = Encoding.ASCII.GetString(reader.ReadBytes(length)).Replace("\0", "", StringComparison.Ordinal);
{
var ret = Encoding.ASCII.GetString(reader.ReadBytes(length)).Replace("\0", "");
return ret; return ret;
} }
public static T[] ReadArray<T>(this BinaryReader pReader, int pLength) where T : new() public static T[] ReadArray<T>(this BinaryReader pReader, int pLength) where T : new() {
{
var array = new T[pLength]; var array = new T[pLength];
for (var index = 0; index < pLength; ++index) for (var index = 0; index < pLength; ++index) {
{
array[index] = pReader.Read<T>(); array[index] = pReader.Read<T>();
} }
return array; return array;
} }
public static T Read<T>(this BinaryReader br) where T : new() public static T Read<T>(this BinaryReader br) where T : new() {
{
return BinaryReader<T>.Read(br); return BinaryReader<T>.Read(br);
} }
} }

View file

@ -1,16 +1,16 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Wonderking.Game.Data; using Wonderking.Game.Data;
using Wonderking.Game.Data.Item; using Wonderking.Game.Data.Item;
namespace Wonderking.Game.Reader; namespace Wonderking.Game.Reader;
public class ItemReader(string path) : DataReader<ItemObject>(path) public class ItemReader(string path) : DataReader<ItemObject>(path) {
{ public override uint GetAmountOfEntries() {
public override uint GetAmountOfEntries()
{
return (uint)((DatFileContent.Length - 9) / SizeOfEntry); return (uint)((DatFileContent.Length - 9) / SizeOfEntry);
} }
@ -46,34 +46,8 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
item.Unknown8 = data.Slice(92, 4).ToArray(); // 92 -> 96 item.Unknown8 = data.Slice(92, 4).ToArray(); // 92 -> 96
item.R2C = data.Slice(96, 16).ToArray(); // 96 -> 112 item.R2C = data.Slice(96, 16).ToArray(); // 96 -> 112
item.Unknown9 = data.Slice(112, 4).ToArray(); // 112 -> 116 item.Unknown9 = data.Slice(112, 4).ToArray(); // 112 -> 116
item.Stats = new Stats item.Stats = ReadStats(ref data); // 116 -> 140
{ item.ElementalStats = ReadElementalStats(ref data); // 140 -> 204
Strength = BitConverter.ToInt32(data.Slice(116, 4)), // 116 -> 120
Dexterity = BitConverter.ToInt32(data.Slice(120, 4)), // 120 -> 124
Intelligence = BitConverter.ToInt32(data.Slice(124, 4)), // 124 -> 128
Vitality = BitConverter.ToInt32(data.Slice(128, 4)), // 128 -> 132
Luck = BitConverter.ToInt32(data.Slice(132, 4)), // 132 -> 136
Wisdom = BitConverter.ToInt32(data.Slice(136, 4)) // 136 -> 140
}; // 116 -> 140
item.ElementalStats = new ElementalStats
{
MinimumFireDamage = BitConverter.ToInt32(data.Slice(140, 4)), // 140 -> 144
MinimumWaterDamage = BitConverter.ToInt32(data.Slice(144, 4)), // 144 -> 148
MinimumDarkDamage = BitConverter.ToInt32(data.Slice(148, 4)), // 148 -> 152
MinimumHolyDamage = BitConverter.ToInt32(data.Slice(152, 4)), // 152 -> 156
MaximumFireDamage = BitConverter.ToInt32(data.Slice(156, 4)), // 156 -> 160
MaximumWaterDamage = BitConverter.ToInt32(data.Slice(160, 4)), // 160 -> 164
MaximumDarkDamage = BitConverter.ToInt32(data.Slice(164, 4)), // 164 -> 168
MaximumHolyDamage = BitConverter.ToInt32(data.Slice(168, 4)), // 168 -> 172
ElementFire = BitConverter.ToUInt32(data.Slice(172, 4)), // 172 -> 176
ElementWater = BitConverter.ToUInt32(data.Slice(176, 4)), // 176 -> 180
ElementDark = BitConverter.ToUInt32(data.Slice(180, 4)), // 180 -> 184
ElementHoly = BitConverter.ToUInt32(data.Slice(184, 4)), // 184 -> 188
FireResistance = BitConverter.ToInt32(data.Slice(188, 4)), // 188 -> 192
WaterResistance = BitConverter.ToInt32(data.Slice(192, 4)), // 192 -> 196
DarkResistance = BitConverter.ToInt32(data.Slice(196, 4)), // 196 -> 200
HolyResistance = BitConverter.ToInt32(data.Slice(200, 4)) // 200 -> 204
}; // 140 -> 204
item.R7C = data.Slice(204, 4).ToArray(); // 204 -> 208 item.R7C = data.Slice(204, 4).ToArray(); // 204 -> 208
item.R8C = data.Slice(208, 8).ToArray(); // 208 -> 216 item.R8C = data.Slice(208, 8).ToArray(); // 208 -> 216
item.Speed = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(216, 4)); // 216 -> 220 item.Speed = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(216, 4)); // 216 -> 220
@ -101,71 +75,80 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
item.MinimalAttackDamage = BitConverter.ToInt32(data.Slice(308, 4)); // 308 -> 312 item.MinimalAttackDamage = BitConverter.ToInt32(data.Slice(308, 4)); // 308 -> 312
item.MaximalAttackDamage = BitConverter.ToInt32(data.Slice(312, 4)); // 312 -> 316 item.MaximalAttackDamage = BitConverter.ToInt32(data.Slice(312, 4)); // 312 -> 316
item.PhysicalDamage = BitConverter.ToInt32(data.Slice(316, 4)); // 316 -> 320 item.PhysicalDamage = BitConverter.ToInt32(data.Slice(316, 4)); // 316 -> 320
item.CraftMaterial = new CraftMaterial[] item.CraftMaterial = ReadCraftMaterial(ref data); // 320 -> 352
{
new()
{
ID = BitConverter.ToUInt32(data.Slice(320, 4)), // 320 -> 324
Amount = BitConverter.ToUInt32(data.Slice(336, 4)) // 336 -> 340
},
new()
{
ID = BitConverter.ToUInt32(data.Slice(324, 4)), // 324 -> 328
Amount = BitConverter.ToUInt32(data.Slice(340, 4)) // 340 -> 344
},
new()
{
ID = BitConverter.ToUInt32(data.Slice(328, 4)), // 328 -> 332
Amount = BitConverter.ToUInt32(data.Slice(344, 4)) // 344 -> 348
},
new()
{
ID = BitConverter.ToUInt32(data.Slice(332, 4)), // 332 -> 336
Amount = BitConverter.ToUInt32(data.Slice(348, 4)) // 348 -> 352
}
}; // 320 -> 352
item.CraftResultAmount = BitConverter.ToUInt32(data.Slice(352, 4)); // 352 -> 356 item.CraftResultAmount = BitConverter.ToUInt32(data.Slice(352, 4)); // 352 -> 356
item.R14C = data.Slice(356, 4).ToArray(); // 356 -> 360 item.R14C = data.Slice(356, 4).ToArray(); // 356 -> 360
item.CraftResultItem = BitConverter.ToUInt32(data.Slice(360, 4)); // 360 -> 364 item.CraftResultItem = BitConverter.ToUInt32(data.Slice(360, 4)); // 360 -> 364
item.R15C = data.Slice(364, 4).ToArray(); // 364 -> 368 item.R15C = data.Slice(364, 4).ToArray(); // 364 -> 368
item.R16C = data.Slice(368, 20).ToArray(); // 368 -> 388 item.R16C = data.Slice(368, 12).ToArray(); // 368 -> 380
item.InventoryX = BitConverter.ToInt32(data.Slice(388, 4)); // 388 -> 392 item.FrontWearItemIndex = BitConverter.ToInt32(data.Slice(380, 4));
item.InventoryY = BitConverter.ToInt32(data.Slice(392, 4)); // 392 -> 396 item.RearWearItemIndex = BitConverter.ToInt32(data.Slice(380, 4));
item.InventoryWidth = BitConverter.ToInt32(data.Slice(396, 4)); // 396 -> 400 item.SheetX = BitConverter.ToInt32(data.Slice(388, 4)); // 388 -> 392
item.InventoryHeight = BitConverter.ToInt32(data.Slice(400, 4)); // 400 -> 404 item.SheetY = BitConverter.ToInt32(data.Slice(392, 4)); // 392 -> 396
item.SheetWidth = BitConverter.ToInt32(data.Slice(396, 4)); // 396 -> 400
item.SheetHeight = BitConverter.ToInt32(data.Slice(400, 4)); // 400 -> 404
item.SheetID = BitConverter.ToInt32(data.Slice(404, 4)); // 404 -> 408 item.SheetID = BitConverter.ToInt32(data.Slice(404, 4)); // 404 -> 408
item.Name = Encoding.ASCII.GetString(data.Slice(408, 20)); // 408 -> 428 item.Name = Encoding.ASCII.GetString(data.Slice(408, 20)); // 408 -> 428
item.Description = Encoding.ASCII.GetString(data.Slice(428, 85)); // 428 -> 513 item.Description = Encoding.ASCII.GetString(data.Slice(428, 85)); // 428 -> 513
item.Unknown1 = data.Slice(513, 175).ToArray(); // 513 -> 688 item.Unknown1 = data.Slice(513, 175).ToArray(); // 513 -> 688
item.IsEnchantable = BitConverter.ToBoolean(data.Slice(688, 4)); // 688 -> 672 item.IsEnchantable = BitConverter.ToBoolean(data.Slice(688, 4)); // 688 -> 672
item.Unknown1_2 = data.Slice(692, 104).ToArray(); // 692 -> 796 item.Unknown1_2 = data.Slice(692, 104).ToArray(); // 692 -> 796
item.SetItems = new[] item.SetItems = ReadSetItems(ref data); // 796 -> 816
{
BitConverter.ToUInt32(data.Slice(796, 4)), // 796 -> 800
BitConverter.ToUInt32(data.Slice(800, 4)), // 800 -> 804
BitConverter.ToUInt32(data.Slice(804, 4)), // 804 -> 808
BitConverter.ToUInt32(data.Slice(808, 4)), // 808 -> 812
BitConverter.ToUInt32(data.Slice(812, 4)) // 812 -> 816
}; // 796 -> 816
item.SetID = BitConverter.ToUInt32(data.Slice(816, 4)); // 816 -> 820 item.SetID = BitConverter.ToUInt32(data.Slice(816, 4)); // 816 -> 820
item.Options = new ItemOptions item.Options = ReadItemOptions(ref data); // 820 -> 840
{
OptionIDs = new[]
{
BitConverter.ToUInt32(data.Slice(824, 4)), // 824 -> 828
BitConverter.ToUInt32(data.Slice(828, 4)), // 828 -> 832
BitConverter.ToUInt32(data.Slice(832, 4)), // 832 -> 836
BitConverter.ToUInt32(data.Slice(836, 4)) // 836 -> 840
},
OptionAvailable = BitConverter.ToBoolean(data.Slice(820, 4)) // 820 -> 824
}; // 820 -> 840
item.Unknown19 = data.Slice(840, 23).ToArray(); // 840 -> 863 item.Unknown19 = data.Slice(840, 23).ToArray(); // 840 -> 863
item.PetID = data[863]; // 863 -> 864 item.PetID = data[863]; // 863 -> 864
item.Unknown20 = data.Slice(864, 20).ToArray(); // 864 -> 884 item.Unknown20 = data.Slice(864, 20).ToArray(); // 864 -> 884
item.HitBoxScaling = data[884]; // 884 -> 885 item.HitBoxScaling = data[884]; // 884 -> 885
item.Unknown20_2 = data.Slice(885, 13).ToArray(); // 885 -> 898 item.Unknown20_2 = data.Slice(885, 13).ToArray(); // 885 -> 898
item.ContainedItems = new[] item.ContainedItems = ReadContainedItems(ref data); // 898 -> 928
{ item.MinimumLevelRequirement = data[928]; // 928 -> 929
item.Unknown21_2 = data.Slice(929, 3).ToArray(); // 929 -> 932
return item;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Stats ReadStats(ref Span<byte> data) {
return new Stats {
Strength = BitConverter.ToInt32(data.Slice(116, 4)), // 116 -> 120
Dexterity = BitConverter.ToInt32(data.Slice(120, 4)), // 120 -> 124
Intelligence = BitConverter.ToInt32(data.Slice(124, 4)), // 124 -> 128
Vitality = BitConverter.ToInt32(data.Slice(128, 4)), // 128 -> 132
Luck = BitConverter.ToInt32(data.Slice(132, 4)), // 132 -> 136
Wisdom = BitConverter.ToInt32(data.Slice(136, 4)) // 136 -> 140
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint[] ReadSetItems(ref Span<byte> data) {
return
[
BitConverter.ToUInt32(data.Slice(796, 4)), // 796 -> 800
BitConverter.ToUInt32(data.Slice(800, 4)), // 800 -> 804
BitConverter.ToUInt32(data.Slice(804, 4)), // 804 -> 808
BitConverter.ToUInt32(data.Slice(808, 4)), // 808 -> 812
BitConverter.ToUInt32(data.Slice(812, 4)) // 812 -> 816
];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ItemOptions ReadItemOptions(ref Span<byte> data) {
return new ItemOptions {
OptionAvailable = BitConverter.ToBoolean(data.Slice(820, 4)), // 820 -> 824
OptionIDs =
[
BitConverter.ToUInt32(data.Slice(824, 4)), // 824 -> 828
BitConverter.ToUInt32(data.Slice(828, 4)), // 828 -> 832
BitConverter.ToUInt32(data.Slice(832, 4)), // 832 -> 836
BitConverter.ToUInt32(data.Slice(836, 4)) // 836 -> 840
]
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ContainedItem[] ReadContainedItems(ref Span<byte> data) {
return
[
new ContainedItem new ContainedItem
{ {
ID = BitConverter.ToInt16(data.Slice(898, 2)), // 898 -> 900 ID = BitConverter.ToInt16(data.Slice(898, 2)), // 898 -> 900
@ -191,9 +174,38 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
ID = BitConverter.ToInt16(data.Slice(906, 2)), // 906 -> 908 ID = BitConverter.ToInt16(data.Slice(906, 2)), // 906 -> 908
ObtainChance = BitConverter.ToSingle(data.Slice(924, 4)) // 924 -> 928 ObtainChance = BitConverter.ToSingle(data.Slice(924, 4)) // 924 -> 928
} }
}; ];
item.MinimumLevelRequirement = data[928]; // 928 -> 929 }
item.Unknown21_2 = data.Slice(929, 3).ToArray(); // 929 -> 932
return item; [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static CraftMaterial[] ReadCraftMaterial(ref Span<byte> data) {
return
[
new CraftMaterial
{
ID = BitConverter.ToUInt32(data.Slice(320, 4)), // 320 -> 324
Amount = BitConverter.ToUInt32(data.Slice(336, 4)) // 336 -> 340
},
new CraftMaterial
{
ID = BitConverter.ToUInt32(data.Slice(324, 4)), // 324 -> 328
Amount = BitConverter.ToUInt32(data.Slice(340, 4)) // 340 -> 344
},
new CraftMaterial
{
ID = BitConverter.ToUInt32(data.Slice(328, 4)), // 328 -> 332
Amount = BitConverter.ToUInt32(data.Slice(344, 4)) // 344 -> 348
},
new CraftMaterial
{
ID = BitConverter.ToUInt32(data.Slice(332, 4)), // 332 -> 336
Amount = BitConverter.ToUInt32(data.Slice(348, 4)) // 348 -> 352
}
];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ElementalStats ReadElementalStats(ref Span<byte> data) {
return MemoryMarshal.Cast<byte, ElementalStats>(data.Slice(140, 64))[0];
} }
} }

View file

@ -1,15 +1,12 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Wonderking.Game.Data.Item; using Wonderking.Game.Data.Item;
namespace Wonderking.Game.Reader; namespace Wonderking.Game.Reader;
public static class ItemReaderExtensions public static class ItemReaderExtensions {
{ public static Stats ReadStats(this BinaryReader reader) {
public static Stats ReadStats(this BinaryReader reader) return new Stats {
{
return new Stats
{
Strength = reader.ReadInt32(), //125 Strength = reader.ReadInt32(), //125
Dexterity = reader.ReadInt32(), //129 Dexterity = reader.ReadInt32(), //129
Intelligence = reader.ReadInt32(), //133 Intelligence = reader.ReadInt32(), //133
@ -19,10 +16,8 @@ public static class ItemReaderExtensions
}; };
} }
public static ElementalStats ReadElementalStats(this BinaryReader reader) public static ElementalStats ReadElementalStats(this BinaryReader reader) {
{ return new ElementalStats {
return new ElementalStats
{
MinimumFireDamage = reader.ReadInt32(), //149 MinimumFireDamage = reader.ReadInt32(), //149
MinimumWaterDamage = reader.ReadInt32(), //153 MinimumWaterDamage = reader.ReadInt32(), //153
MinimumDarkDamage = reader.ReadInt32(), //157 MinimumDarkDamage = reader.ReadInt32(), //157
@ -42,52 +37,44 @@ public static class ItemReaderExtensions
}; };
} }
public static ContainedItem[] ReadContainedItems(this BinaryReader reader) public static ContainedItem[] ReadContainedItems(this BinaryReader reader) {
{
var list = new ContainedItem[5]; var list = new ContainedItem[5];
//893 //893
for (var i = 0; i < 5; i++) for (var i = 0; i < 5; i++) {
{
list[i].ID = reader.ReadInt16(); list[i].ID = reader.ReadInt16();
} }
//903 //903
for (var i = 0; i < 5; i++) for (var i = 0; i < 5; i++) {
{
list[i].ObtainChance = reader.ReadSingle(); list[i].ObtainChance = reader.ReadSingle();
} }
return list; return list;
} }
public static CraftMaterial[] ReadCraftMaterial(this BinaryReader reader) public static CraftMaterial[] ReadCraftMaterial(this BinaryReader reader) {
{
var mats = new CraftMaterial[4]; var mats = new CraftMaterial[4];
//329 //329
for (var i = 0; i < 4; ++i) for (var i = 0; i < 4; ++i) {
{
mats[i].ID = reader.ReadUInt32(); mats[i].ID = reader.ReadUInt32();
} }
//345 //345
for (var i = 0; i < 4; ++i) for (var i = 0; i < 4; ++i) {
{
mats[i].ID = reader.ReadUInt32(); mats[i].ID = reader.ReadUInt32();
} }
return mats; return mats;
} }
public static ItemOptions ReadItemOptions(this BinaryReader reader) public static ItemOptions ReadItemOptions(this BinaryReader reader) {
{
var options = new ItemOptions(); var options = new ItemOptions();
options.OptionAvailable = reader.ReadInt32() == 1; //819 options.OptionAvailable = reader.ReadInt32() == 1; //819
var optionIDs = new List<uint>(4); var optionIDs = new List<uint>(4);
//823 //823
for (var i = 0; i < 3; i++) for (var i = 0; i < 3; i++) {
{
optionIDs.Add(reader.ReadUInt32()); optionIDs.Add(reader.ReadUInt32());
} }

View file

@ -1,12 +0,0 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License.
using JetBrains.Annotations;
namespace Wonderking.Packets;
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
public interface IPacket
{
public void Deserialize(byte[] data);
public byte[] Serialize();
}

View file

@ -1,21 +1,16 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using RaiNote.PacketMediator;
namespace Wonderking.Packets.Incoming; namespace Wonderking.Packets.Incoming;
[PacketId(OperationCode.ChannelSelection)] [WonderkingPacketId(OperationCode.ChannelSelection)]
public class ChannelSelectionPacket : IPacket public class ChannelSelectionPacket : IIncomingPacket {
{
public required ushort ServerId { get; set; } public required ushort ServerId { get; set; }
public required ushort ChannelId { get; set; } public required ushort ChannelId { get; set; }
public void Deserialize(byte[] data) public void Deserialize(byte[] data) {
{
ServerId = BitConverter.ToUInt16(data, 0); ServerId = BitConverter.ToUInt16(data, 0);
ChannelId = BitConverter.ToUInt16(data, 2); ChannelId = BitConverter.ToUInt16(data, 2);
} }
public byte[] Serialize()
{
throw new NotSupportedException();
}
} }

View file

@ -1,13 +1,13 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text; using System.Text;
using RaiNote.PacketMediator;
using Wonderking.Game.Data.Character; using Wonderking.Game.Data.Character;
namespace Wonderking.Packets.Incoming; namespace Wonderking.Packets.Incoming;
[PacketId(OperationCode.CharacterCreation)] [WonderkingPacketId(OperationCode.CharacterCreation)]
public class CharacterCreationPacket : IPacket public class CharacterCreationPacket : IIncomingPacket {
{
public required byte Slot { get; set; } public required byte Slot { get; set; }
public required byte Unknown { get; set; } public required byte Unknown { get; set; }
public required ushort Id { get; set; } public required ushort Id { get; set; }
@ -20,8 +20,7 @@ public class CharacterCreationPacket : IPacket
public required byte Shirt { get; set; } public required byte Shirt { get; set; }
public required byte Pants { get; set; } public required byte Pants { get; set; }
public void Deserialize(byte[] data) public void Deserialize(byte[] data) {
{
Slot = data[0]; Slot = data[0];
Unknown = data[1]; Unknown = data[1];
Id = BitConverter.ToUInt16(data, 2); Id = BitConverter.ToUInt16(data, 2);
@ -33,9 +32,4 @@ public class CharacterCreationPacket : IPacket
Shirt = data[28]; Shirt = data[28];
Pants = data[29]; Pants = data[29];
} }
public byte[] Serialize()
{
throw new NotSupportedException();
}
} }

View file

@ -1,26 +1,20 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text; using System.Text;
using RaiNote.PacketMediator;
namespace Wonderking.Packets.Incoming; namespace Wonderking.Packets.Incoming;
[PacketId(OperationCode.CharacterDeletion)] [WonderkingPacketId(OperationCode.CharacterDeletion)]
public class CharacterDeletePacket : IPacket public class CharacterDeletePacket : IIncomingPacket {
{ public required byte Slot { get; set; }
public byte Slot { get; set; } public required string Name { get; set; }
public string Name { get; set; } public required uint Unknown { get; set; }
public uint Unknown { get; set; }
public void Deserialize(byte[] data) public void Deserialize(byte[] data) {
{
Span<byte> span = data; Span<byte> span = data;
Slot = span[0]; Slot = span[0];
Name = Encoding.ASCII.GetString(span.Slice(1, 20)).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0'); Name = Encoding.ASCII.GetString(span.Slice(1, 20)).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
Unknown = BitConverter.ToUInt32(span.Slice(21, 4)); Unknown = BitConverter.ToUInt32(span.Slice(21, 4));
} }
public byte[] Serialize()
{
throw new NotSupportedException();
}
} }

View file

@ -1,21 +1,15 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text; using System.Text;
using RaiNote.PacketMediator;
namespace Wonderking.Packets.Incoming; namespace Wonderking.Packets.Incoming;
[PacketId(OperationCode.CharacterNameCheck)] [WonderkingPacketId(OperationCode.CharacterNameCheck)]
public class CharacterNameCheckPacket : IPacket public class CharacterNameCheckPacket : IIncomingPacket {
{
public required string Name { get; set; } public required string Name { get; set; }
public void Deserialize(byte[] data) public void Deserialize(byte[] data) {
{
Name = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0'); Name = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
} }
public byte[] Serialize()
{
throw new NotSupportedException();
}
} }

View file

@ -1,38 +1,19 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text; using System.Text;
using RaiNote.PacketMediator;
namespace Wonderking.Packets.Incoming; namespace Wonderking.Packets.Incoming;
[PacketId(OperationCode.LoginInfo)] [WonderkingPacketId(OperationCode.LoginInfo)]
public class LoginInfoPacket : IPacket public class LoginInfoPacket : IIncomingPacket {
{
public required string Username { get; set; } public required string Username { get; set; }
public required string Password { get; set; } public required string Password { get; set; }
public void Deserialize(byte[] data) public void Deserialize(byte[] data) {
{
Username = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0'); Username = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
// Remove unnecessary Symbols // Remove unnecessary Symbols
Password = Encoding.ASCII.GetString(data, 20, 31).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0'); Password = Encoding.ASCII.GetString(data, 20, 31).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
} }
public byte[] Serialize()
{
Span<byte> dataSpan = stackalloc byte[20 + 31];
var usernameBytes = Encoding.ASCII.GetBytes(Username);
var passwordBytes = Encoding.ASCII.GetBytes(Password);
for (var i = 0; i < 20 || i < Username.Length; i++)
{
dataSpan[i] = usernameBytes[i];
}
for (var i = 0; i < 31 || i < Password.Length; i++)
{
dataSpan[20 + i] = passwordBytes[i];
}
return dataSpan.ToArray();
}
} }

View file

@ -1,9 +1,8 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
namespace Wonderking.Packets; namespace Wonderking.Packets;
public enum OperationCode : ushort public enum OperationCode : ushort {
{
LoginInfo = 11, LoginInfo = 11,
LoginResponse = 12, LoginResponse = 12,
ChannelSelection = 13, ChannelSelection = 13,

View file

@ -1,26 +1,20 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Text; using System.Text;
using RaiNote.PacketMediator;
using Wonderking.Packets.Outgoing.Data; using Wonderking.Packets.Outgoing.Data;
namespace Wonderking.Packets.Outgoing; namespace Wonderking.Packets.Outgoing;
[PacketId(OperationCode.ChannelSelectionResponse)] [WonderkingPacketId(OperationCode.ChannelSelectionResponse)]
public class ChannelSelectionResponsePacket : IPacket public class ChannelSelectionResponsePacket : IOutgoingPacket {
{
public required byte ChannelIsFullFlag { get; set; } public required byte ChannelIsFullFlag { get; set; }
public required string Endpoint { get; set; } public required string Endpoint { get; set; }
public required ushort Port { get; set; } public required ushort Port { get; set; }
public required CharacterData[] Characters { get; set; } public required CharacterData[] Characters { get; set; }
public void Deserialize(byte[] data) public byte[] Serialize() {
{
throw new NotSupportedException();
}
public byte[] Serialize()
{
Span<byte> data = stackalloc byte[1 + 16 + 2 + 1 + 132 * Characters.Length]; Span<byte> data = stackalloc byte[1 + 16 + 2 + 1 + 132 * Characters.Length];
data.Clear(); data.Clear();
data[0] = ChannelIsFullFlag; data[0] = ChannelIsFullFlag;
@ -29,8 +23,7 @@ public class ChannelSelectionResponsePacket : IPacket
data[19] = (byte)Characters.Length; data[19] = (byte)Characters.Length;
// Character Data // Character Data
for (var i = 0; i < Characters.Length; i++) for (var i = 0; i < Characters.Length; i++) {
{
var offset = 20 + i * 132; var offset = 20 + i * 132;
var character = Characters[i]; var character = Characters[i];
// Character Data // Character Data
@ -58,8 +51,7 @@ public class ChannelSelectionResponsePacket : IPacket
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset + 44, 4), character.Health); BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset + 44, 4), character.Health);
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset + 48, 4), character.Mana); BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset + 48, 4), character.Mana);
for (var j = 0; j < 20; j++) for (var j = 0; j < 20; j++) {
{
// Equipped Items // Equipped Items
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(offset + 52 + j * 2, 2), BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(offset + 52 + j * 2, 2),
character.EquippedItems.Length > j ? character.EquippedItems[j] : (ushort)0); character.EquippedItems.Length > j ? character.EquippedItems[j] : (ushort)0);

View file

@ -1,25 +1,19 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Text; using System.Text;
using RaiNote.PacketMediator;
using Wonderking.Packets.Outgoing.Data; using Wonderking.Packets.Outgoing.Data;
namespace Wonderking.Packets.Outgoing; namespace Wonderking.Packets.Outgoing;
[PacketId(OperationCode.CharacterCreationResponse)] [WonderkingPacketId(OperationCode.CharacterCreationResponse)]
public class CharacterCreationResponsePacket : IPacket public class CharacterCreationResponsePacket : IOutgoingPacket {
{
public required CharacterData Character { get; set; } public required CharacterData Character { get; set; }
public required int Slot { get; set; } public required int Slot { get; set; }
public required bool isDuplicate { get; set; } public required bool isDuplicate { get; set; }
public void Deserialize(byte[] data) public byte[] Serialize() {
{
throw new NotSupportedException();
}
public byte[] Serialize()
{
Span<byte> data = stackalloc byte[1 + 132]; Span<byte> data = stackalloc byte[1 + 132];
data[0] = isDuplicate ? (byte)1 : (byte)0; data[0] = isDuplicate ? (byte)1 : (byte)0;
@ -48,8 +42,7 @@ public class CharacterCreationResponsePacket : IPacket
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(45, 4), Character.Health); BinaryPrimitives.WriteInt32LittleEndian(data.Slice(45, 4), Character.Health);
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(49, 4), Character.Mana); BinaryPrimitives.WriteInt32LittleEndian(data.Slice(49, 4), Character.Mana);
for (var i = 0; i < 20; i++) for (var i = 0; i < 20; i++) {
{
// Equipped Items // Equipped Items
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(53 + i * 2, 2), BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(53 + i * 2, 2),
Character.EquippedItems.Length > i ? Character.EquippedItems[i] : (ushort)0); Character.EquippedItems.Length > i ? Character.EquippedItems[i] : (ushort)0);

View file

@ -1,21 +1,14 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using NotSupportedException = System.NotSupportedException; using RaiNote.PacketMediator;
namespace Wonderking.Packets.Outgoing; namespace Wonderking.Packets.Outgoing;
[PacketId(OperationCode.CharacterDeletionResponse)] [WonderkingPacketId(OperationCode.CharacterDeletionResponse)]
public class CharacterDeleteResponsePacket : IPacket public class CharacterDeleteResponsePacket : IOutgoingPacket {
{
public required byte HasToBeZero { get; set; } public required byte HasToBeZero { get; set; }
public void Deserialize(byte[] data) public byte[] Serialize() {
{
throw new NotSupportedException();
}
public byte[] Serialize()
{
Span<byte> data = stackalloc byte[1]; Span<byte> data = stackalloc byte[1];
data[0] = HasToBeZero; data[0] = HasToBeZero;
return data.ToArray(); return data.ToArray();

View file

@ -1,19 +1,14 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using RaiNote.PacketMediator;
namespace Wonderking.Packets.Outgoing; namespace Wonderking.Packets.Outgoing;
[PacketId(OperationCode.CharacterNameCheckResponse)] [WonderkingPacketId(OperationCode.CharacterNameCheckResponse)]
public class CharacterNameCheckPacketResponse : IPacket public class CharacterNameCheckPacketResponse : IOutgoingPacket {
{
public required bool IsTaken { get; set; } public required bool IsTaken { get; set; }
public void Deserialize(byte[] data) public byte[] Serialize() {
{
throw new NotSupportedException();
}
public byte[] Serialize()
{
Span<byte> data = stackalloc byte[1]; Span<byte> data = stackalloc byte[1];
data[0] = IsTaken ? (byte)1 : (byte)0; data[0] = IsTaken ? (byte)1 : (byte)0;
return data.ToArray(); return data.ToArray();

View file

@ -1,26 +1,19 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text; using System.Text;
using RaiNote.PacketMediator;
namespace Wonderking.Packets.Outgoing; namespace Wonderking.Packets.Outgoing;
[PacketId(OperationCode.CharacterSelectionSetGuildName)] [WonderkingPacketId(OperationCode.CharacterSelectionSetGuildName)]
public class CharacterSelectionSetGuildNamePacket : IPacket public class CharacterSelectionSetGuildNamePacket : IOutgoingPacket {
{
public required string[] GuildNames { get; set; } public required string[] GuildNames { get; set; }
public void Deserialize(byte[] data) public byte[] Serialize() {
{
throw new NotSupportedException();
}
public byte[] Serialize()
{
Span<byte> data = stackalloc byte[1 + (1 + 16 + 1) * GuildNames.Length]; Span<byte> data = stackalloc byte[1 + (1 + 16 + 1) * GuildNames.Length];
data.Clear(); data.Clear();
data[0] = (byte)GuildNames.Length; data[0] = (byte)GuildNames.Length;
for (var i = 0; i < GuildNames.Length; i++) for (var i = 0; i < GuildNames.Length; i++) {
{
data[1 + i * (1 + 16 + 1)] = (byte)i; data[1 + i * (1 + 16 + 1)] = (byte)i;
Encoding.ASCII.GetBytes(GuildNames[i], data.Slice(2 + i * (1 + 16 + 1), 16)); Encoding.ASCII.GetBytes(GuildNames[i], data.Slice(2 + i * (1 + 16 + 1), 16));
// Null terminator // Null terminator

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -8,8 +8,7 @@ namespace Wonderking.Packets.Outgoing.Data;
[UsedImplicitly] [UsedImplicitly]
[Owned] [Owned]
public class BaseStats public class BaseStats {
{
[JsonPropertyName("strength")] public required short Strength { get; set; } [JsonPropertyName("strength")] public required short Strength { get; set; }
[JsonPropertyName("dexterity")] public required short Dexterity { get; set; } [JsonPropertyName("dexterity")] public required short Dexterity { get; set; }
[JsonPropertyName("intelligence")] public required short Intelligence { get; set; } [JsonPropertyName("intelligence")] public required short Intelligence { get; set; }

View file

@ -1,11 +1,10 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using Wonderking.Game.Data.Character; using Wonderking.Game.Data.Character;
namespace Wonderking.Packets.Outgoing.Data; namespace Wonderking.Packets.Outgoing.Data;
public struct CharacterData public struct CharacterData {
{
public required string Name { get; set; } public required string Name { get; set; }
public required JobData Job { get; set; } public required JobData Job { get; set; }
public required Gender Gender { get; set; } public required Gender Gender { get; set; }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -7,8 +7,7 @@ namespace Wonderking.Packets.Outgoing.Data;
[UsedImplicitly] [UsedImplicitly]
[Owned] [Owned]
public class JobData public class JobData {
{
public required byte FirstJob { get; set; } public required byte FirstJob { get; set; }
public required byte SecondJob { get; set; } public required byte SecondJob { get; set; }
public required byte ThirdJob { get; set; } public required byte ThirdJob { get; set; }

View file

@ -1,9 +1,8 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
namespace Wonderking.Packets.Outgoing.Data; namespace Wonderking.Packets.Outgoing.Data;
public enum LoginResponseReason : byte public enum LoginResponseReason : byte {
{
Ok, Ok,
AccountDoesNotExit, AccountDoesNotExit,
WrongPassword, WrongPassword,

View file

@ -1,12 +1,11 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Wonderking.Packets.Outgoing.Data; namespace Wonderking.Packets.Outgoing.Data;
[StructLayout(LayoutKind.Auto)] [StructLayout(LayoutKind.Auto)]
public struct ServerChannelData public struct ServerChannelData {
{
public ushort ServerId { get; set; } public ushort ServerId { get; set; }
public ushort ChannelId { get; set; } public ushort ChannelId { get; set; }
public byte LoadPercentage { get; set; } public byte LoadPercentage { get; set; }

View file

@ -1,36 +1,20 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License. // Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using System.Buffers.Binary; using System.Buffers.Binary;
using RaiNote.PacketMediator;
using Wonderking.Packets.Outgoing.Data; using Wonderking.Packets.Outgoing.Data;
namespace Wonderking.Packets.Outgoing; namespace Wonderking.Packets.Outgoing;
[PacketId(OperationCode.LoginResponse)] [WonderkingPacketId(OperationCode.LoginResponse)]
public class LoginResponsePacket : IPacket public class LoginResponsePacket : IOutgoingPacket {
{
public required LoginResponseReason ResponseReason { get; set; } public required LoginResponseReason ResponseReason { get; set; }
public required byte UnknownFlag { get; set; } = 1; public required byte UnknownFlag { get; set; } = 1;
public required bool IsGameMaster { get; set; } public required bool IsGameMaster { get; set; }
public required ServerChannelData[] ChannelData { get; set; } public required ServerChannelData[] ChannelData { get; set; }
public void Deserialize(byte[] data) public byte[] Serialize() {
{
ResponseReason = (LoginResponseReason)data[0];
UnknownFlag = data[1];
IsGameMaster = BitConverter.ToBoolean(data, 2);
var channelAmount = BitConverter.ToUInt16(data, 3);
const int sizeOfServerChannelData = 5;
ChannelData = Enumerable.Repeat(0, channelAmount).Select(i => new ServerChannelData
{
ServerId = BitConverter.ToUInt16(data, 5 + 0 + i * sizeOfServerChannelData),
ChannelId = BitConverter.ToUInt16(data, 5 + 2 + i * sizeOfServerChannelData),
LoadPercentage = data[5 + 4 + i * sizeOfServerChannelData]
}).ToArray();
}
public byte[] Serialize()
{
const int sizeOfServerChannelData = 5; const int sizeOfServerChannelData = 5;
Span<byte> dataSpan = stackalloc byte[5 + ChannelData.Length * sizeOfServerChannelData]; Span<byte> dataSpan = stackalloc byte[5 + ChannelData.Length * sizeOfServerChannelData];
dataSpan.Clear(); dataSpan.Clear();
@ -39,8 +23,7 @@ public class LoginResponsePacket : IPacket
dataSpan[2] = BitConverter.GetBytes(IsGameMaster)[0]; dataSpan[2] = BitConverter.GetBytes(IsGameMaster)[0];
BinaryPrimitives.WriteUInt16LittleEndian(dataSpan.Slice(3, 2), (ushort)ChannelData.Length); BinaryPrimitives.WriteUInt16LittleEndian(dataSpan.Slice(3, 2), (ushort)ChannelData.Length);
for (var i = 0; i < ChannelData.Length; i++) for (var i = 0; i < ChannelData.Length; i++) {
{
var bytesOfServerId = BitConverter.GetBytes(ChannelData[i].ServerId); var bytesOfServerId = BitConverter.GetBytes(ChannelData[i].ServerId);
var bytesOfChannelId = BitConverter.GetBytes(ChannelData[i].ChannelId); var bytesOfChannelId = BitConverter.GetBytes(ChannelData[i].ChannelId);
dataSpan[5 + 0 + i * sizeOfServerChannelData] = bytesOfServerId[0]; dataSpan[5 + 0 + i * sizeOfServerChannelData] = bytesOfServerId[0];

View file

@ -1,14 +0,0 @@
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License.
namespace Wonderking.Packets;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class PacketIdAttribute : Attribute
{
public PacketIdAttribute(OperationCode code)
{
Code = code;
}
public OperationCode Code { get; }
}

View file

@ -0,0 +1,11 @@
// Licensed to Timothy Schenk under the GNU AGPL Version 3 License.
using RaiNote.PacketMediator;
namespace Wonderking.Packets;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class WonderkingPacketIdAttribute : PacketIdAttribute<OperationCode> {
public WonderkingPacketIdAttribute(OperationCode code) : base(code) {
}
}

Some files were not shown because too many files have changed in this diff Show more