Merge pull request 'feature/72-channelselection-and-character-creation' (#82) from feature/72-channelselection-and-character-creation into master
All checks were successful
Build, Package and Push Images / preprocess (push) Successful in 4s
Build, Package and Push Images / build (push) Successful in 27s
Build, Package and Push Images / sbom-scan (push) Successful in 42s
Build, Package and Push Images / sonarqube (push) Successful in 1m44s
Build, Package and Push Images / container-build (push) Successful in 2m1s
Build, Package and Push Images / container-sbom-scan (push) Successful in 48s
All checks were successful
Build, Package and Push Images / preprocess (push) Successful in 4s
Build, Package and Push Images / build (push) Successful in 27s
Build, Package and Push Images / sbom-scan (push) Successful in 42s
Build, Package and Push Images / sonarqube (push) Successful in 1m44s
Build, Package and Push Images / container-build (push) Successful in 2m1s
Build, Package and Push Images / container-sbom-scan (push) Successful in 48s
Reviewed-on: #82
This commit is contained in:
commit
0817873eb1
55 changed files with 2564 additions and 73 deletions
|
@ -28,6 +28,8 @@ insert_final_newline = true
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
dotnet_sort_system_directives_first = true
|
dotnet_sort_system_directives_first = true
|
||||||
|
|
||||||
|
MA0004.report = DetectContext # (default) Try to detect the current context and report only if it considers ConfigureAwait is needed
|
||||||
|
MA0004.report = Always # Always report missing ConfigureAwait whatever the context
|
||||||
# Don't use this. qualifier
|
# Don't use this. qualifier
|
||||||
dotnet_style_qualification_for_field = false:suggestion
|
dotnet_style_qualification_for_field = false:suggestion
|
||||||
dotnet_style_qualification_for_property = false:suggestion
|
dotnet_style_qualification_for_property = false:suggestion
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -482,3 +482,5 @@ $RECYCLE.BIN/
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
.nuke
|
.nuke
|
||||||
|
|
||||||
|
wk-data
|
||||||
|
|
|
@ -4,13 +4,21 @@
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>12</LangVersion>
|
||||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AsyncFixer" Version="1.6.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
|
||||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.104">
|
<PackageReference Include="Meziantou.Analyzer" Version="2.0.109">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.7.30">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|
|
@ -2,30 +2,26 @@ namespace Benchmarks;
|
||||||
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using BenchmarkDotNet.Order;
|
using BenchmarkDotNet.Order;
|
||||||
|
|
||||||
[SimpleJob(RuntimeMoniker.Net80)]
|
|
||||||
[SimpleJob(RuntimeMoniker.Net70)]
|
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
|
||||||
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
||||||
[RankColumn]
|
[Config(typeof(GenericConfig))]
|
||||||
[MemoryDiagnoser]
|
|
||||||
[ThreadingDiagnoser]
|
|
||||||
public class BinaryConversionBenchmarks
|
public class BinaryConversionBenchmarks
|
||||||
{
|
{
|
||||||
private byte[] _data = null!;
|
private byte[] _data = null!;
|
||||||
private int _offset;
|
private int _offset;
|
||||||
|
private int _writeBuffer;
|
||||||
|
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
this._data = RandomNumberGenerator.GetBytes(4000);
|
this._data = RandomNumberGenerator.GetBytes(4000);
|
||||||
this._offset = RandomNumberGenerator.GetInt32(0, 3500);
|
this._offset = RandomNumberGenerator.GetInt32(0, 3500);
|
||||||
|
this._writeBuffer = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public short BitConverterTest() => BitConverter.ToInt16(this._data, this._offset);
|
public short BitConverterParseTest() => BitConverter.ToInt16(this._data, this._offset);
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public short BinaryReader()
|
public short BinaryReader()
|
||||||
|
@ -37,7 +33,30 @@ public class BinaryConversionBenchmarks
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public short BinaryPrimitives() =>
|
public short BinaryPrimitivesRead() =>
|
||||||
System.Buffers.Binary.BinaryPrimitives.ReadInt16LittleEndian(
|
System.Buffers.Binary.BinaryPrimitives.ReadInt16LittleEndian(
|
||||||
new ArraySegment<byte>(this._data, this._offset, sizeof(short)));
|
new ArraySegment<byte>(this._data, this._offset, sizeof(short)));
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void BinaryPrimitivesWrite()
|
||||||
|
{
|
||||||
|
System.Buffers.Binary.BinaryPrimitives.WriteInt32LittleEndian(this._data.AsSpan(_offset, 4),
|
||||||
|
this._writeBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void BitConverterCopy()
|
||||||
|
{
|
||||||
|
BitConverter.GetBytes(this._writeBuffer).CopyTo(this._data, this._offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void BitConverterAssignment()
|
||||||
|
{
|
||||||
|
var bytes = BitConverter.GetBytes(this._writeBuffer);
|
||||||
|
this._data[this._offset] = bytes[0];
|
||||||
|
this._data[this._offset + 1] = bytes[1];
|
||||||
|
this._data[this._offset + 2] = bytes[2];
|
||||||
|
this._data[this._offset + 3] = bytes[3];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
85
Benchmarks/DataCacheBenchmark.cs
Normal file
85
Benchmarks/DataCacheBenchmark.cs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace Benchmarks;
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Order;
|
||||||
|
|
||||||
|
[Config(typeof(GenericConfig))]
|
||||||
|
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
||||||
|
public class DataCacheBenchmark
|
||||||
|
{
|
||||||
|
[Params(1000, 100000, 1000000)] public int N;
|
||||||
|
private HashSet<int> _hashSet;
|
||||||
|
private Dictionary<int, int> _dictionary;
|
||||||
|
private ConcurrentDictionary<int, int> _concurrentDictionary;
|
||||||
|
private ImmutableHashSet<int> _immutableHashSet;
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_hashSet = new HashSet<int>();
|
||||||
|
_dictionary = new Dictionary<int, int>();
|
||||||
|
_concurrentDictionary = new ConcurrentDictionary<int, int>();
|
||||||
|
_immutableHashSet = ImmutableHashSet<int>.Empty;
|
||||||
|
|
||||||
|
_hashSet.Clear();
|
||||||
|
_dictionary.Clear();
|
||||||
|
_concurrentDictionary.Clear();
|
||||||
|
_immutableHashSet = _immutableHashSet.Clear();
|
||||||
|
_hashSet.EnsureCapacity(N);
|
||||||
|
_dictionary.EnsureCapacity(N);
|
||||||
|
|
||||||
|
for (var i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
_immutableHashSet = _immutableHashSet.Add(i);
|
||||||
|
_hashSet.Add(i);
|
||||||
|
_dictionary.Add(i, i);
|
||||||
|
_concurrentDictionary.TryAdd(i, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void HashSetAdd()
|
||||||
|
{
|
||||||
|
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _hashSet.Add(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void DictionaryAdd()
|
||||||
|
{
|
||||||
|
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _dictionary.Add(N + i, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void ConcurrentDictionaryAddOrUpdate()
|
||||||
|
{
|
||||||
|
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i =>
|
||||||
|
_concurrentDictionary.AddOrUpdate(N + i, i, (key, oldValue) => oldValue + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void ImmutableHashSetLookup()
|
||||||
|
{
|
||||||
|
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _immutableHashSet.Contains(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void HashSetLookup()
|
||||||
|
{
|
||||||
|
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _hashSet.Contains(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void DictionaryLookup()
|
||||||
|
{
|
||||||
|
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _dictionary.ContainsKey(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void ConcurrentDictionaryLookup()
|
||||||
|
{
|
||||||
|
ParallelEnumerable.Range(0, N).AsParallel().ForAll(i => _concurrentDictionary.ContainsKey(i));
|
||||||
|
}
|
||||||
|
}
|
31
Benchmarks/GenericConfig.cs
Normal file
31
Benchmarks/GenericConfig.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using BenchmarkDotNet.Analysers;
|
||||||
|
using BenchmarkDotNet.Columns;
|
||||||
|
using BenchmarkDotNet.Configs;
|
||||||
|
using BenchmarkDotNet.Diagnosers;
|
||||||
|
using BenchmarkDotNet.Environments;
|
||||||
|
using BenchmarkDotNet.Exporters;
|
||||||
|
using BenchmarkDotNet.Exporters.Csv;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|
||||||
|
namespace Benchmarks;
|
||||||
|
|
||||||
|
public class GenericConfig : ManualConfig
|
||||||
|
{
|
||||||
|
public GenericConfig()
|
||||||
|
{
|
||||||
|
AddJob(Job.Default
|
||||||
|
.WithRuntime(CoreRuntime.Core80))
|
||||||
|
.AddDiagnoser(ThreadingDiagnoser.Default, MemoryDiagnoser.Default,
|
||||||
|
EventPipeProfiler.Default)
|
||||||
|
.AddAnalyser(MinIterationTimeAnalyser.Default, OutliersAnalyser.Default,
|
||||||
|
RuntimeErrorAnalyser.Default, EnvironmentAnalyser.Default)
|
||||||
|
.AddColumn(RankColumn.Arabic).AddExporter(CsvExporter.Default, MarkdownExporter.Default);
|
||||||
|
AddJob(Job.Default
|
||||||
|
.WithRuntime(CoreRuntime.Core70))
|
||||||
|
.AddDiagnoser(ThreadingDiagnoser.Default, MemoryDiagnoser.Default,
|
||||||
|
EventPipeProfiler.Default)
|
||||||
|
.AddAnalyser(MinIterationTimeAnalyser.Default, OutliersAnalyser.Default,
|
||||||
|
RuntimeErrorAnalyser.Default, EnvironmentAnalyser.Default)
|
||||||
|
.AddColumn(RankColumn.Arabic).AddExporter(CsvExporter.Default, MarkdownExporter.Default);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
using Wonderking.Game.Data.Character;
|
||||||
|
using Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
namespace Server.DB.Documents;
|
namespace Server.DB.Documents;
|
||||||
|
|
||||||
public class Character
|
public class Character
|
||||||
|
@ -14,4 +17,13 @@ public class Character
|
||||||
public Gender Gender { get; set; }
|
public Gender Gender { get; set; }
|
||||||
public long Experience { get; set; }
|
public long Experience { get; set; }
|
||||||
public byte Level { get; set; }
|
public byte Level { get; set; }
|
||||||
|
public ICollection<InventoryItem> InventoryItems { get; set; }
|
||||||
|
|
||||||
|
public BaseStats BaseStats { get; set; }
|
||||||
|
|
||||||
|
public JobData JobData { get; set; }
|
||||||
|
public int Health { get; set; }
|
||||||
|
public int Mana { get; set; }
|
||||||
|
public Guid GuildId { get; set; }
|
||||||
|
public Guild Guild { get; set; }
|
||||||
}
|
}
|
||||||
|
|
9
Server/DB/Documents/Guild.cs
Normal file
9
Server/DB/Documents/Guild.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Server.DB.Documents;
|
||||||
|
|
||||||
|
public class Guild
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Notice { get; set; }
|
||||||
|
public ICollection<GuildMember> GuildMembers { get; set; }
|
||||||
|
}
|
11
Server/DB/Documents/GuildMember.cs
Normal file
11
Server/DB/Documents/GuildMember.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Server.DB.Documents;
|
||||||
|
|
||||||
|
public class GuildMember
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid CharacterId { get; set; }
|
||||||
|
public Character Character { get; set; }
|
||||||
|
public Guid GuildId { get; set; }
|
||||||
|
public Guild Guild { get; set; }
|
||||||
|
public GuildRank Rank { get; set; }
|
||||||
|
}
|
11
Server/DB/Documents/GuildRank.cs
Normal file
11
Server/DB/Documents/GuildRank.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Server.DB.Documents;
|
||||||
|
|
||||||
|
public enum GuildRank : byte
|
||||||
|
{
|
||||||
|
Initiate = 0,
|
||||||
|
Member = 1,
|
||||||
|
Veteran = 2,
|
||||||
|
Elite = 3,
|
||||||
|
Officer = 4,
|
||||||
|
Master = 5
|
||||||
|
}
|
20
Server/DB/Documents/InventoryItem.cs
Normal file
20
Server/DB/Documents/InventoryItem.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
namespace Server.DB.Documents;
|
||||||
|
|
||||||
|
public class InventoryItem
|
||||||
|
{
|
||||||
|
public Guid CharacterId { get; set; }
|
||||||
|
public Character Character { get; set; }
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public ushort ItemId { get; set; }
|
||||||
|
public ushort Count { get; set; }
|
||||||
|
public byte Slot { get; set; }
|
||||||
|
public ItemType ItemType { get; set; }
|
||||||
|
public byte Level { get; set; }
|
||||||
|
public byte Rarity { get; set; }
|
||||||
|
public byte AddOption { get; set; }
|
||||||
|
public byte AddOption2 { get; set; }
|
||||||
|
public byte AddOption3 { get; set; }
|
||||||
|
public short Option { get; set; }
|
||||||
|
public short Option2 { get; set; }
|
||||||
|
public short Option3 { get; set; }
|
||||||
|
}
|
12
Server/DB/Documents/ItemType.cs
Normal file
12
Server/DB/Documents/ItemType.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Server.DB.Documents;
|
||||||
|
|
||||||
|
public enum ItemType : byte
|
||||||
|
{
|
||||||
|
WornEquipment = 0,
|
||||||
|
WornCashEquipment = 1,
|
||||||
|
Equipment = 2,
|
||||||
|
Etc = 3,
|
||||||
|
Cash = 4,
|
||||||
|
Warehouse = 5,
|
||||||
|
GiftBox = 6
|
||||||
|
}
|
183
Server/DB/Migrations/20231108143729_AddInventoryToCharacter.Designer.cs
generated
Normal file
183
Server/DB/Migrations/20231108143729_AddInventoryToCharacter.Designer.cs
generated
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Server.DB;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Server.DB.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(WonderkingContext))]
|
||||||
|
[Migration("20231108143729_AddInventoryToCharacter")]
|
||||||
|
partial class AddInventoryToCharacter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "7.0.13")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<byte[]>("Password")
|
||||||
|
.HasColumnType("bytea");
|
||||||
|
|
||||||
|
b.Property<byte>("PermissionLevel")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte[]>("Salt")
|
||||||
|
.HasColumnType("bytea");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Accounts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<long>("Experience")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<byte>("Gender")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("LastXCoordinate")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("LastYCoordinate")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Level")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<int>("MapId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.Property<byte>("PvPLevel")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("ServerId")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
|
b.ToTable("Characters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<short>("ItemId")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("ItemType")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Level")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Rarity")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Slot")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.ToTable("InventoryItem");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||||
|
.WithMany("Characters")
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Account");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||||
|
.WithMany("InventoryItems")
|
||||||
|
.HasForeignKey("CharacterId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Character");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Characters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("InventoryItems");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Server.DB.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddInventoryToCharacter : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "InventoryItem",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
CharacterId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ItemId = table.Column<short>(type: "smallint", nullable: false),
|
||||||
|
Count = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
Slot = table.Column<byte>(type: "smallint", nullable: false),
|
||||||
|
ItemType = table.Column<byte>(type: "smallint", nullable: false),
|
||||||
|
Level = table.Column<byte>(type: "smallint", nullable: false),
|
||||||
|
Rarity = table.Column<byte>(type: "smallint", nullable: false),
|
||||||
|
AddOption = table.Column<byte>(type: "smallint", nullable: false),
|
||||||
|
AddOption2 = table.Column<byte>(type: "smallint", nullable: false),
|
||||||
|
AddOption3 = table.Column<byte>(type: "smallint", nullable: false),
|
||||||
|
Option = table.Column<short>(type: "smallint", nullable: false),
|
||||||
|
Option2 = table.Column<short>(type: "smallint", nullable: false),
|
||||||
|
Option3 = table.Column<short>(type: "smallint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_InventoryItem", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_InventoryItem_Characters_CharacterId",
|
||||||
|
column: x => x.CharacterId,
|
||||||
|
principalTable: "Characters",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_InventoryItem_CharacterId",
|
||||||
|
table: "InventoryItem",
|
||||||
|
column: "CharacterId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "InventoryItem");
|
||||||
|
}
|
||||||
|
}
|
249
Server/DB/Migrations/20231113192405_AdditionalCharacterData.Designer.cs
generated
Normal file
249
Server/DB/Migrations/20231113192405_AdditionalCharacterData.Designer.cs
generated
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Server.DB;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Server.DB.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(WonderkingContext))]
|
||||||
|
[Migration("20231113192405_AdditionalCharacterData")]
|
||||||
|
partial class AdditionalCharacterData
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "7.0.13")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<byte[]>("Password")
|
||||||
|
.HasColumnType("bytea");
|
||||||
|
|
||||||
|
b.Property<byte>("PermissionLevel")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte[]>("Salt")
|
||||||
|
.HasColumnType("bytea");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Accounts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<long>("Experience")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<byte>("Gender")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<int>("Health")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<short>("LastXCoordinate")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("LastYCoordinate")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Level")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<int>("Mana")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("MapId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.Property<byte>("PvPLevel")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("ServerId")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
|
b.ToTable("Characters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("ItemId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<byte>("ItemType")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Level")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Rarity")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Slot")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.ToTable("InventoryItem");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||||
|
.WithMany("Characters")
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.OwnsOne("Wonderking.Packets.Outgoing.BaseStats", "BaseStats", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b1.Property<short>("Dexterity")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Intelligence")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Luck")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Strength")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Vitality")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Wisdom")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.HasKey("CharacterId");
|
||||||
|
|
||||||
|
b1.ToTable("Characters");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("CharacterId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.OwnsOne("Wonderking.Packets.Outgoing.JobData", "JobData", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b1.Property<byte>("FirstJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<byte>("FourthJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<byte>("SecondJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<byte>("ThirdJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.HasKey("CharacterId");
|
||||||
|
|
||||||
|
b1.ToTable("Characters");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("CharacterId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("Account");
|
||||||
|
|
||||||
|
b.Navigation("BaseStats");
|
||||||
|
|
||||||
|
b.Navigation("JobData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||||
|
.WithMany("InventoryItems")
|
||||||
|
.HasForeignKey("CharacterId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Character");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Characters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("InventoryItems");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
Server/DB/Migrations/20231113192405_AdditionalCharacterData.cs
Normal file
155
Server/DB/Migrations/20231113192405_AdditionalCharacterData.cs
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Server.DB.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AdditionalCharacterData : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "ItemId",
|
||||||
|
table: "InventoryItem",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(short),
|
||||||
|
oldType: "smallint");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<short>(
|
||||||
|
name: "BaseStats_Dexterity",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<short>(
|
||||||
|
name: "BaseStats_Intelligence",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<short>(
|
||||||
|
name: "BaseStats_Luck",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<short>(
|
||||||
|
name: "BaseStats_Strength",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<short>(
|
||||||
|
name: "BaseStats_Vitality",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<short>(
|
||||||
|
name: "BaseStats_Wisdom",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Health",
|
||||||
|
table: "Characters",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<byte>(
|
||||||
|
name: "JobData_FirstJob",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<byte>(
|
||||||
|
name: "JobData_FourthJob",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<byte>(
|
||||||
|
name: "JobData_SecondJob",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<byte>(
|
||||||
|
name: "JobData_ThirdJob",
|
||||||
|
table: "Characters",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Mana",
|
||||||
|
table: "Characters",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BaseStats_Dexterity",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BaseStats_Intelligence",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BaseStats_Luck",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BaseStats_Strength",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BaseStats_Vitality",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BaseStats_Wisdom",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Health",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "JobData_FirstJob",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "JobData_FourthJob",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "JobData_SecondJob",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "JobData_ThirdJob",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Mana",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<short>(
|
||||||
|
name: "ItemId",
|
||||||
|
table: "InventoryItem",
|
||||||
|
type: "smallint",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "integer");
|
||||||
|
}
|
||||||
|
}
|
327
Server/DB/Migrations/20231114184404_AddGuildData.Designer.cs
generated
Normal file
327
Server/DB/Migrations/20231114184404_AddGuildData.Designer.cs
generated
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Server.DB;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Server.DB.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(WonderkingContext))]
|
||||||
|
[Migration("20231114184404_AddGuildData")]
|
||||||
|
partial class AddGuildData
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "7.0.13")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<byte[]>("Password")
|
||||||
|
.HasColumnType("bytea");
|
||||||
|
|
||||||
|
b.Property<byte>("PermissionLevel")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte[]>("Salt")
|
||||||
|
.HasColumnType("bytea");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Accounts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<long>("Experience")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<byte>("Gender")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<Guid>("GuildId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("Health")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<short>("LastXCoordinate")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("LastYCoordinate")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Level")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<int>("Mana")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("MapId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("varchar(20)");
|
||||||
|
|
||||||
|
b.Property<byte>("PvPLevel")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("ServerId")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId");
|
||||||
|
|
||||||
|
b.ToTable("Characters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Notice")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Guild");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("GuildId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<byte>("Rank")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId");
|
||||||
|
|
||||||
|
b.ToTable("GuildMember");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("ItemId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<byte>("ItemType")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Level")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Rarity")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Slot")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.ToTable("InventoryItem");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||||
|
.WithMany("Characters")
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GuildId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.OwnsOne("Wonderking.Packets.Outgoing.BaseStats", "BaseStats", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b1.Property<short>("Dexterity")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Intelligence")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Luck")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Strength")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Vitality")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<short>("Wisdom")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.HasKey("CharacterId");
|
||||||
|
|
||||||
|
b1.ToTable("Characters");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("CharacterId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.OwnsOne("Wonderking.Packets.Outgoing.JobData", "JobData", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b1.Property<byte>("FirstJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<byte>("FourthJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<byte>("SecondJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.Property<byte>("ThirdJob")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b1.HasKey("CharacterId");
|
||||||
|
|
||||||
|
b1.ToTable("Characters");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("CharacterId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("Account");
|
||||||
|
|
||||||
|
b.Navigation("BaseStats");
|
||||||
|
|
||||||
|
b.Navigation("Guild");
|
||||||
|
|
||||||
|
b.Navigation("JobData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CharacterId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||||
|
.WithMany("GuildMembers")
|
||||||
|
.HasForeignKey("GuildId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Character");
|
||||||
|
|
||||||
|
b.Navigation("Guild");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||||
|
.WithMany("InventoryItems")
|
||||||
|
.HasForeignKey("CharacterId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Character");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Characters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("InventoryItems");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("GuildMembers");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
Server/DB/Migrations/20231114184404_AddGuildData.cs
Normal file
104
Server/DB/Migrations/20231114184404_AddGuildData.cs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Server.DB.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddGuildData : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "GuildId",
|
||||||
|
table: "Characters",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Guild",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "text", nullable: true),
|
||||||
|
Notice = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Guild", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GuildMember",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
CharacterId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
GuildId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Rank = table.Column<byte>(type: "smallint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GuildMember", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GuildMember_Characters_CharacterId",
|
||||||
|
column: x => x.CharacterId,
|
||||||
|
principalTable: "Characters",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GuildMember_Guild_GuildId",
|
||||||
|
column: x => x.GuildId,
|
||||||
|
principalTable: "Guild",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Characters_GuildId",
|
||||||
|
table: "Characters",
|
||||||
|
column: "GuildId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GuildMember_CharacterId",
|
||||||
|
table: "GuildMember",
|
||||||
|
column: "CharacterId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GuildMember_GuildId",
|
||||||
|
table: "GuildMember",
|
||||||
|
column: "GuildId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Characters_Guild_GuildId",
|
||||||
|
table: "Characters",
|
||||||
|
column: "GuildId",
|
||||||
|
principalTable: "Guild",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Characters_Guild_GuildId",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GuildMember");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Guild");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Characters_GuildId",
|
||||||
|
table: "Characters");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "GuildId",
|
||||||
|
table: "Characters");
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ namespace Server.DB.Migrations
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "7.0.10")
|
.HasAnnotation("ProductVersion", "7.0.13")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
@ -91,6 +91,58 @@ namespace Server.DB.Migrations
|
||||||
b.ToTable("Characters");
|
b.ToTable("Characters");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("AddOption3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<short>("ItemId")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("ItemType")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Level")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option2")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<short>("Option3")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Rarity")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<byte>("Slot")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.ToTable("InventoryItem");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Server.DB.Documents.Account", "Account")
|
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||||
|
@ -102,10 +154,26 @@ namespace Server.DB.Migrations
|
||||||
b.Navigation("Account");
|
b.Navigation("Account");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||||
|
.WithMany("InventoryItems")
|
||||||
|
.HasForeignKey("CharacterId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Character");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Characters");
|
b.Navigation("Characters");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("InventoryItems");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,20 @@ public class WonderkingContext : DbContext
|
||||||
builder.Property(b => b.Password).HasColumnType("bytea");
|
builder.Property(b => b.Password).HasColumnType("bytea");
|
||||||
builder.Property(b => b.Salt).HasColumnType("bytea");
|
builder.Property(b => b.Salt).HasColumnType("bytea");
|
||||||
builder.HasKey(b => b.Id);
|
builder.HasKey(b => b.Id);
|
||||||
builder.HasMany(e => e.Characters).WithOne(e => e.Account).HasForeignKey(e => e.AccountId).IsRequired();
|
builder.HasMany(e => e.Characters).WithOne(e => e.Account).HasForeignKey(e => e.AccountId)
|
||||||
|
.IsRequired();
|
||||||
}).Entity<Character>(builder =>
|
}).Entity<Character>(builder =>
|
||||||
{
|
{
|
||||||
builder.HasKey(c => c.Id);
|
builder.HasKey(c => c.Id);
|
||||||
builder.Property(c => c.Name).HasColumnType("varchar(20)");
|
builder.Property(c => c.Name).HasColumnType("varchar(20)");
|
||||||
|
builder.HasMany(e => e.InventoryItems).WithOne(e => e.Character)
|
||||||
|
.HasForeignKey(e => e.CharacterId).IsRequired();
|
||||||
|
builder.OwnsOne(p => p.BaseStats);
|
||||||
|
builder.OwnsOne(p => p.JobData);
|
||||||
|
}).Entity<InventoryItem>(builder => { builder.HasKey(i => i.Id); }).Entity<Guild>(builder =>
|
||||||
|
{
|
||||||
|
builder.HasKey(g => g.Id);
|
||||||
|
builder.HasMany(g => g.GuildMembers).WithOne(g => g.Guild).HasForeignKey(g => g.GuildId)
|
||||||
|
.IsRequired();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
15
Server/LoggerMessages/LoginHandlerLoggerMessages.cs
Normal file
15
Server/LoggerMessages/LoginHandlerLoggerMessages.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Server.PacketHandlers;
|
||||||
|
|
||||||
|
namespace Server.LoggerMessages;
|
||||||
|
|
||||||
|
public static partial class LoginHandlerLoggerMessages
|
||||||
|
{
|
||||||
|
[LoggerMessage(EventId = 0, Level = LogLevel.Information,
|
||||||
|
Message = "Login data: Username {Username} & Password {Password}")]
|
||||||
|
public static partial void LoginData(this ILogger<LoginHandler> logger, string username, string password);
|
||||||
|
|
||||||
|
[LoggerMessage(EventId = 1, Level = LogLevel.Information,
|
||||||
|
Message = "Requested account for user: {Username} does not exist")]
|
||||||
|
public static partial void RequestedAccountDoesNotExist(this ILogger<LoginHandler> logger, string username);
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Server.DB.Documents;
|
||||||
|
using Wonderking.Game.Data.Character;
|
||||||
using Wonderking.Packets.Incoming;
|
using Wonderking.Packets.Incoming;
|
||||||
|
using Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
namespace Server.PacketHandlers;
|
namespace Server.PacketHandlers;
|
||||||
|
|
||||||
|
@ -29,9 +32,148 @@ public class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket>
|
||||||
public Task HandleAsync(ChannelSelectionPacket packet, TcpSession session)
|
public Task HandleAsync(ChannelSelectionPacket packet, TcpSession session)
|
||||||
{
|
{
|
||||||
var authSession = (AuthSession)session;
|
var authSession = (AuthSession)session;
|
||||||
var charactersOfAccount = this._wonderkingContext.Accounts.Include(account => account.Characters)
|
ChannelSelectionResponsePacket responsePacket;
|
||||||
.FirstOrDefault(a => a.Id == authSession.AccountId)
|
CharacterSelectionSetGuildNamePacket guildNameResponsePacket;
|
||||||
?.Characters;
|
|
||||||
|
var hasCharacters = this._wonderkingContext.Accounts.Include(account => account.Characters)
|
||||||
|
.FirstOrDefault(a => a.Id == authSession.AccountId)?.Characters.Count != 0;
|
||||||
|
var testingChars = false;
|
||||||
|
if (hasCharacters && !testingChars)
|
||||||
|
{
|
||||||
|
responsePacket = new ChannelSelectionResponsePacket
|
||||||
|
{
|
||||||
|
ChannelIsFullFlag = 0,
|
||||||
|
Endpoint = "127.0.0.1",
|
||||||
|
Port = 12345,
|
||||||
|
Characters = this._wonderkingContext.Characters.Where(c => c.AccountId == authSession.AccountId)
|
||||||
|
.Select(c =>
|
||||||
|
new CharacterData
|
||||||
|
{
|
||||||
|
Name = c.Name,
|
||||||
|
Job = c.JobData,
|
||||||
|
Gender = c.Gender,
|
||||||
|
Level = c.Level,
|
||||||
|
Experience = 0,
|
||||||
|
Stats = c.BaseStats,
|
||||||
|
Health = c.Health,
|
||||||
|
Mana = c.Mana,
|
||||||
|
EquippedItems =
|
||||||
|
c.InventoryItems.Where(item => item.ItemType == ItemType.WornEquipment)
|
||||||
|
.Select(item => item.ItemId)
|
||||||
|
.ToArray(),
|
||||||
|
EquippedCashItems = c.InventoryItems
|
||||||
|
.Where(item => item.ItemType == ItemType.WornCashEquipment)
|
||||||
|
.Select(item => item.ItemId)
|
||||||
|
.ToArray(),
|
||||||
|
})
|
||||||
|
.ToArray(),
|
||||||
|
};
|
||||||
|
|
||||||
|
guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket
|
||||||
|
{
|
||||||
|
GuildNames = this._wonderkingContext.Characters.Where(c => c.AccountId == authSession.AccountId)
|
||||||
|
.Select(character => character.Guild.Name).ToArray()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
responsePacket = testingChars
|
||||||
|
? CreateTestChannelSelectionResponsePacket()
|
||||||
|
: new ChannelSelectionResponsePacket
|
||||||
|
{
|
||||||
|
ChannelIsFullFlag = 0,
|
||||||
|
Endpoint = "127.0.0.1",
|
||||||
|
Port = 12345,
|
||||||
|
Characters = Array.Empty<CharacterData>()
|
||||||
|
};
|
||||||
|
guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket
|
||||||
|
{
|
||||||
|
GuildNames = new[] { "ABCDEFGHIJKLMNOP", "QRSTUVWXYZ123456", "A Guild Name For" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
authSession.Send(responsePacket);
|
||||||
|
if (guildNameResponsePacket.GuildNames.Length > 0)
|
||||||
|
{
|
||||||
|
authSession.Send(guildNameResponsePacket);
|
||||||
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ChannelSelectionResponsePacket CreateTestChannelSelectionResponsePacket()
|
||||||
|
{
|
||||||
|
return new ChannelSelectionResponsePacket
|
||||||
|
{
|
||||||
|
ChannelIsFullFlag = 0,
|
||||||
|
Endpoint = "127.0.0.1",
|
||||||
|
Port = 12345,
|
||||||
|
Characters = new[]
|
||||||
|
{
|
||||||
|
new CharacterData
|
||||||
|
{
|
||||||
|
Name = "1",
|
||||||
|
Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 },
|
||||||
|
Gender = Gender.Female,
|
||||||
|
Level = ushort.MaxValue - 1,
|
||||||
|
Experience = 255,
|
||||||
|
Stats = new BaseStats
|
||||||
|
{
|
||||||
|
Strength = 5,
|
||||||
|
Dexterity = 5,
|
||||||
|
Intelligence = 5,
|
||||||
|
Vitality = 5,
|
||||||
|
Luck = 5,
|
||||||
|
Wisdom = 5
|
||||||
|
},
|
||||||
|
Health = int.MaxValue - 1,
|
||||||
|
Mana = int.MaxValue - 1,
|
||||||
|
EquippedItems = Enumerable.Repeat((ushort)25, 20).ToArray(),
|
||||||
|
EquippedCashItems = Enumerable.Repeat((ushort)70, 20).ToArray()
|
||||||
|
},
|
||||||
|
new CharacterData
|
||||||
|
{
|
||||||
|
Name = "2",
|
||||||
|
Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 },
|
||||||
|
Gender = Gender.Female,
|
||||||
|
Level = ushort.MaxValue - 1,
|
||||||
|
Experience = 255,
|
||||||
|
Stats = new BaseStats
|
||||||
|
{
|
||||||
|
Strength = 5,
|
||||||
|
Dexterity = 5,
|
||||||
|
Intelligence = 5,
|
||||||
|
Vitality = 5,
|
||||||
|
Luck = 5,
|
||||||
|
Wisdom = 5
|
||||||
|
},
|
||||||
|
Health = int.MaxValue - 1,
|
||||||
|
Mana = int.MaxValue - 1,
|
||||||
|
EquippedItems = Enumerable.Repeat((ushort)35, 20).ToArray(),
|
||||||
|
EquippedCashItems = Enumerable.Repeat((ushort)55, 20).ToArray()
|
||||||
|
},
|
||||||
|
new CharacterData
|
||||||
|
{
|
||||||
|
Name = "3",
|
||||||
|
Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 },
|
||||||
|
Gender = Gender.Female,
|
||||||
|
Level = ushort.MaxValue - 1,
|
||||||
|
Experience = 255,
|
||||||
|
Stats = new BaseStats
|
||||||
|
{
|
||||||
|
Strength = 5,
|
||||||
|
Dexterity = 5,
|
||||||
|
Intelligence = 5,
|
||||||
|
Vitality = 5,
|
||||||
|
Luck = 5,
|
||||||
|
Wisdom = 5
|
||||||
|
},
|
||||||
|
Health = int.MaxValue - 1,
|
||||||
|
Mana = int.MaxValue - 1,
|
||||||
|
EquippedItems = Enumerable.Repeat((ushort)45, 20).ToArray(),
|
||||||
|
EquippedCashItems = Enumerable.Repeat((ushort)65, 20).ToArray()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Server.LoggerMessages;
|
||||||
using Wonderking.Packets.Incoming;
|
using Wonderking.Packets.Incoming;
|
||||||
using Wonderking.Packets.Outgoing;
|
using Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
|
@ -25,47 +26,79 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
||||||
this._configuration = configuration;
|
this._configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
|
private static Task<byte[]> GetPasswordHashAsync(string password, byte[] salt, Guid userId)
|
||||||
{
|
{
|
||||||
LoginResponseReason loginResponseReason;
|
|
||||||
this._logger.LogInformation("Login data: Username {Username} & Password {Password}", packet.Username,
|
|
||||||
packet.Password);
|
|
||||||
var account = this._wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
|
|
||||||
|
|
||||||
// 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(packet.Password))
|
var argon2Id = new Argon2id(Encoding.ASCII.GetBytes(password))
|
||||||
{
|
{
|
||||||
MemorySize = 1024 * 19,
|
MemorySize = 1024 * 19,
|
||||||
Iterations = 2,
|
Iterations = 2,
|
||||||
DegreeOfParallelism = 1
|
DegreeOfParallelism = 1,
|
||||||
|
Salt = salt,
|
||||||
|
AssociatedData = userId.ToByteArray()
|
||||||
};
|
};
|
||||||
|
return argon2Id.GetBytesAsync(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<LoginResponseReason> CreateAccountOnLoginAsync(string username, string password)
|
||||||
|
{
|
||||||
|
LoginResponseReason loginResponseReason;
|
||||||
|
var transaction =
|
||||||
|
await _wonderkingContext.Database.BeginTransactionAsync().ConfigureAwait(true);
|
||||||
|
await using (transaction.ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var salt = RandomNumberGenerator.GetBytes(16);
|
||||||
|
var finalAccount =
|
||||||
|
await this._wonderkingContext.Accounts.AddAsync(new Account(username,
|
||||||
|
Array.Empty<byte>(), "",
|
||||||
|
0, salt)).ConfigureAwait(true);
|
||||||
|
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
||||||
|
finalAccount.Entity.Password =
|
||||||
|
await LoginHandler.GetPasswordHashAsync(password, salt, finalAccount.Entity.Id)
|
||||||
|
.ConfigureAwait(true);
|
||||||
|
this._wonderkingContext.Accounts.Update(finalAccount.Entity);
|
||||||
|
loginResponseReason = LoginResponseReason.Ok;
|
||||||
|
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
||||||
|
|
||||||
|
await transaction.CommitAsync().ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
await transaction.RollbackAsync().ConfigureAwait(true); // Rollback the transaction on error
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return loginResponseReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
|
||||||
|
{
|
||||||
|
LoginResponseReason loginResponseReason;
|
||||||
|
this._logger.LoginData(packet.Username, packet.Password);
|
||||||
|
var account = this._wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
|
||||||
|
|
||||||
if (account == null)
|
if (account == null)
|
||||||
{
|
{
|
||||||
if (this._configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
|
if (this._configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
|
||||||
{
|
{
|
||||||
argon2Id.Salt = RandomNumberGenerator.GetBytes(16);
|
loginResponseReason = await CreateAccountOnLoginAsync(packet.Username, packet.Password)
|
||||||
var finalAccount =
|
.ConfigureAwait(true);
|
||||||
await this._wonderkingContext.Accounts.AddAsync(new Account(packet.Username, Array.Empty<byte>(), "",
|
|
||||||
0, argon2Id.Salt)).ConfigureAwait(true);
|
|
||||||
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
|
||||||
argon2Id.AssociatedData = finalAccount.Entity.Id.ToByteArray();
|
|
||||||
finalAccount.Entity.Password = await argon2Id.GetBytesAsync(16).ConfigureAwait(true);
|
|
||||||
this._wonderkingContext.Accounts.Update(finalAccount.Entity);
|
|
||||||
loginResponseReason = LoginResponseReason.Ok;
|
|
||||||
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._logger.LogInformation("Requested account for user: {Username} does not exist", packet.Username);
|
this._logger.RequestedAccountDoesNotExist(packet.Username);
|
||||||
loginResponseReason = LoginResponseReason.AccountDoesNotExit;
|
loginResponseReason = LoginResponseReason.AccountDoesNotExit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
argon2Id.Salt = account.Salt;
|
var salt = account.Salt;
|
||||||
argon2Id.AssociatedData = account.Id.ToByteArray();
|
var tempPasswordBytes = await LoginHandler.GetPasswordHashAsync(packet.Password, salt, account.Id)
|
||||||
var tempPasswordBytes = await argon2Id.GetBytesAsync(16).ConfigureAwait(true);
|
.ConfigureAwait(false);
|
||||||
loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password)
|
loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password)
|
||||||
? LoginResponseReason.Ok
|
? LoginResponseReason.Ok
|
||||||
: LoginResponseReason.WrongPassword;
|
: LoginResponseReason.WrongPassword;
|
||||||
|
@ -84,6 +117,7 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
||||||
sess.AccountId = account.Id;
|
sess.AccountId = account.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("LoginResponsePacket: {@LoginResponsePacket}", loginResponsePacket);
|
||||||
sess?.Send(loginResponsePacket);
|
sess?.Send(loginResponsePacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ builder.Services.AddDbContext<WonderkingContext>();
|
||||||
builder.Services.AddSingleton<PacketDistributorService>();
|
builder.Services.AddSingleton<PacketDistributorService>();
|
||||||
builder.Services.AddHostedService(provider =>
|
builder.Services.AddHostedService(provider =>
|
||||||
provider.GetService<PacketDistributorService>() ?? throw new InvalidOperationException());
|
provider.GetService<PacketDistributorService>() ?? throw new InvalidOperationException());
|
||||||
|
builder.Services.AddSingleton<ItemObjectPoolService>();
|
||||||
builder.Services.AddMassTransit(x =>
|
builder.Services.AddMassTransit(x =>
|
||||||
{
|
{
|
||||||
x.UsingInMemory((context, configurator) => configurator.ConfigureEndpoints(context));
|
x.UsingInMemory((context, configurator) => configurator.ConfigureEndpoints(context));
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<RootNamespace>Server</RootNamespace>
|
<RootNamespace>Server</RootNamespace>
|
||||||
<LangVersion>default</LangVersion>
|
<LangVersion>default</LangVersion>
|
||||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||||
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||||
<Features>strict</Features>
|
<Features>strict</Features>
|
||||||
<Authors>Timothy (RaiNote) Schenk</Authors>
|
<Authors>Timothy (RaiNote) Schenk</Authors>
|
||||||
|
@ -41,17 +41,17 @@
|
||||||
<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="4.15.0"/>
|
<PackageReference Include="DotNext" Version="4.15.1" />
|
||||||
<PackageReference Include="DotNext.IO" Version="4.15.0"/>
|
<PackageReference Include="DotNext.IO" Version="4.15.0"/>
|
||||||
<PackageReference Include="DotNext.Metaprogramming" Version="4.15.0"/>
|
<PackageReference Include="DotNext.Metaprogramming" Version="4.15.0"/>
|
||||||
<PackageReference Include="DotNext.Reflection" Version="4.9.0"/>
|
<PackageReference Include="DotNext.Reflection" Version="4.9.0"/>
|
||||||
<PackageReference Include="DotNext.Threading" Version="4.14.2"/>
|
<PackageReference Include="DotNext.Threading" Version="4.14.2"/>
|
||||||
<PackageReference Include="DotNext.Unsafe" Version="4.14.0"/>
|
<PackageReference Include="DotNext.Unsafe" Version="4.14.0"/>
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2023.2.0"/>
|
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||||
<PackageReference Include="JetBrains.ExternalAnnotations" Version="10.2.134"/>
|
<PackageReference Include="JetBrains.ExternalAnnotations" Version="10.2.135" />
|
||||||
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.0"/>
|
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.0"/>
|
||||||
<PackageReference Include="MassTransit" Version="8.1.1"/>
|
<PackageReference Include="MassTransit" Version="8.1.2" />
|
||||||
<PackageReference Include="MassTransit.Analyzers" Version="8.1.1">
|
<PackageReference Include="MassTransit.Analyzers" Version="8.1.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -63,16 +63,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>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.14"/>
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="7.0.14"/>
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.0"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="7.0.14"/>
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.0"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0"/>
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.14">
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0"/>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0"/>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1"/>
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0"/>
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0"/>
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.7.30">
|
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.7.30">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
@ -89,6 +93,7 @@
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0"/>
|
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0"/>
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
51
Server/Services/ItemObjectPoolService.cs
Normal file
51
Server/Services/ItemObjectPoolService.cs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Wonderking.Game.Data;
|
||||||
|
using Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
namespace Server.Services;
|
||||||
|
|
||||||
|
public class ItemObjectPoolService : IHostedService
|
||||||
|
{
|
||||||
|
readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool = new();
|
||||||
|
private readonly ItemReader _itemReader;
|
||||||
|
|
||||||
|
public ItemObjectPoolService(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue<string>("Path") ??
|
||||||
|
string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var amountOfEntries = _itemReader.GetAmountOfEntries();
|
||||||
|
ParallelEnumerable.Range(0, (int)amountOfEntries).AsParallel().ForAll(i =>
|
||||||
|
{
|
||||||
|
var itemObject = _itemReader.GetEntry((uint)i);
|
||||||
|
_itemObjectPool.TryAdd(itemObject.ItemID, itemObject);
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_itemReader.Dispose();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemObject GetItem(ushort itemId)
|
||||||
|
{
|
||||||
|
return _itemObjectPool[itemId];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsItem(ushort itemId)
|
||||||
|
{
|
||||||
|
return _itemObjectPool.ContainsKey(itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQueryable<ItemObject> QueryItems()
|
||||||
|
{
|
||||||
|
return _itemObjectPool.AsReadOnly().Values.AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,9 +36,9 @@ public class PacketDistributorService : IHostedService
|
||||||
var tempDeserializationMap =
|
var tempDeserializationMap =
|
||||||
new Dictionary<OperationCode, Func<byte[], IPacket>>();
|
new Dictionary<OperationCode, Func<byte[], IPacket>>();
|
||||||
|
|
||||||
var executingAssembly = Assembly.GetExecutingAssembly();
|
var wonderkingAssembly = Assembly.GetAssembly(typeof(IPacket));
|
||||||
var packetsTypes = this.GetPacketsWithId(executingAssembly);
|
var packetsTypes = this.GetPacketsWithId(wonderkingAssembly);
|
||||||
var packetHandlers = this.GetAllPacketHandlersWithId(executingAssembly);
|
var packetHandlers = this.GetAllPacketHandlersWithId(Assembly.GetExecutingAssembly());
|
||||||
this._packetHandlersInstantiation = new ConcurrentDictionary<OperationCode, object>();
|
this._packetHandlersInstantiation = new ConcurrentDictionary<OperationCode, object>();
|
||||||
packetHandlers.ForEach(x =>
|
packetHandlers.ForEach(x =>
|
||||||
{
|
{
|
||||||
|
@ -76,6 +76,7 @@ public class PacketDistributorService : IHostedService
|
||||||
// ! : We are filtering if types that don't have an instance of the required Attribute
|
// ! : We are filtering if types that don't have an instance of the required Attribute
|
||||||
var packetsWithId = executingAssembly.GetTypes().AsParallel()
|
var packetsWithId = executingAssembly.GetTypes().AsParallel()
|
||||||
.Where(type => type.HasInterface(typeof(IPacket)) && type is { IsInterface: false, IsAbstract: false })
|
.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>() })
|
.Select(type => new { Type = type, Attribute = type.GetCustomAttribute<PacketIdAttribute>() })
|
||||||
.Where(item => item.Attribute is not null)
|
.Where(item => item.Attribute is not null)
|
||||||
.ToDictionary(item => item.Attribute!.Code, item => item.Type);
|
.ToDictionary(item => item.Attribute!.Code, item => item.Type);
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
services:
|
services:
|
||||||
server:
|
server:
|
||||||
container_name: continuity-server
|
container_name: continuity-server
|
||||||
image: server:latest
|
image: continuity:latest
|
||||||
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
environment:
|
environment:
|
||||||
|
@ -11,29 +12,39 @@
|
||||||
- DB:Port=5432
|
- DB:Port=5432
|
||||||
- DB:Username=continuity
|
- DB:Username=continuity
|
||||||
- DB:Password=continuity
|
- DB:Password=continuity
|
||||||
|
- Game:Data:Path=/app/data
|
||||||
networks:
|
networks:
|
||||||
- continuity
|
- continuity
|
||||||
ports:
|
ports:
|
||||||
- "10001:10001"
|
- "10001:10001"
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: game-data
|
||||||
|
target: /app/data
|
||||||
|
read_only: true
|
||||||
|
|
||||||
db:
|
db:
|
||||||
container_name: continuity-db
|
container_name: continuity-db
|
||||||
image: postgres:16.0-alpine
|
image: postgres:16.0-alpine
|
||||||
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=continuity
|
- POSTGRES_USER=continuity
|
||||||
- POSTGRES_DB=continuity
|
- POSTGRES_DB=continuity
|
||||||
- POSTGRES_PASSWORD=continuity
|
- POSTGRES_PASSWORD=continuity
|
||||||
networks:
|
networks:
|
||||||
- continuity
|
- continuity
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- db-data:/var/lib/postgresql/data
|
- db-data:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'" ]
|
test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}" ]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
continuity:
|
continuity:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db-data:
|
db-data:
|
||||||
|
|
|
@ -8,5 +8,10 @@
|
||||||
},
|
},
|
||||||
"Testing": {
|
"Testing": {
|
||||||
"CreateAccountOnLogin": true
|
"CreateAccountOnLogin": true
|
||||||
|
},
|
||||||
|
"Game":{
|
||||||
|
"Data":{
|
||||||
|
"Path": "../wk-data"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Server.DB.Documents;
|
namespace Wonderking.Game.Data.Character;
|
||||||
|
|
||||||
public enum Gender : byte
|
public enum Gender : byte
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Server.DB.Documents;
|
namespace Wonderking.Game.Data.Character;
|
||||||
|
|
||||||
public enum PvPLevel : byte
|
public enum PvPLevel : byte
|
||||||
{
|
{
|
10
Wonderking/Game/Data/Item/ContainedItem.cs
Normal file
10
Wonderking/Game/Data/Item/ContainedItem.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct ContainedItem
|
||||||
|
{
|
||||||
|
public short ID { get; internal set; }
|
||||||
|
public float ObtainChance { get; internal set; }
|
||||||
|
}
|
11
Wonderking/Game/Data/Item/CraftMaterial.cs
Normal file
11
Wonderking/Game/Data/Item/CraftMaterial.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct CraftMaterial
|
||||||
|
{
|
||||||
|
public uint ID;
|
||||||
|
public uint Amount;
|
||||||
|
}
|
||||||
|
|
55
Wonderking/Game/Data/Item/ElementalStats.cs
Normal file
55
Wonderking/Game/Data/Item/ElementalStats.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
[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;
|
||||||
|
}
|
7
Wonderking/Game/Data/Item/ItemOptions.cs
Normal file
7
Wonderking/Game/Data/Item/ItemOptions.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
public struct ItemOptions
|
||||||
|
{
|
||||||
|
public ICollection<byte> OptionIDs { get; internal set; }
|
||||||
|
public bool OptionAvailable { get; internal set; }
|
||||||
|
}
|
25
Wonderking/Game/Data/Item/Stats.cs
Normal file
25
Wonderking/Game/Data/Item/Stats.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 24)]
|
||||||
|
public struct Stats
|
||||||
|
{
|
||||||
|
[FieldOffset(0), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Strength;
|
||||||
|
|
||||||
|
[FieldOffset(4), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Dexterity;
|
||||||
|
|
||||||
|
[FieldOffset(8), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Intelligence;
|
||||||
|
|
||||||
|
[FieldOffset(12), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Vitality;
|
||||||
|
|
||||||
|
[FieldOffset(16), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Luck;
|
||||||
|
|
||||||
|
[FieldOffset(20), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Wisdom;
|
||||||
|
}
|
170
Wonderking/Game/Data/ItemObject.cs
Normal file
170
Wonderking/Game/Data/ItemObject.cs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Wonderking.Game.Data.Item;
|
||||||
|
using Wonderking.Utils;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data;
|
||||||
|
|
||||||
|
[GameDataMetadata(932, "baseitemdata.dat", 197)]
|
||||||
|
public struct ItemObject
|
||||||
|
{
|
||||||
|
public uint ItemID { get; set; }
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
public uint ItemType { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown2 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown3 { get; set; }
|
||||||
|
|
||||||
|
public uint ClassNo1 { get; set; }
|
||||||
|
public uint ClassNo2 { get; set; }
|
||||||
|
public uint ClassNo3 { get; set; }
|
||||||
|
public uint ClassNo4 { get; set; }
|
||||||
|
public uint SlotNo1 { get; set; }
|
||||||
|
public uint SlotNo2 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown4 { get; set; }
|
||||||
|
|
||||||
|
public uint IsCash { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown5 { get; set; }
|
||||||
|
|
||||||
|
public uint Price { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown7 { get; set; }
|
||||||
|
|
||||||
|
public uint MaxNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown17 { get; set; }
|
||||||
|
|
||||||
|
public uint MaximumLevelRequirement { get; set; }
|
||||||
|
public uint SexNo { get; set; }
|
||||||
|
public uint WeaponSomething { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown8 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R2C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown9 { get; set; }
|
||||||
|
|
||||||
|
public Stats Stats { get; set; }
|
||||||
|
public ElementalStats ElementalStats { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R7C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R8C { get; set; }
|
||||||
|
|
||||||
|
public float Speed { get; set; }
|
||||||
|
|
||||||
|
public float Jump { get; set; }
|
||||||
|
public int StatDefense { get; set; }
|
||||||
|
public uint MagicID { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown13 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown14 { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalHealthRecoveryVolume { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R9C_1 { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalManaRecoveryVolume { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R9C_2 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R10C { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalHealthPoints { get; set; }
|
||||||
|
public int AdditionalManaPoints { get; set; }
|
||||||
|
public bool IsArrow { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown18 { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalEvasionRate { get; set; }
|
||||||
|
public int HitRate { get; set; }
|
||||||
|
|
||||||
|
public int ChanceToHit { get; set; }
|
||||||
|
public int MagicalDamage { get; set; }
|
||||||
|
public int CriticalHitChance { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R12C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown16 { get; set; }
|
||||||
|
|
||||||
|
public int MinimalAttackDamage { get; set; }
|
||||||
|
public int MaximalAttackDamage { get; set; }
|
||||||
|
public int PhysicalDamage { get; set; }
|
||||||
|
public CraftMaterial[] CraftMaterial { get; set; }
|
||||||
|
public uint CraftResultAmount { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R14C { get; set; }
|
||||||
|
|
||||||
|
public uint CraftResultItem { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R15C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R16C { get; set; }
|
||||||
|
|
||||||
|
public int InventoryX { get; set; }
|
||||||
|
public int InventoryY { get; set; }
|
||||||
|
public int InventoryWidth { get; set; }
|
||||||
|
public int InventoryHeight { get; set; }
|
||||||
|
public int SheetID { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown1 { get; set; }
|
||||||
|
|
||||||
|
public bool IsEnchantable { get; set; }
|
||||||
|
|
||||||
|
public uint SetID { get; set; }
|
||||||
|
public uint[] SetItems { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown1_2 { get; set; }
|
||||||
|
|
||||||
|
public ItemOptions Options { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown19 { get; set; }
|
||||||
|
|
||||||
|
public byte PetID { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown20 { get; set; }
|
||||||
|
|
||||||
|
public byte HitBoxScaling { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown20_2 { get; set; }
|
||||||
|
|
||||||
|
public ContainedItem[] ContainedItems { get; set; }
|
||||||
|
|
||||||
|
public bool IsQuestItem { get; set; }
|
||||||
|
public byte MinimumLevelRequirement { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown21_2 { get; set; }
|
||||||
|
}
|
50
Wonderking/Game/DataReader.cs
Normal file
50
Wonderking/Game/DataReader.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Wonderking.Game;
|
||||||
|
|
||||||
|
public abstract class DataReader<T>
|
||||||
|
{
|
||||||
|
protected DataReader(string path)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
DatFileContent = new(GetDatFileContent(path).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private protected string Path { get; init; }
|
||||||
|
|
||||||
|
public abstract uint GetAmountOfEntries();
|
||||||
|
public abstract T GetEntry(uint entryId);
|
||||||
|
|
||||||
|
protected ushort GetSizeOfEntry()
|
||||||
|
{
|
||||||
|
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DataEntrySize ??
|
||||||
|
throw new NotSupportedException("DataEntrySize is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetDatFileName()
|
||||||
|
{
|
||||||
|
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DatFileName ??
|
||||||
|
throw new NotSupportedException("DatFileName is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte GetXorKey()
|
||||||
|
{
|
||||||
|
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.XorKey ??
|
||||||
|
throw new NotSupportedException("XorKey is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MemoryStream DatFileContent { get; }
|
||||||
|
|
||||||
|
private static Span<byte> GetDatFileContent(string path)
|
||||||
|
{
|
||||||
|
var fileData = File.ReadAllBytes(path + GetDatFileName());
|
||||||
|
var data = new byte[fileData.Length];
|
||||||
|
|
||||||
|
for (var i = 0; i < fileData.Length; i++)
|
||||||
|
{
|
||||||
|
data[i] = (byte)(fileData[i] ^ GetXorKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
12
Wonderking/Game/GameDataMetadataAttribute.cs
Normal file
12
Wonderking/Game/GameDataMetadataAttribute.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Wonderking.Game;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
|
||||||
|
public class GameDataMetadataAttribute(ushort dataEntrySize, string datFileName, byte xorKey) : Attribute
|
||||||
|
{
|
||||||
|
[UsedImplicitly] public byte XorKey { get; init; } = xorKey;
|
||||||
|
[UsedImplicitly] public ushort DataEntrySize { get; init; } = dataEntrySize;
|
||||||
|
|
||||||
|
[UsedImplicitly] internal string DatFileName { get; init; } = datFileName;
|
||||||
|
}
|
75
Wonderking/Game/Reader/BinaryReader.cs
Normal file
75
Wonderking/Game/Reader/BinaryReader.cs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
public static class BinaryReader<T> where T : new()
|
||||||
|
{
|
||||||
|
public static readonly Func<BinaryReader, T> Read;
|
||||||
|
|
||||||
|
static BinaryReader()
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
|
||||||
|
if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, bool>)(p => p.ReadBoolean());
|
||||||
|
}
|
||||||
|
else if (type == typeof(char))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, char>)(p => p.ReadChar());
|
||||||
|
}
|
||||||
|
else if (type == typeof(string))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, string>)(p => p.ReadString());
|
||||||
|
}
|
||||||
|
else if (type == typeof(sbyte))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, sbyte>)(p => p.ReadSByte());
|
||||||
|
}
|
||||||
|
else if (type == typeof(short))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, short>)(p => p.ReadInt16());
|
||||||
|
}
|
||||||
|
else if (type == typeof(int))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, int>)(p => p.ReadInt32());
|
||||||
|
}
|
||||||
|
else if (type == typeof(long))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, long>)(p => p.ReadInt64());
|
||||||
|
}
|
||||||
|
else if (type == typeof(byte))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, byte>)(p => p.ReadByte());
|
||||||
|
}
|
||||||
|
else if (type == typeof(ushort))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ushort>)(p => p.ReadUInt16());
|
||||||
|
}
|
||||||
|
else if (type == typeof(uint))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, uint>)(p => p.ReadUInt32());
|
||||||
|
}
|
||||||
|
else if (type == typeof(ulong))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ulong>)(p => p.ReadUInt64());
|
||||||
|
}
|
||||||
|
else if (type == typeof(float))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, float>)(p => p.ReadSingle());
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, double>)(p => p.ReadDouble());
|
||||||
|
}
|
||||||
|
else if (type == typeof(decimal))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, decimal>)(p => p.ReadDecimal());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(p =>
|
||||||
|
(T)(object)p.ReadBytes(Marshal.SizeOf(new T())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Wonderking/Game/Reader/GenericReaderExtensions.cs
Normal file
37
Wonderking/Game/Reader/GenericReaderExtensions.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
/* Nicht gemergte Änderung aus Projekt "Wonderking(net7.0)"
|
||||||
|
Vor:
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
Nach:
|
||||||
|
using System.Text;
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
public static class GenericReaderExtensions
|
||||||
|
{
|
||||||
|
public static string ReadString(this BinaryReader reader, int length)
|
||||||
|
{
|
||||||
|
var ret = Encoding.ASCII.GetString(reader.ReadBytes(length)).Replace("\0", "");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T[] ReadArray<T>(this BinaryReader pReader, int pLength) where T : new()
|
||||||
|
{
|
||||||
|
var array = new T[pLength];
|
||||||
|
for (var index = 0; index < pLength; ++index)
|
||||||
|
{
|
||||||
|
array[index] = pReader.Read<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Read<T>(this BinaryReader br) where T : new()
|
||||||
|
{
|
||||||
|
return BinaryReader<T>.Read(br);
|
||||||
|
}
|
||||||
|
}
|
107
Wonderking/Game/Reader/ItemReader.cs
Normal file
107
Wonderking/Game/Reader/ItemReader.cs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
using Wonderking.Game.Data;
|
||||||
|
using Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
public class ItemReader(string path) : DataReader<ItemObject>(path), IDisposable
|
||||||
|
{
|
||||||
|
public override uint GetAmountOfEntries()
|
||||||
|
{
|
||||||
|
return (uint)((this.DatFileContent.Length - 9) / this.GetSizeOfEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ItemObject GetEntry(uint entryId)
|
||||||
|
{
|
||||||
|
var item = new ItemObject();
|
||||||
|
this.DatFileContent.Position = 9 + entryId * this.GetSizeOfEntry();
|
||||||
|
var reader = new BinaryReader(this.DatFileContent);
|
||||||
|
item.ItemID = reader.ReadUInt32(); //9
|
||||||
|
item.Disabled = reader.ReadUInt32() == 1; //13
|
||||||
|
item.ItemType = reader.ReadUInt32(); //17
|
||||||
|
item.Unknown2 = reader.ReadBytes(4); //21
|
||||||
|
item.Unknown3 = reader.ReadBytes(4); //25
|
||||||
|
item.ClassNo1 = reader.ReadUInt32(); //29
|
||||||
|
item.ClassNo2 = reader.ReadUInt32(); //33
|
||||||
|
item.ClassNo3 = reader.ReadUInt32(); //37
|
||||||
|
item.ClassNo4 = reader.ReadUInt32(); //41
|
||||||
|
item.SlotNo1 = reader.ReadUInt32(); //45
|
||||||
|
item.SlotNo2 = reader.ReadUInt32(); //49
|
||||||
|
item.Unknown4 = reader.ReadBytes(4); //53
|
||||||
|
item.IsCash = reader.ReadUInt32(); //57
|
||||||
|
item.Unknown5 = reader.ReadBytes(4); //61
|
||||||
|
item.Price = reader.ReadUInt32(); //65
|
||||||
|
item.Unknown7 = reader.ReadBytes(4); //69
|
||||||
|
item.MaxNumber = reader.ReadUInt32(); //73
|
||||||
|
item.Unknown17 = reader.ReadBytes(12); //77
|
||||||
|
item.MaximumLevelRequirement = reader.ReadUInt32(); //89
|
||||||
|
item.SexNo = reader.ReadUInt32(); //93
|
||||||
|
item.WeaponSomething = reader.ReadUInt32(); //97
|
||||||
|
item.Unknown8 = reader.ReadBytes(4); //101
|
||||||
|
item.R2C = reader.ReadBytes(16); //105
|
||||||
|
item.Unknown9 = reader.ReadBytes(4); //121
|
||||||
|
item.Stats = reader.ReadStats(); //125
|
||||||
|
item.ElementalStats = reader.ReadElementalStats(); //149
|
||||||
|
item.R7C = reader.ReadBytes(4); //213
|
||||||
|
item.R8C = reader.ReadBytes(8); //217
|
||||||
|
item.Speed = reader.ReadSingle(); //225
|
||||||
|
item.Jump = reader.ReadSingle(); //229
|
||||||
|
item.StatDefense = reader.ReadInt32(); //233
|
||||||
|
item.MagicID = reader.ReadUInt32(); //237
|
||||||
|
item.Unknown13 = reader.ReadBytes(4); //241
|
||||||
|
item.Unknown14 = reader.ReadBytes(4); //245
|
||||||
|
item.AdditionalHealthRecoveryVolume = reader.ReadInt32(); //249
|
||||||
|
item.R9C_1 = reader.ReadBytes(4); //253
|
||||||
|
item.AdditionalManaRecoveryVolume = reader.ReadInt32(); //257
|
||||||
|
item.R9C_2 = reader.ReadBytes(4); //261
|
||||||
|
item.R10C = reader.ReadBytes(8); //265
|
||||||
|
item.AdditionalHealthPoints = reader.ReadInt32(); //273
|
||||||
|
item.AdditionalManaPoints = reader.ReadInt32(); //277
|
||||||
|
item.IsArrow = reader.ReadBoolean(); //281
|
||||||
|
item.Unknown18 = reader.ReadBytes(7); //282
|
||||||
|
item.AdditionalEvasionRate = reader.ReadInt32(); //289
|
||||||
|
item.HitRate = reader.ReadInt32(); //293
|
||||||
|
item.ChanceToHit = reader.ReadInt32(); //297
|
||||||
|
item.MagicalDamage = reader.ReadInt32(); //301
|
||||||
|
item.CriticalHitChance = reader.ReadInt32(); //305
|
||||||
|
item.R12C = reader.ReadBytes(4); //309
|
||||||
|
item.Unknown16 = reader.ReadBytes(4); //313
|
||||||
|
item.MinimalAttackDamage = reader.ReadInt32(); //317
|
||||||
|
item.MaximalAttackDamage = reader.ReadInt32(); //321
|
||||||
|
item.PhysicalDamage = reader.ReadInt32(); //325
|
||||||
|
item.CraftMaterial = reader.ReadCraftMaterial(); //329
|
||||||
|
item.CraftResultAmount = reader.ReadUInt32(); //361
|
||||||
|
item.R14C = reader.ReadBytes(4); //365
|
||||||
|
item.CraftResultItem = reader.ReadUInt32(); //369
|
||||||
|
item.R15C = reader.ReadBytes(4); //373
|
||||||
|
item.R16C = reader.ReadBytes(20); //377
|
||||||
|
item.InventoryX = reader.ReadInt32(); //397
|
||||||
|
item.InventoryY = reader.ReadInt32(); //401
|
||||||
|
item.InventoryWidth = reader.ReadInt32(); //405
|
||||||
|
item.InventoryHeight = reader.ReadInt32(); //409
|
||||||
|
item.SheetID = reader.ReadInt32(); //413
|
||||||
|
item.Name = reader.ReadString(20); //417
|
||||||
|
item.Description = reader.ReadString(85); //427
|
||||||
|
item.Unknown1 = reader.ReadBytes(175); //493
|
||||||
|
item.IsEnchantable = reader.ReadUInt32() == 1; //687
|
||||||
|
item.Unknown1_2 = reader.ReadBytes(104); //687
|
||||||
|
item.SetItems = reader.ReadArray<uint>(5);
|
||||||
|
item.SetID = reader.ReadUInt32(); //691
|
||||||
|
item.Options = reader.ReadItemOptions(); //819
|
||||||
|
item.Unknown19 = reader.ReadBytes(23); //835
|
||||||
|
item.PetID = reader.ReadByte(); //858
|
||||||
|
item.Unknown20 = reader.ReadBytes(20); //859
|
||||||
|
item.HitBoxScaling = reader.ReadByte(); //879
|
||||||
|
item.Unknown20_2 = reader.ReadBytes(13); //880
|
||||||
|
item.ContainedItems = reader.ReadContainedItems(); //893
|
||||||
|
item.IsQuestItem = reader.ReadBoolean(); //923
|
||||||
|
item.MinimumLevelRequirement = reader.ReadByte(); //924
|
||||||
|
item.Unknown21_2 = reader.ReadBytes(6); //925
|
||||||
|
reader.Dispose(); //931
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.DatFileContent.Dispose();
|
||||||
|
}
|
||||||
|
}
|
96
Wonderking/Game/Reader/ItemReaderExtensions.cs
Normal file
96
Wonderking/Game/Reader/ItemReaderExtensions.cs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
using Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
public static class ItemReaderExtensions
|
||||||
|
{
|
||||||
|
public static Stats ReadStats(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
return new Stats()
|
||||||
|
{
|
||||||
|
Strength = reader.ReadInt32(), //125
|
||||||
|
Dexterity = reader.ReadInt32(), //129
|
||||||
|
Intelligence = reader.ReadInt32(), //133
|
||||||
|
Vitality = reader.ReadInt32(), //137
|
||||||
|
Luck = reader.ReadInt32(), //141
|
||||||
|
Wisdom = reader.ReadInt32(), //145
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ElementalStats ReadElementalStats(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
return new ElementalStats()
|
||||||
|
{
|
||||||
|
MinimumFireDamage = reader.ReadInt32(), //149
|
||||||
|
MinimumWaterDamage = reader.ReadInt32(), //153
|
||||||
|
MinimumDarkDamage = reader.ReadInt32(), //157
|
||||||
|
MinimumHolyDamage = reader.ReadInt32(), //161
|
||||||
|
MaximumFireDamage = reader.ReadInt32(), //165
|
||||||
|
MaximumWaterDamage = reader.ReadInt32(), //169
|
||||||
|
MaximumDarkDamage = reader.ReadInt32(), //173
|
||||||
|
MaximumHolyDamage = reader.ReadInt32(), //177
|
||||||
|
ElementFire = reader.ReadUInt32(), //181
|
||||||
|
ElementWater = reader.ReadUInt32(), //185
|
||||||
|
ElementDark = reader.ReadUInt32(), //189
|
||||||
|
ElementHoly = reader.ReadUInt32(), //193
|
||||||
|
FireResistance = reader.ReadInt32(), //197
|
||||||
|
WaterResistance = reader.ReadInt32(), //201
|
||||||
|
DarkResistance = reader.ReadInt32(), //205
|
||||||
|
HolyResistance = reader.ReadInt32(), //209
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ContainedItem[] ReadContainedItems(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
var list = new ContainedItem[5];
|
||||||
|
//893
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
list[i].ID = reader.ReadInt16();
|
||||||
|
}
|
||||||
|
|
||||||
|
//903
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
list[i].ObtainChance = reader.ReadSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CraftMaterial[] ReadCraftMaterial(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
var mats = new CraftMaterial[4];
|
||||||
|
//329
|
||||||
|
for (var i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
mats[i].ID = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
//345
|
||||||
|
for (var i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
mats[i].ID = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemOptions ReadItemOptions(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
var options = new ItemOptions();
|
||||||
|
|
||||||
|
options.OptionAvailable = reader.ReadInt32() == 1; //819
|
||||||
|
|
||||||
|
var optionIDs = new List<byte>(4);
|
||||||
|
//823
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
optionIDs.Add((byte)reader.ReadUInt32());
|
||||||
|
}
|
||||||
|
|
||||||
|
options.OptionIDs = optionIDs;
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,9 @@ public class LoginInfoPacket : IPacket
|
||||||
|
|
||||||
public void Deserialize(byte[] data)
|
public void Deserialize(byte[] data)
|
||||||
{
|
{
|
||||||
this.Username = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0');
|
this.Username = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||||
this.Password = Encoding.ASCII.GetString(data, 20, 31).TrimEnd('\0');
|
// Remove unnecessary Symbols
|
||||||
|
this.Password = Encoding.ASCII.GetString(data, 20, 31).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] Serialize()
|
public byte[] Serialize()
|
||||||
|
|
|
@ -4,5 +4,7 @@ public enum OperationCode : ushort
|
||||||
{
|
{
|
||||||
LoginInfo = 11,
|
LoginInfo = 11,
|
||||||
LoginResponse = 12,
|
LoginResponse = 12,
|
||||||
ChannelSelection
|
ChannelSelection = 13,
|
||||||
|
ChannelSelectionResponse = 13,
|
||||||
|
CharacterSelectionSetGuildName = 19,
|
||||||
}
|
}
|
||||||
|
|
14
Wonderking/Packets/Outgoing/BaseStats.cs
Normal file
14
Wonderking/Packets/Outgoing/BaseStats.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class BaseStats
|
||||||
|
{
|
||||||
|
public required short Strength { get; set; }
|
||||||
|
public required short Dexterity { get; set; }
|
||||||
|
public required short Intelligence { get; set; }
|
||||||
|
public required short Vitality { get; set; }
|
||||||
|
public required short Luck { get; set; }
|
||||||
|
public required short Wisdom { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
|
[PacketId(OperationCode.ChannelSelectionResponse)]
|
||||||
|
public class ChannelSelectionResponsePacket : IPacket
|
||||||
|
{
|
||||||
|
public required byte ChannelIsFullFlag { get; set; }
|
||||||
|
public required string Endpoint { get; set; }
|
||||||
|
public required ushort Port { get; set; }
|
||||||
|
public required CharacterData[] Characters { get; set; }
|
||||||
|
|
||||||
|
public void Deserialize(byte[] data)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
Span<byte> data = stackalloc byte[1 + 16 + 2 + 1 + 132 * this.Characters.Length];
|
||||||
|
data.Clear();
|
||||||
|
data[0] = this.ChannelIsFullFlag;
|
||||||
|
Encoding.ASCII.GetBytes(this.Endpoint, data.Slice(1, 16));
|
||||||
|
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(17, 2), this.Port);
|
||||||
|
data[19] = (byte)this.Characters.Length;
|
||||||
|
|
||||||
|
// Character Data
|
||||||
|
for (var i = 0; i < Characters.Length; i++)
|
||||||
|
{
|
||||||
|
var character = Characters[i];
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(20 + (i * 132), 4), i);
|
||||||
|
Encoding.ASCII.GetBytes(character.Name, data.Slice(24 + (i * 132), 20));
|
||||||
|
|
||||||
|
// Job Data
|
||||||
|
data[44 + (i * 132)] = character.Job.FirstJob;
|
||||||
|
data[45 + (i * 132)] = character.Job.SecondJob;
|
||||||
|
data[46 + (i * 132)] = character.Job.ThirdJob;
|
||||||
|
data[47 + (i * 132)] = character.Job.FourthJob;
|
||||||
|
|
||||||
|
data[48 + (i * 132)] = (byte)character.Gender;
|
||||||
|
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(49 + (i * 132), 2), character.Level);
|
||||||
|
data[51 + (i * 132)] = (byte)character.Experience;
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(52 + (i * 132), 2), character.Stats.Strength);
|
||||||
|
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(54 + (i * 132), 2), character.Stats.Dexterity);
|
||||||
|
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(56 + (i * 132), 2), character.Stats.Intelligence);
|
||||||
|
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(58 + (i * 132), 2), character.Stats.Vitality);
|
||||||
|
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(60 + (i * 132), 2), character.Stats.Luck);
|
||||||
|
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(62 + (i * 132), 2), character.Stats.Wisdom);
|
||||||
|
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(64 + (i * 132), 4), character.Health);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(68 + (i * 132), 4), character.Mana);
|
||||||
|
|
||||||
|
for (var j = 0; j < 20; j++)
|
||||||
|
{
|
||||||
|
// Equipped Items
|
||||||
|
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(72 + (i * 132) + (j * 2), 2),
|
||||||
|
character.EquippedItems.Length > j ? character.EquippedItems[j] : (ushort)0);
|
||||||
|
|
||||||
|
// Equipped Cash Items
|
||||||
|
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(112 + (i * 132) + (j * 2), 2),
|
||||||
|
character.EquippedCashItems.Length > j ? character.EquippedCashItems[j] : (ushort)0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.ToArray();
|
||||||
|
}
|
||||||
|
}
|
17
Wonderking/Packets/Outgoing/CharacterData.cs
Normal file
17
Wonderking/Packets/Outgoing/CharacterData.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using Wonderking.Game.Data.Character;
|
||||||
|
|
||||||
|
namespace Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
|
public struct CharacterData
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required JobData Job { get; set; }
|
||||||
|
public required Gender Gender { get; set; }
|
||||||
|
public required ushort Level { get; set; }
|
||||||
|
public required float Experience { get; set; }
|
||||||
|
public required BaseStats Stats { get; set; }
|
||||||
|
public required int Health { get; set; }
|
||||||
|
public required int Mana { get; set; }
|
||||||
|
public required ushort[] EquippedItems { get; set; }
|
||||||
|
public required ushort[] EquippedCashItems { get; set; }
|
||||||
|
}
|
5
Wonderking/Packets/Outgoing/CharacterGuildInfo.cs
Normal file
5
Wonderking/Packets/Outgoing/CharacterGuildInfo.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
|
public struct CharacterGuildInfo
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
|
[PacketId(OperationCode.CharacterSelectionSetGuildName)]
|
||||||
|
public class CharacterSelectionSetGuildNamePacket : IPacket
|
||||||
|
{
|
||||||
|
public required string[] GuildNames { get; set; }
|
||||||
|
|
||||||
|
public void Deserialize(byte[] data)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
Span<byte> data = stackalloc byte[1 + (16 + 1 + 1) * this.GuildNames.Length];
|
||||||
|
data.Clear();
|
||||||
|
data[0] = (byte)this.GuildNames.Length;
|
||||||
|
for (var i = 0; i < this.GuildNames.Length; i++)
|
||||||
|
{
|
||||||
|
data[1 + (i * (16 + 1 + 1))] = (byte)i;
|
||||||
|
Encoding.ASCII.GetBytes(this.GuildNames[i], data.Slice(2 + (i * (16 + 1 + 1)), 16));
|
||||||
|
// Null terminator
|
||||||
|
data[18 + (i * (16 + 1 + 1))] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.ToArray();
|
||||||
|
}
|
||||||
|
}
|
12
Wonderking/Packets/Outgoing/JobData.cs
Normal file
12
Wonderking/Packets/Outgoing/JobData.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Wonderking.Packets.Outgoing;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class JobData
|
||||||
|
{
|
||||||
|
public required byte FirstJob { get; set; }
|
||||||
|
public required byte SecondJob { get; set; }
|
||||||
|
public required byte ThirdJob { get; set; }
|
||||||
|
public required byte FourthJob { get; set; }
|
||||||
|
}
|
36
Wonderking/Utils/ByteArrayConverter.cs
Normal file
36
Wonderking/Utils/ByteArrayConverter.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Wonderking.Utils;
|
||||||
|
|
||||||
|
public class ByteArrayConverter : JsonConverter<byte[]>
|
||||||
|
{
|
||||||
|
public override byte[] Read(
|
||||||
|
ref Utf8JsonReader reader,
|
||||||
|
Type typeToConvert,
|
||||||
|
JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var hexData = reader.GetString();
|
||||||
|
if (hexData != null)
|
||||||
|
{
|
||||||
|
return hexData.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
|
||||||
|
}
|
||||||
|
throw new JsonException("Hex string is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(
|
||||||
|
Utf8JsonWriter writer,
|
||||||
|
byte[]? value,
|
||||||
|
JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
writer.WriteNullValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var hexData = BitConverter.ToString(value).Replace("-", string.Empty);
|
||||||
|
writer.WriteStringValue(hexData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,9 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
|
|
||||||
<Features>strict</Features>
|
<Features>strict</Features>
|
||||||
|
<LangVersion>12</LangVersion>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -12,8 +13,8 @@
|
||||||
<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="JetBrains.Annotations" Version="2023.2.0" />
|
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.104">
|
<PackageReference Include="Meziantou.Analyzer" Version="2.0.109">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -30,5 +31,13 @@
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Game\Writer\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
2
build-image.ps1
Normal file
2
build-image.ps1
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#!sh
|
||||||
|
docker build --platform linux/arm64,linux/amd64 -f Server/Dockerfile -t continuity .
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "7.0.404",
|
"version": "8.0.100",
|
||||||
"rollForward": "latestMajor",
|
"rollForward": "latestMinor",
|
||||||
"allowPrerelease": true
|
"allowPrerelease": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue