chore: reformatting
All checks were successful
Build, Package and Push Images / preprocess (push) Successful in 2s
Build, Package and Push Images / build (push) Successful in 25s
Build, Package and Push Images / sbom-scan (push) Successful in 49s
Build, Package and Push Images / container-build (push) Successful in 2m11s
Build, Package and Push Images / sonarqube (push) Successful in 2m12s
Build, Package and Push Images / container-sbom-scan (push) Successful in 39s
All checks were successful
Build, Package and Push Images / preprocess (push) Successful in 2s
Build, Package and Push Images / build (push) Successful in 25s
Build, Package and Push Images / sbom-scan (push) Successful in 49s
Build, Package and Push Images / container-build (push) Successful in 2m11s
Build, Package and Push Images / sonarqube (push) Successful in 2m12s
Build, Package and Push Images / container-sbom-scan (push) Successful in 39s
This commit is contained in:
parent
db63e581e5
commit
9f3007b10e
40 changed files with 416 additions and 379 deletions
|
@ -12,11 +12,6 @@ repos:
|
|||
hooks:
|
||||
- id: actionlint
|
||||
additional_dependencies: [pyflakes>=3.0.1, shellcheck-py>=0.9.0.5]
|
||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||
rev: v2.11.0
|
||||
hooks:
|
||||
- id: pretty-format-yaml
|
||||
args: [--autofix, --indent, '2']
|
||||
- repo: https://github.com/hadolint/hadolint
|
||||
rev: v2.12.0
|
||||
hooks:
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
namespace Benchmarks;
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Security.Cryptography;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Order;
|
||||
|
||||
namespace Benchmarks;
|
||||
|
||||
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
||||
[Config(typeof(GenericConfig))]
|
||||
public class BinaryConversionBenchmarks
|
||||
|
@ -15,48 +16,53 @@ public class BinaryConversionBenchmarks
|
|||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
this._data = RandomNumberGenerator.GetBytes(4000);
|
||||
this._offset = RandomNumberGenerator.GetInt32(0, 3500);
|
||||
this._writeBuffer = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
|
||||
_data = RandomNumberGenerator.GetBytes(4000);
|
||||
_offset = RandomNumberGenerator.GetInt32(0, 3500);
|
||||
_writeBuffer = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public short BitConverterParseTest() => BitConverter.ToInt16(this._data, this._offset);
|
||||
public short BitConverterParseTest()
|
||||
{
|
||||
return BitConverter.ToInt16(_data, _offset);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public short BinaryReader()
|
||||
{
|
||||
using var ms = new MemoryStream(this._data);
|
||||
using var ms = new MemoryStream(_data);
|
||||
using var reader = new BinaryReader(ms);
|
||||
reader.BaseStream.Position = this._offset;
|
||||
reader.BaseStream.Position = _offset;
|
||||
return reader.ReadInt16();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public short BinaryPrimitivesRead() =>
|
||||
System.Buffers.Binary.BinaryPrimitives.ReadInt16LittleEndian(
|
||||
new ArraySegment<byte>(this._data, this._offset, sizeof(short)));
|
||||
public short BinaryPrimitivesRead()
|
||||
{
|
||||
return BinaryPrimitives.ReadInt16LittleEndian(
|
||||
new ArraySegment<byte>(_data, _offset, sizeof(short)));
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BinaryPrimitivesWrite()
|
||||
{
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteInt32LittleEndian(this._data.AsSpan(_offset, 4),
|
||||
this._writeBuffer);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(_data.AsSpan(_offset, 4),
|
||||
_writeBuffer);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BitConverterCopy()
|
||||
{
|
||||
BitConverter.GetBytes(this._writeBuffer).CopyTo(this._data, this._offset);
|
||||
BitConverter.GetBytes(_writeBuffer).CopyTo(_data, _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];
|
||||
var bytes = BitConverter.GetBytes(_writeBuffer);
|
||||
_data[_offset] = bytes[0];
|
||||
_data[_offset + 1] = bytes[1];
|
||||
_data[_offset + 2] = bytes[2];
|
||||
_data[_offset + 3] = bytes[3];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Benchmarks;
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Order;
|
||||
|
||||
namespace Benchmarks;
|
||||
|
||||
[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 Dictionary<int, int> _dictionary;
|
||||
private HashSet<int> _hashSet;
|
||||
private ImmutableHashSet<int> _immutableHashSet;
|
||||
[Params(1000, 100000, 1000000)] public int N;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
using System.Net.Sockets;
|
||||
using Wonderking.Packets;
|
||||
|
||||
namespace Server;
|
||||
|
||||
using System.Reflection;
|
||||
using MassTransit.Mediator;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
using Packets;
|
||||
using Server.Packets;
|
||||
using Wonderking.Packets;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class AuthSession : TcpSession
|
||||
{
|
||||
|
@ -17,22 +16,22 @@ public class AuthSession : TcpSession
|
|||
public AuthSession(TcpServer
|
||||
server, IMediator mediator, ILogger<AuthSession> logger) : base(server)
|
||||
{
|
||||
this._mediator = mediator;
|
||||
this._logger = logger;
|
||||
_mediator = mediator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Guid AccountId { get; set; }
|
||||
|
||||
public override long Send(byte[] buffer)
|
||||
{
|
||||
this._logger.LogInformation("Data being sent is: {Data}", BitConverter.ToString(buffer));
|
||||
_logger.LogInformation("Data being sent is: {Data}", BitConverter.ToString(buffer));
|
||||
return base.Send(buffer);
|
||||
}
|
||||
|
||||
public Task SendAsync(IPacket packet)
|
||||
{
|
||||
var type = packet.GetType();
|
||||
this._logger.LogInformation("Packet of type {Type} is being serialized", type.Name);
|
||||
_logger.LogInformation("Packet of type {Type} is being serialized", type.Name);
|
||||
var packetIdAttribute = type.GetCustomAttribute<PacketIdAttribute>();
|
||||
if (packetIdAttribute == null)
|
||||
{
|
||||
|
@ -60,41 +59,41 @@ public class AuthSession : TcpSession
|
|||
buffer[2 + i] = bytesOfOpcode[i];
|
||||
}
|
||||
|
||||
this._logger.LogInformation("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
|
||||
this._logger.LogInformation("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));
|
||||
_logger.LogInformation("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
|
||||
_logger.LogInformation("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));
|
||||
|
||||
this.SendAsync(buffer);
|
||||
SendAsync(buffer);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override void OnReceived(byte[] buffer, long offset, long size)
|
||||
{
|
||||
this._logger.LogDebug("Length: {Size} & offset: {Offset}", size, offset);
|
||||
_logger.LogDebug("Length: {Size} & offset: {Offset}", size, offset);
|
||||
Span<byte> decryptedBuffer = new byte[size];
|
||||
|
||||
// xor every value after the first 8 bytes
|
||||
var dataBuffer = Decrypt(new ArraySegment<byte>(buffer, 8, (int)size - 8).ToArray());
|
||||
|
||||
this._logger.LogDebug("Length {Length}", BitConverter.ToUInt16(buffer, 0));
|
||||
_logger.LogDebug("Length {Length}", BitConverter.ToUInt16(buffer, 0));
|
||||
|
||||
var opCode = BitConverter.ToUInt16(buffer.ToArray(), 2);
|
||||
|
||||
this._logger.LogDebug("Packet Op Code: {OpCode}", opCode);
|
||||
this._logger.LogDebug("Some Value: {RandomValue}", buffer[4]);
|
||||
_logger.LogDebug("Packet Op Code: {OpCode}", opCode);
|
||||
_logger.LogDebug("Some Value: {RandomValue}", buffer[4]);
|
||||
|
||||
var clientAliveTime = BitConverter.ToUInt16(buffer.ToArray(), 5);
|
||||
|
||||
this._logger.LogDebug("Client Alive time: {ClientAliveTime}", clientAliveTime);
|
||||
this._logger.LogDebug("Might be a flag: {Flag}", buffer[7]);
|
||||
_logger.LogDebug("Client Alive time: {ClientAliveTime}", clientAliveTime);
|
||||
_logger.LogDebug("Might be a flag: {Flag}", buffer[7]);
|
||||
|
||||
this._logger.LogDebug("Full buffer: {Buffer}", BitConverter.ToString(dataBuffer.ToArray()));
|
||||
_logger.LogDebug("Full buffer: {Buffer}", BitConverter.ToString(dataBuffer.ToArray()));
|
||||
|
||||
var rawPacket = new RawPacket((OperationCode)opCode, dataBuffer, clientAliveTime, buffer[7],
|
||||
buffer[4], this.Id, this);
|
||||
buffer[4], Id, this);
|
||||
|
||||
_ = this._mediator.Send(rawPacket);
|
||||
_ = _mediator.Send(rawPacket);
|
||||
|
||||
this._logger.LogInformation("Connection from: {@RemoteEndpoint}", this.Socket.RemoteEndPoint?.ToString());
|
||||
_logger.LogInformation("Connection from: {@RemoteEndpoint}", Socket.RemoteEndPoint?.ToString());
|
||||
base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
namespace Server;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using MassTransit.Mediator;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class ChannelSession : TcpSession
|
||||
{
|
||||
private static readonly byte[] _key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7 }
|
||||
|
@ -24,8 +24,8 @@ public class ChannelSession : TcpSession
|
|||
|
||||
public ChannelSession(TcpServer server, IMediator mediator, ILogger<ChannelSession> logger) : base(server)
|
||||
{
|
||||
this._mediator = mediator;
|
||||
this._logger = logger;
|
||||
_mediator = mediator;
|
||||
_logger = logger;
|
||||
var aes = Aes.Create();
|
||||
aes.Key = _key;
|
||||
aes.IV = _iv;
|
||||
|
@ -34,8 +34,8 @@ public class ChannelSession : TcpSession
|
|||
aes.Mode = CipherMode.ECB;
|
||||
#pragma warning restore SEC0026
|
||||
|
||||
this._decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||
this._encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||
_decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||
_encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||
}
|
||||
|
||||
protected override void OnReceived(byte[] buffer, long offset, long size)
|
||||
|
@ -43,12 +43,12 @@ public class ChannelSession : TcpSession
|
|||
try
|
||||
{
|
||||
using (var ms = new MemoryStream(Decrypt(buffer)))
|
||||
using (var cs = new CryptoStream(ms, this._decryptor, CryptoStreamMode.Read))
|
||||
using (var cs = new CryptoStream(ms, _decryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
var amountOfReadBytes = cs.Read(buffer);
|
||||
if (amountOfReadBytes != buffer.Length)
|
||||
{
|
||||
this._logger.LogError("Amount of read bytes is not equal to buffer length.");
|
||||
_logger.LogError("Amount of read bytes is not equal to buffer length.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,8 +56,8 @@ public class ChannelSession : TcpSession
|
|||
}
|
||||
catch (CryptographicException ex)
|
||||
{
|
||||
this._logger.LogError("An error has occured while decrypting: {ErrorMessage}", ex.Message);
|
||||
this._logger.LogError("Default buffer message: {Message}", Encoding.ASCII.GetString(buffer));
|
||||
_logger.LogError("An error has occured while decrypting: {ErrorMessage}", ex.Message);
|
||||
_logger.LogError("Default buffer message: {Message}", Encoding.ASCII.GetString(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
namespace Server.Consumers;
|
||||
|
||||
using MassTransit;
|
||||
using Packets;
|
||||
using Services;
|
||||
using Server.Packets;
|
||||
using Server.Services;
|
||||
|
||||
namespace Server.Consumers;
|
||||
|
||||
public class PacketConsumer : IConsumer<RawPacket>
|
||||
{
|
||||
private readonly PacketDistributorService _distributorService;
|
||||
|
||||
public PacketConsumer(PacketDistributorService distributorService) => this._distributorService = distributorService;
|
||||
public PacketConsumer(PacketDistributorService distributorService)
|
||||
{
|
||||
_distributorService = distributorService;
|
||||
}
|
||||
|
||||
public Task Consume(ConsumeContext<RawPacket> context)
|
||||
{
|
||||
this._distributorService.AddPacket(context.Message);
|
||||
_distributorService.AddPacket(context.Message);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,17 @@ using Microsoft.EntityFrameworkCore;
|
|||
|
||||
namespace Server.DB.Documents;
|
||||
|
||||
[Index(nameof(Username), IsUnique = true), Index(nameof(Id), IsUnique = true)]
|
||||
[Index(nameof(Username), IsUnique = true)]
|
||||
[Index(nameof(Id), IsUnique = true)]
|
||||
public class Account
|
||||
{
|
||||
public Account(string username, byte[] password, string email, byte permissionLevel, byte[] salt)
|
||||
{
|
||||
this.Username = username;
|
||||
this.Password = password;
|
||||
this.Email = email;
|
||||
this.PermissionLevel = permissionLevel;
|
||||
this.Salt = salt;
|
||||
Username = username;
|
||||
Password = password;
|
||||
Email = email;
|
||||
PermissionLevel = permissionLevel;
|
||||
Salt = salt;
|
||||
}
|
||||
|
||||
[Key]
|
||||
|
|
|
@ -6,7 +6,8 @@ using Wonderking.Packets.Outgoing.Data;
|
|||
|
||||
namespace Server.DB.Documents;
|
||||
|
||||
[Index(nameof(Name), IsUnique = true), Index(nameof(Id), IsUnique = true)]
|
||||
[Index(nameof(Name), IsUnique = true)]
|
||||
[Index(nameof(Id), IsUnique = true)]
|
||||
public class Character
|
||||
{
|
||||
public virtual Account Account { get; set; }
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Server.DB.Documents;
|
||||
|
||||
namespace Server.DB;
|
||||
|
||||
using Documents;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class WonderkingContext : DbContext
|
||||
{
|
||||
public WonderkingContext([NotNull] DbContextOptions options) : base(options)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
using Server.DB;
|
||||
using Server.DB.Documents;
|
||||
using Wonderking.Packets.Incoming;
|
||||
using Wonderking.Packets.Outgoing;
|
||||
|
@ -6,11 +10,6 @@ using Wonderking.Packets.Outgoing.Data;
|
|||
|
||||
namespace Server.PacketHandlers;
|
||||
|
||||
using DB;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
|
||||
public class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket>
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
@ -20,9 +19,9 @@ public class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket>
|
|||
public ChannelSelectionHandler(IConfiguration configuration, ILogger<ChannelSelectionHandler> logger,
|
||||
WonderkingContext wonderkingContext)
|
||||
{
|
||||
this._configuration = configuration;
|
||||
this._logger = logger;
|
||||
this._wonderkingContext = wonderkingContext;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
_wonderkingContext = wonderkingContext;
|
||||
}
|
||||
|
||||
public async Task HandleAsync(ChannelSelectionPacket packet, TcpSession session)
|
||||
|
@ -31,7 +30,7 @@ public class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket>
|
|||
ChannelSelectionResponsePacket responsePacket;
|
||||
var guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { GuildNames = Array.Empty<string>() };
|
||||
|
||||
var account = await this._wonderkingContext.Accounts
|
||||
var account = await _wonderkingContext.Accounts
|
||||
.FirstOrDefaultAsync(a => a.Id == authSession.AccountId).ConfigureAwait(true);
|
||||
if (account != null && account.Characters.Count > 0)
|
||||
{
|
||||
|
@ -59,9 +58,9 @@ public class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket>
|
|||
.Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).ToArray()),
|
||||
EquippedCashItems = GetItemIDsByInventoryTab(c.InventoryItems
|
||||
.Where(item => item.InventoryTab == InventoryTab.WornCashEquipment)
|
||||
.Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).ToArray()),
|
||||
.Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).ToArray())
|
||||
})
|
||||
.ToArrayAsync().ConfigureAwait(true),
|
||||
.ToArrayAsync().ConfigureAwait(true)
|
||||
};
|
||||
|
||||
guildNameResponsePacket.GuildNames = await _wonderkingContext.Characters
|
||||
|
|
|
@ -13,9 +13,9 @@ namespace Server.PacketHandlers;
|
|||
|
||||
public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
|
||||
{
|
||||
private readonly WonderkingContext _wonderkingContext;
|
||||
private readonly ItemObjectPoolService _itemObjectPoolService;
|
||||
private readonly CharacterStatsMappingConfiguration _characterStatsMapping;
|
||||
private readonly ItemObjectPoolService _itemObjectPoolService;
|
||||
private readonly WonderkingContext _wonderkingContext;
|
||||
|
||||
public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService,
|
||||
CharacterStatsMappingConfiguration characterStatsMappingConfiguration)
|
||||
|
@ -62,7 +62,7 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
|
|||
_itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 +
|
||||
packet.Shirt + 49)),
|
||||
_itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 +
|
||||
packet.Pants + 58)),
|
||||
packet.Pants + 58))
|
||||
];
|
||||
|
||||
var calculateCurrentMana = CalculateCurrentMana(1, firstJobConfig);
|
||||
|
@ -109,13 +109,13 @@ public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
|
|||
.Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).ToArray()),
|
||||
EquippedCashItems = GetItemIDsByInventoryTab(c.InventoryItems
|
||||
.Where(item => item.InventoryTab == InventoryTab.WornCashEquipment)
|
||||
.Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).ToArray()),
|
||||
.Select(item => new Tuple<ushort, byte>(item.ItemId, item.Slot)).ToArray())
|
||||
}).FirstAsync().ConfigureAwait(true);
|
||||
await authSession.SendAsync(new CharacterCreationResponsePacket
|
||||
{
|
||||
Character = character,
|
||||
Slot = packet.Slot,
|
||||
isDuplicate = false,
|
||||
isDuplicate = false
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
using JetBrains.Annotations;
|
||||
using NetCoreServer;
|
||||
using Wonderking.Packets;
|
||||
|
||||
namespace Server.PacketHandlers;
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using NetCoreServer;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IPacketHandler<in T> where T : IPacket
|
||||
{
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Konscious.Security.Cryptography;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
using Server.DB;
|
||||
using Server.DB.Documents;
|
||||
using Server.LoggerMessages;
|
||||
using Wonderking.Packets.Incoming;
|
||||
using Wonderking.Packets.Outgoing;
|
||||
|
@ -7,13 +13,6 @@ using Wonderking.Packets.Outgoing.Data;
|
|||
|
||||
namespace Server.PacketHandlers;
|
||||
|
||||
using DB;
|
||||
using DB.Documents;
|
||||
using Konscious.Security.Cryptography;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
|
||||
public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
@ -22,9 +21,55 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
|||
|
||||
public LoginHandler(ILogger<LoginHandler> logger, WonderkingContext wonderkingContext, IConfiguration configuration)
|
||||
{
|
||||
this._logger = logger;
|
||||
this._wonderkingContext = wonderkingContext;
|
||||
this._configuration = configuration;
|
||||
_logger = logger;
|
||||
_wonderkingContext = wonderkingContext;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
|
||||
{
|
||||
LoginResponseReason loginResponseReason;
|
||||
_logger.LoginData(packet.Username, packet.Password);
|
||||
var account = _wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
if (_configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
|
||||
{
|
||||
loginResponseReason = await CreateAccountOnLoginAsync(packet.Username, packet.Password)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.RequestedAccountDoesNotExist(packet.Username);
|
||||
loginResponseReason = LoginResponseReason.AccountDoesNotExit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var salt = account.Salt;
|
||||
var tempPasswordBytes = await GetPasswordHashAsync(packet.Password, salt, account.Id)
|
||||
.ConfigureAwait(false);
|
||||
loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password)
|
||||
? LoginResponseReason.Ok
|
||||
: LoginResponseReason.WrongPassword;
|
||||
}
|
||||
|
||||
var loginResponsePacket = new LoginResponsePacket
|
||||
{
|
||||
ResponseReason = loginResponseReason,
|
||||
ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 0, ServerId = 0 } },
|
||||
UnknownFlag = 1,
|
||||
IsGameMaster = true
|
||||
};
|
||||
var authSession = session as AuthSession;
|
||||
if (account != null && authSession != null)
|
||||
{
|
||||
authSession.AccountId = account.Id;
|
||||
}
|
||||
|
||||
_logger.LogInformation("LoginResponsePacket: {@LoginResponsePacket}", loginResponsePacket);
|
||||
authSession?.SendAsync(loginResponsePacket);
|
||||
}
|
||||
|
||||
private static Task<byte[]> GetPasswordHashAsync(string password, byte[] salt, Guid userId)
|
||||
|
@ -53,16 +98,16 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
|||
{
|
||||
var salt = RandomNumberGenerator.GetBytes(16);
|
||||
var finalAccount =
|
||||
await this._wonderkingContext.Accounts.AddAsync(new Account(username,
|
||||
await _wonderkingContext.Accounts.AddAsync(new Account(username,
|
||||
Array.Empty<byte>(), "",
|
||||
0, salt)).ConfigureAwait(true);
|
||||
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
||||
await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
||||
finalAccount.Entity.Password =
|
||||
await LoginHandler.GetPasswordHashAsync(password, salt, finalAccount.Entity.Id)
|
||||
await GetPasswordHashAsync(password, salt, finalAccount.Entity.Id)
|
||||
.ConfigureAwait(true);
|
||||
this._wonderkingContext.Accounts.Update(finalAccount.Entity);
|
||||
_wonderkingContext.Accounts.Update(finalAccount.Entity);
|
||||
loginResponseReason = LoginResponseReason.Ok;
|
||||
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
||||
await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
||||
|
||||
await transaction.CommitAsync().ConfigureAwait(true);
|
||||
}
|
||||
|
@ -75,50 +120,4 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
|||
|
||||
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 (this._configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
|
||||
{
|
||||
loginResponseReason = await CreateAccountOnLoginAsync(packet.Username, packet.Password)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._logger.RequestedAccountDoesNotExist(packet.Username);
|
||||
loginResponseReason = LoginResponseReason.AccountDoesNotExit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var salt = account.Salt;
|
||||
var tempPasswordBytes = await LoginHandler.GetPasswordHashAsync(packet.Password, salt, account.Id)
|
||||
.ConfigureAwait(false);
|
||||
loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password)
|
||||
? LoginResponseReason.Ok
|
||||
: LoginResponseReason.WrongPassword;
|
||||
}
|
||||
|
||||
var loginResponsePacket = new LoginResponsePacket
|
||||
{
|
||||
ResponseReason = loginResponseReason,
|
||||
ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 0, ServerId = 0 } },
|
||||
UnknownFlag = 1,
|
||||
IsGameMaster = true
|
||||
};
|
||||
var sess = session as AuthSession;
|
||||
if (account != null && sess != null)
|
||||
{
|
||||
sess.AccountId = account.Id;
|
||||
}
|
||||
|
||||
_logger.LogInformation("LoginResponsePacket: {@LoginResponsePacket}", loginResponsePacket);
|
||||
sess?.SendAsync(loginResponsePacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
using MassTransit;
|
||||
using Wonderking.Packets;
|
||||
|
||||
namespace Server.Packets;
|
||||
|
||||
using MassTransit;
|
||||
|
||||
[MessageUrn("packets")]
|
||||
public class RawPacket
|
||||
{
|
||||
public RawPacket(OperationCode operationCode, byte[] messageBody, uint aliveTime, byte unknownValue2,
|
||||
byte unknownValue, Guid sessionId, AuthSession session)
|
||||
{
|
||||
this.MessageBody = messageBody;
|
||||
this.UnknownValue2 = unknownValue2;
|
||||
this.UnknownValue = unknownValue;
|
||||
this.SessionId = sessionId;
|
||||
this.Session = session;
|
||||
this.OperationCode = operationCode;
|
||||
MessageBody = messageBody;
|
||||
UnknownValue2 = unknownValue2;
|
||||
UnknownValue = unknownValue;
|
||||
SessionId = sessionId;
|
||||
Session = session;
|
||||
OperationCode = operationCode;
|
||||
/*
|
||||
* 20s = 5
|
||||
* 15s = 4
|
||||
* 10s = 3
|
||||
* client alive time * 5s => uptime
|
||||
*/
|
||||
this.ClientAliveTime = TimeSpan.FromSeconds(5 * aliveTime);
|
||||
ClientAliveTime = TimeSpan.FromSeconds(5 * aliveTime);
|
||||
}
|
||||
|
||||
public OperationCode OperationCode { get; }
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Server.Services;
|
|||
|
||||
public class ItemObjectPoolService : IHostedService
|
||||
{
|
||||
readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool;
|
||||
private readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool;
|
||||
private readonly ItemReader _itemReader;
|
||||
private readonly ILogger<ItemObjectPoolService> _logger;
|
||||
|
||||
|
@ -66,7 +66,7 @@ public class ItemObjectPoolService : IHostedService
|
|||
|
||||
public InventoryItem GetBaseInventoryItem(ushort itemId, ushort count = 1, bool isWorn = false)
|
||||
{
|
||||
var item = this.GetItem(itemId);
|
||||
var item = GetItem(itemId);
|
||||
return new InventoryItem
|
||||
{
|
||||
ItemId = itemId,
|
||||
|
|
|
@ -1,38 +1,41 @@
|
|||
using Server.LoggerMessages;
|
||||
using Wonderking.Packets;
|
||||
|
||||
namespace Server.Services;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using DotNext.Collections.Generic;
|
||||
using DotNext.Linq.Expressions;
|
||||
using DotNext.Metaprogramming;
|
||||
using MassTransit.Internals;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualBasic.CompilerServices;
|
||||
using Newtonsoft.Json;
|
||||
using PacketHandlers;
|
||||
using Packets;
|
||||
using static DotNext.Linq.Expressions.ExpressionBuilder;
|
||||
using static DotNext.Metaprogramming.CodeGenerator;
|
||||
using Server.LoggerMessages;
|
||||
using Server.PacketHandlers;
|
||||
using Server.Packets;
|
||||
using Wonderking.Packets;
|
||||
|
||||
namespace Server.Services;
|
||||
|
||||
using static CodeGenerator;
|
||||
using static ExpressionBuilder;
|
||||
|
||||
public class PacketDistributorService : IHostedService
|
||||
{
|
||||
private readonly ConcurrentQueue<RawPacket> _concurrentQueue;
|
||||
|
||||
private readonly ILogger<PacketDistributorService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
private ImmutableDictionary<OperationCode,
|
||||
Func<byte[], IPacket>> _deserializationMap;
|
||||
|
||||
private readonly ILogger<PacketDistributorService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private ConcurrentDictionary<OperationCode, object> _packetHandlersInstantiation;
|
||||
|
||||
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
|
||||
{
|
||||
this._concurrentQueue = new ConcurrentQueue<RawPacket>();
|
||||
this._logger = logger;
|
||||
_concurrentQueue = new ConcurrentQueue<RawPacket>();
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
|
@ -42,15 +45,15 @@ public class PacketDistributorService : IHostedService
|
|||
new Dictionary<OperationCode, Func<byte[], IPacket>>();
|
||||
|
||||
var wonderkingAssembly = Assembly.GetAssembly(typeof(IPacket));
|
||||
var packetsTypes = this.GetPacketsWithId(wonderkingAssembly);
|
||||
var packetHandlers = this.GetAllPacketHandlersWithId(Assembly.GetExecutingAssembly());
|
||||
this._packetHandlersInstantiation = new ConcurrentDictionary<OperationCode, object>();
|
||||
var packetsTypes = GetPacketsWithId(wonderkingAssembly);
|
||||
var packetHandlers = GetAllPacketHandlersWithId(Assembly.GetExecutingAssembly());
|
||||
_packetHandlersInstantiation = new ConcurrentDictionary<OperationCode, object>();
|
||||
packetHandlers.ForEach(x =>
|
||||
{
|
||||
var packetHandler =
|
||||
ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider,
|
||||
x.Value);
|
||||
this._packetHandlersInstantiation.TryAdd(x.Key, packetHandler);
|
||||
_packetHandlersInstantiation.TryAdd(x.Key, packetHandler);
|
||||
});
|
||||
foreach (var packetsType in packetsTypes)
|
||||
{
|
||||
|
@ -69,11 +72,14 @@ public class PacketDistributorService : IHostedService
|
|||
tempDeserializationMap.Add(packetsType.Key, lambda);
|
||||
}
|
||||
|
||||
this._deserializationMap = tempDeserializationMap.ToImmutableDictionary();
|
||||
_deserializationMap = tempDeserializationMap.ToImmutableDictionary();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Dictionary<OperationCode, Type> GetPacketsWithId(Assembly executingAssembly)
|
||||
{
|
||||
|
@ -87,11 +93,11 @@ public class PacketDistributorService : IHostedService
|
|||
if (packetsWithId is not { Count: 0 })
|
||||
{
|
||||
packetsWithId.AsParallel()
|
||||
.ForAll(packet => this._logger.PacketWithIdAdded(packet.Key, packet.Value.FullName));
|
||||
.ForAll(packet => _logger.PacketWithIdAdded(packet.Key, packet.Value.FullName));
|
||||
return packetsWithId;
|
||||
}
|
||||
|
||||
this._logger.NoPacketsFound();
|
||||
_logger.NoPacketsFound();
|
||||
throw new IncompleteInitialization();
|
||||
}
|
||||
|
||||
|
@ -119,44 +125,44 @@ public class PacketDistributorService : IHostedService
|
|||
if (packetHandlersWithId is not { Count: 0 })
|
||||
{
|
||||
packetHandlersWithId.AsParallel().ForAll(packetHandler =>
|
||||
this._logger.PacketHandlerWithIdAdded(packetHandler.Key, packetHandler.Value.FullName));
|
||||
_logger.PacketHandlerWithIdAdded(packetHandler.Key, packetHandler.Value.FullName));
|
||||
return packetHandlersWithId;
|
||||
}
|
||||
|
||||
this._logger.NoPacketHandlersFound();
|
||||
_logger.NoPacketHandlersFound();
|
||||
throw new IncompleteInitialization();
|
||||
}
|
||||
|
||||
public void AddPacket(RawPacket rawPacket)
|
||||
{
|
||||
this._concurrentQueue.Enqueue(rawPacket);
|
||||
this.DequeueRawPacket();
|
||||
this._logger.PacketReceived(rawPacket.OperationCode);
|
||||
_concurrentQueue.Enqueue(rawPacket);
|
||||
DequeueRawPacket();
|
||||
_logger.PacketReceived(rawPacket.OperationCode);
|
||||
}
|
||||
|
||||
private void DequeueRawPacket()
|
||||
{
|
||||
if (this._concurrentQueue.TryDequeue(out var item))
|
||||
if (_concurrentQueue.TryDequeue(out var item))
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(this.InvokePacketHandler, item, true);
|
||||
ThreadPool.QueueUserWorkItem(InvokePacketHandler, item, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void InvokePacketHandler(RawPacket item)
|
||||
{
|
||||
this._logger.PacketDequeued(item.Session.Id, item.OperationCode);
|
||||
if (!this._deserializationMap.TryGetValue(item.OperationCode, out var value))
|
||||
_logger.PacketDequeued(item.Session.Id, item.OperationCode);
|
||||
if (!_deserializationMap.TryGetValue(item.OperationCode, out var value))
|
||||
{
|
||||
this._logger.PacketTypeNotFound(item.OperationCode);
|
||||
_logger.PacketTypeNotFound(item.OperationCode);
|
||||
return;
|
||||
}
|
||||
|
||||
var packet = value(item.MessageBody);
|
||||
this._logger.PacketData(JsonConvert.SerializeObject(packet));
|
||||
this._packetHandlersInstantiation[item.OperationCode].GetType()
|
||||
_logger.PacketData(JsonConvert.SerializeObject(packet));
|
||||
_packetHandlersInstantiation[item.OperationCode].GetType()
|
||||
.GetMethod(nameof(IPacketHandler<IPacket>.HandleAsync))
|
||||
?.Invoke(this._packetHandlersInstantiation[item.OperationCode], new object[] { packet, item.Session });
|
||||
?.Invoke(_packetHandlersInstantiation[item.OperationCode], new object[] { packet, item.Session });
|
||||
|
||||
this._logger.PacketFinished(item.Session.Id, item.OperationCode);
|
||||
_logger.PacketFinished(item.Session.Id, item.OperationCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
namespace Server.Services;
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
@ -7,6 +5,8 @@ using Microsoft.Extensions.Hosting;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
|
||||
namespace Server.Services;
|
||||
|
||||
public class WonderkingAuthServer : TcpServer, IHostedService
|
||||
{
|
||||
private readonly ILogger<WonderkingAuthServer> _logger;
|
||||
|
@ -15,61 +15,66 @@ public class WonderkingAuthServer : TcpServer, IHostedService
|
|||
public WonderkingAuthServer(IPAddress address, int port, ILogger<WonderkingAuthServer> logger,
|
||||
IServiceProvider serviceProvider) : base(address, port)
|
||||
{
|
||||
this._logger = logger;
|
||||
this._serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.Start();
|
||||
Start();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.DisconnectAll();
|
||||
this.Stop();
|
||||
DisconnectAll();
|
||||
Stop();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override TcpSession CreateSession() =>
|
||||
ActivatorUtilities.CreateInstance<AuthSession>(this._serviceProvider, this);
|
||||
protected override TcpSession CreateSession()
|
||||
{
|
||||
return ActivatorUtilities.CreateInstance<AuthSession>(_serviceProvider, this);
|
||||
}
|
||||
|
||||
protected override void OnStarting()
|
||||
{
|
||||
this._logger.LogInformation("Starting");
|
||||
_logger.LogInformation("Starting");
|
||||
base.OnStarting();
|
||||
}
|
||||
|
||||
protected override void OnStarted()
|
||||
{
|
||||
this._logger.LogInformation("Started");
|
||||
_logger.LogInformation("Started");
|
||||
base.OnStarted();
|
||||
}
|
||||
|
||||
protected override void OnStopping()
|
||||
{
|
||||
this._logger.LogInformation("Stopping");
|
||||
_logger.LogInformation("Stopping");
|
||||
base.OnStopping();
|
||||
}
|
||||
|
||||
protected override void OnStopped()
|
||||
{
|
||||
this._logger.LogInformation("Stopped");
|
||||
_logger.LogInformation("Stopped");
|
||||
base.OnStopped();
|
||||
}
|
||||
|
||||
protected override void OnConnected(TcpSession session)
|
||||
{
|
||||
this._logger.LogInformation("Client connected {Session}", session.Id);
|
||||
_logger.LogInformation("Client connected {Session}", session.Id);
|
||||
base.OnConnected(session);
|
||||
}
|
||||
|
||||
protected override void OnDisconnected(TcpSession session)
|
||||
{
|
||||
this._logger.LogInformation("Client disconnected {Session}", session.Id);
|
||||
_logger.LogInformation("Client disconnected {Session}", session.Id);
|
||||
base.OnDisconnected(session);
|
||||
}
|
||||
|
||||
protected override void OnError(SocketError error) => this._logger.LogError("An error has occured {Error}", error);
|
||||
protected override void OnError(SocketError error)
|
||||
{
|
||||
_logger.LogError("An error has occured {Error}", error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,3 @@ public struct CraftMaterial
|
|||
public uint ID;
|
||||
public uint Amount;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,51 +5,67 @@ namespace Wonderking.Game.Data.Item;
|
|||
[StructLayout(LayoutKind.Explicit, Size = 64)]
|
||||
public struct ElementalStats
|
||||
{
|
||||
[FieldOffset(0), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(0)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MinimumFireDamage;
|
||||
|
||||
[FieldOffset(4), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(4)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MinimumWaterDamage;
|
||||
|
||||
[FieldOffset(8), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(8)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MinimumDarkDamage;
|
||||
|
||||
[FieldOffset(12), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(12)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MinimumHolyDamage;
|
||||
|
||||
[FieldOffset(16), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(16)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MaximumFireDamage;
|
||||
|
||||
[FieldOffset(20), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(20)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MaximumWaterDamage;
|
||||
|
||||
[FieldOffset(24), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(24)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MaximumDarkDamage;
|
||||
|
||||
[FieldOffset(28), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(28)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int MaximumHolyDamage;
|
||||
|
||||
[FieldOffset(32), MarshalAs(UnmanagedType.U4)]
|
||||
[FieldOffset(32)]
|
||||
[MarshalAs(UnmanagedType.U4)]
|
||||
public uint ElementFire;
|
||||
|
||||
[FieldOffset(36), MarshalAs(UnmanagedType.U4)]
|
||||
[FieldOffset(36)]
|
||||
[MarshalAs(UnmanagedType.U4)]
|
||||
public uint ElementWater;
|
||||
|
||||
[FieldOffset(40), MarshalAs(UnmanagedType.U4)]
|
||||
[FieldOffset(40)]
|
||||
[MarshalAs(UnmanagedType.U4)]
|
||||
public uint ElementDark;
|
||||
|
||||
[FieldOffset(44), MarshalAs(UnmanagedType.U4)]
|
||||
[FieldOffset(44)]
|
||||
[MarshalAs(UnmanagedType.U4)]
|
||||
public uint ElementHoly;
|
||||
|
||||
[FieldOffset(48), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(48)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int FireResistance;
|
||||
|
||||
[FieldOffset(52), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(52)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int WaterResistance;
|
||||
|
||||
[FieldOffset(56), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(56)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int DarkResistance;
|
||||
|
||||
[FieldOffset(60), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(60)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int HolyResistance;
|
||||
}
|
||||
|
|
|
@ -5,21 +5,27 @@ namespace Wonderking.Game.Data.Item;
|
|||
[StructLayout(LayoutKind.Explicit, Size = 24)]
|
||||
public struct Stats
|
||||
{
|
||||
[FieldOffset(0), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(0)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Strength;
|
||||
|
||||
[FieldOffset(4), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(4)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Dexterity;
|
||||
|
||||
[FieldOffset(8), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(8)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Intelligence;
|
||||
|
||||
[FieldOffset(12), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(12)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Vitality;
|
||||
|
||||
[FieldOffset(16), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(16)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Luck;
|
||||
|
||||
[FieldOffset(20), MarshalAs(UnmanagedType.I4)]
|
||||
[FieldOffset(20)]
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Wisdom;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,11 @@ namespace Wonderking.Game;
|
|||
|
||||
public abstract class DataReader<T>
|
||||
{
|
||||
private readonly string _datFileName;
|
||||
|
||||
private readonly byte _xorKey;
|
||||
protected readonly ushort SizeOfEntry;
|
||||
|
||||
protected DataReader(string path)
|
||||
{
|
||||
Path = path;
|
||||
|
@ -15,6 +20,8 @@ public abstract class DataReader<T>
|
|||
|
||||
private protected string Path { get; init; }
|
||||
|
||||
protected byte[] DatFileContent { get; }
|
||||
|
||||
public abstract uint GetAmountOfEntries();
|
||||
public abstract T GetEntry(uint entryId);
|
||||
|
||||
|
@ -36,20 +43,14 @@ public abstract class DataReader<T>
|
|||
throw new NotSupportedException("XorKey is null");
|
||||
}
|
||||
|
||||
private readonly byte _xorKey;
|
||||
protected readonly ushort SizeOfEntry;
|
||||
private readonly string _datFileName;
|
||||
|
||||
protected byte[] DatFileContent { get; }
|
||||
|
||||
private Span<byte> GetDatFileContent(string path)
|
||||
{
|
||||
var fileData = File.ReadAllBytes(path + this._datFileName);
|
||||
var fileData = File.ReadAllBytes(path + _datFileName);
|
||||
var data = new byte[fileData.Length];
|
||||
|
||||
for (var i = 0; i < fileData.Length; i++)
|
||||
{
|
||||
data[i] = (byte)(fileData[i] ^ this._xorKey);
|
||||
data[i] = (byte)(fileData[i] ^ _xorKey);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
|
|
@ -4,8 +4,7 @@ namespace Wonderking.Game.Mapping;
|
|||
|
||||
public class Item
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public ushort Id { get; set; }
|
||||
[JsonPropertyName("quantity")]
|
||||
public ushort Quantity { get; set; }
|
||||
[JsonPropertyName("id")] public ushort Id { get; set; }
|
||||
|
||||
[JsonPropertyName("quantity")] public ushort Quantity { get; set; }
|
||||
}
|
||||
|
|
|
@ -5,10 +5,9 @@ namespace Wonderking.Game.Mapping;
|
|||
|
||||
public class JobSpecificMapping
|
||||
{
|
||||
[JsonPropertyName("items")]
|
||||
public ICollection<Item> Items { get; set; }
|
||||
[JsonPropertyName("baseStats")]
|
||||
public BaseStats BaseStats { get; set; }
|
||||
[JsonPropertyName("dynamicStats")]
|
||||
public DynamicStats DynamicStats { get; set; }
|
||||
[JsonPropertyName("items")] public ICollection<Item> Items { get; set; }
|
||||
|
||||
[JsonPropertyName("baseStats")] public BaseStats BaseStats { get; set; }
|
||||
|
||||
[JsonPropertyName("dynamicStats")] public DynamicStats DynamicStats { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/* Nicht gemergte Änderung aus Projekt "Wonderking(net7.0)"
|
||||
Vor:
|
||||
using System.Runtime.InteropServices;
|
||||
|
|
|
@ -9,14 +9,14 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
|
|||
{
|
||||
public override uint GetAmountOfEntries()
|
||||
{
|
||||
return (uint)((this.DatFileContent.Length - 9) / this.SizeOfEntry);
|
||||
return (uint)((DatFileContent.Length - 9) / SizeOfEntry);
|
||||
}
|
||||
|
||||
public override ItemObject GetEntry(uint entryId)
|
||||
{
|
||||
var item = new ItemObject();
|
||||
var arraySegment = new ArraySegment<byte>(DatFileContent,
|
||||
9 + (int)entryId * this.SizeOfEntry, this.SizeOfEntry);
|
||||
9 + (int)entryId * SizeOfEntry, SizeOfEntry);
|
||||
var data = new Span<byte>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
|
||||
item.ItemID = BitConverter.ToUInt32(data.Slice(0, 4)); // 0 -> 4
|
||||
item.Disabled = BitConverter.ToBoolean(data.Slice(4, 4)); // 4 -> 8
|
||||
|
@ -49,7 +49,7 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
|
|||
Intelligence = BitConverter.ToInt32(data.Slice(124, 4)), // 124 -> 128
|
||||
Vitality = BitConverter.ToInt32(data.Slice(128, 4)), // 128 -> 132
|
||||
Luck = BitConverter.ToInt32(data.Slice(132, 4)), // 132 -> 136
|
||||
Wisdom = BitConverter.ToInt32(data.Slice(136, 4)), // 136 -> 140
|
||||
Wisdom = BitConverter.ToInt32(data.Slice(136, 4)) // 136 -> 140
|
||||
}; // 116 -> 140
|
||||
item.ElementalStats = new ElementalStats
|
||||
{
|
||||
|
@ -68,7 +68,7 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
|
|||
FireResistance = BitConverter.ToInt32(data.Slice(188, 4)), // 188 -> 192
|
||||
WaterResistance = BitConverter.ToInt32(data.Slice(192, 4)), // 192 -> 196
|
||||
DarkResistance = BitConverter.ToInt32(data.Slice(196, 4)), // 196 -> 200
|
||||
HolyResistance = BitConverter.ToInt32(data.Slice(200, 4)), // 200 -> 204
|
||||
HolyResistance = BitConverter.ToInt32(data.Slice(200, 4)) // 200 -> 204
|
||||
}; // 140 -> 204
|
||||
item.R7C = data.Slice(204, 4).ToArray(); // 204 -> 208
|
||||
item.R8C = data.Slice(208, 8).ToArray(); // 208 -> 216
|
||||
|
@ -118,7 +118,7 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
|
|||
{
|
||||
ID = BitConverter.ToUInt32(data.Slice(332, 4)), // 332 -> 336
|
||||
Amount = BitConverter.ToUInt32(data.Slice(348, 4)) // 348 -> 352
|
||||
},
|
||||
}
|
||||
}; // 320 -> 352
|
||||
item.CraftResultAmount = BitConverter.ToUInt32(data.Slice(352, 4)); // 352 -> 356
|
||||
item.R14C = data.Slice(356, 4).ToArray(); // 356 -> 360
|
||||
|
@ -141,7 +141,7 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
|
|||
BitConverter.ToUInt32(data.Slice(800, 4)), // 800 -> 804
|
||||
BitConverter.ToUInt32(data.Slice(804, 4)), // 804 -> 808
|
||||
BitConverter.ToUInt32(data.Slice(808, 4)), // 808 -> 812
|
||||
BitConverter.ToUInt32(data.Slice(812, 4)), // 812 -> 816
|
||||
BitConverter.ToUInt32(data.Slice(812, 4)) // 812 -> 816
|
||||
}; // 796 -> 816
|
||||
item.SetID = BitConverter.ToUInt32(data.Slice(816, 4)); // 816 -> 820
|
||||
item.Options = new ItemOptions
|
||||
|
@ -151,9 +151,9 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
|
|||
BitConverter.ToUInt32(data.Slice(824, 4)), // 824 -> 828
|
||||
BitConverter.ToUInt32(data.Slice(828, 4)), // 828 -> 832
|
||||
BitConverter.ToUInt32(data.Slice(832, 4)), // 832 -> 836
|
||||
BitConverter.ToUInt32(data.Slice(836, 4)), // 836 -> 840
|
||||
BitConverter.ToUInt32(data.Slice(836, 4)) // 836 -> 840
|
||||
},
|
||||
OptionAvailable = BitConverter.ToBoolean(data.Slice(820, 4)), // 820 -> 824
|
||||
OptionAvailable = BitConverter.ToBoolean(data.Slice(820, 4)) // 820 -> 824
|
||||
}; // 820 -> 840
|
||||
item.Unknown19 = data.Slice(840, 23).ToArray(); // 840 -> 863
|
||||
item.PetID = data[863]; // 863 -> 864
|
||||
|
@ -186,7 +186,7 @@ public class ItemReader(string path) : DataReader<ItemObject>(path)
|
|||
{
|
||||
ID = BitConverter.ToInt16(data.Slice(906, 2)), // 906 -> 908
|
||||
ObtainChance = BitConverter.ToSingle(data.Slice(924, 4)) // 924 -> 928
|
||||
},
|
||||
}
|
||||
};
|
||||
item.MinimumLevelRequirement = data[928]; // 928 -> 929
|
||||
item.Unknown21_2 = data.Slice(929, 3).ToArray(); // 929 -> 932
|
||||
|
|
|
@ -6,20 +6,20 @@ public static class ItemReaderExtensions
|
|||
{
|
||||
public static Stats ReadStats(this BinaryReader reader)
|
||||
{
|
||||
return new Stats()
|
||||
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
|
||||
Wisdom = reader.ReadInt32() //145
|
||||
};
|
||||
}
|
||||
|
||||
public static ElementalStats ReadElementalStats(this BinaryReader reader)
|
||||
{
|
||||
return new ElementalStats()
|
||||
return new ElementalStats
|
||||
{
|
||||
MinimumFireDamage = reader.ReadInt32(), //149
|
||||
MinimumWaterDamage = reader.ReadInt32(), //153
|
||||
|
@ -36,7 +36,7 @@ public static class ItemReaderExtensions
|
|||
FireResistance = reader.ReadInt32(), //197
|
||||
WaterResistance = reader.ReadInt32(), //201
|
||||
DarkResistance = reader.ReadInt32(), //205
|
||||
HolyResistance = reader.ReadInt32(), //209
|
||||
HolyResistance = reader.ReadInt32() //209
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,12 @@ public class ChannelSelectionPacket : IPacket
|
|||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
this.ServerId = BitConverter.ToUInt16(data, 0);
|
||||
this.ChannelId = BitConverter.ToUInt16(data, 2);
|
||||
ServerId = BitConverter.ToUInt16(data, 0);
|
||||
ChannelId = BitConverter.ToUInt16(data, 2);
|
||||
}
|
||||
|
||||
public byte[] Serialize() => throw new NotSupportedException();
|
||||
public byte[] Serialize()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,22 +11,22 @@ public class LoginInfoPacket : IPacket
|
|||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
this.Username = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||
Username = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||
// Remove unnecessary Symbols
|
||||
this.Password = Encoding.ASCII.GetString(data, 20, 31).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||
Password = Encoding.ASCII.GetString(data, 20, 31).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
Span<byte> dataSpan = stackalloc byte[20 + 31];
|
||||
var usernameBytes = Encoding.ASCII.GetBytes(this.Username);
|
||||
var passwordBytes = Encoding.ASCII.GetBytes(this.Password);
|
||||
for (var i = 0; i < 20 || i < this.Username.Length; i++)
|
||||
var usernameBytes = Encoding.ASCII.GetBytes(Username);
|
||||
var passwordBytes = Encoding.ASCII.GetBytes(Password);
|
||||
for (var i = 0; i < 20 || i < Username.Length; i++)
|
||||
{
|
||||
dataSpan[i] = usernameBytes[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < 31 || i < this.Password.Length; i++)
|
||||
for (var i = 0; i < 31 || i < Password.Length; i++)
|
||||
{
|
||||
dataSpan[20 + i] = passwordBytes[i];
|
||||
}
|
||||
|
|
|
@ -13,5 +13,5 @@ public enum OperationCode : ushort
|
|||
CharacterDeletion = 16,
|
||||
CharacterDeletionResponse = 16,
|
||||
CharacterSelection = 17,
|
||||
CharacterSelectionSetGuildName = 19,
|
||||
CharacterSelectionSetGuildName = 19
|
||||
}
|
||||
|
|
|
@ -19,17 +19,17 @@ public class ChannelSelectionResponsePacket : IPacket
|
|||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
Span<byte> data = stackalloc byte[1 + 16 + 2 + 1 + 132 * this.Characters.Length];
|
||||
Span<byte> data = stackalloc byte[1 + 16 + 2 + 1 + 132 * 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;
|
||||
data[0] = ChannelIsFullFlag;
|
||||
Encoding.ASCII.GetBytes(Endpoint, data.Slice(1, 16));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(17, 2), Port);
|
||||
data[19] = (byte)Characters.Length;
|
||||
|
||||
// Character Data
|
||||
for (var i = 0; i < Characters.Length; i++)
|
||||
{
|
||||
int offset = 20 + (i * 132);
|
||||
var offset = 20 + i * 132;
|
||||
var character = Characters[i];
|
||||
// Character Data
|
||||
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset, 4), i);
|
||||
|
|
|
@ -13,7 +13,7 @@ public class CharacterNameCheckPacketResponse : IPacket
|
|||
public byte[] Serialize()
|
||||
{
|
||||
Span<byte> data = stackalloc byte[1];
|
||||
data[0] = this.IsTaken ? (byte)1 : (byte)0;
|
||||
data[0] = IsTaken ? (byte)1 : (byte)0;
|
||||
return data.ToArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,15 +14,15 @@ public class CharacterSelectionSetGuildNamePacket : IPacket
|
|||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
Span<byte> data = stackalloc byte[1 + (1 + 16 + 1) * this.GuildNames.Length];
|
||||
Span<byte> data = stackalloc byte[1 + (1 + 16 + 1) * GuildNames.Length];
|
||||
data.Clear();
|
||||
data[0] = (byte)this.GuildNames.Length;
|
||||
for (var i = 0; i < this.GuildNames.Length; i++)
|
||||
data[0] = (byte)GuildNames.Length;
|
||||
for (var i = 0; i < GuildNames.Length; i++)
|
||||
{
|
||||
data[1 + (i * (1 + 16 + 1))] = (byte)i;
|
||||
Encoding.ASCII.GetBytes(this.GuildNames[i], data.Slice(2 + (i * (1 + 16 + 1)), 16));
|
||||
data[1 + i * (1 + 16 + 1)] = (byte)i;
|
||||
Encoding.ASCII.GetBytes(GuildNames[i], data.Slice(2 + i * (1 + 16 + 1), 16));
|
||||
// Null terminator
|
||||
data[18 + (i * (1 + 16 + 1))] = 0;
|
||||
data[18 + i * (1 + 16 + 1)] = 0;
|
||||
}
|
||||
|
||||
return data.ToArray();
|
||||
|
|
|
@ -14,38 +14,38 @@ public class LoginResponsePacket : IPacket
|
|||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
this.ResponseReason = (LoginResponseReason)data[0];
|
||||
this.UnknownFlag = data[1];
|
||||
this.IsGameMaster = BitConverter.ToBoolean(data, 2);
|
||||
ResponseReason = (LoginResponseReason)data[0];
|
||||
UnknownFlag = data[1];
|
||||
IsGameMaster = BitConverter.ToBoolean(data, 2);
|
||||
var channelAmount = BitConverter.ToUInt16(data, 3);
|
||||
const int sizeOfServerChannelData = 5;
|
||||
this.ChannelData = Enumerable.Repeat(0, channelAmount).Select(i => new ServerChannelData
|
||||
ChannelData = Enumerable.Repeat(0, channelAmount).Select(i => new ServerChannelData
|
||||
{
|
||||
ServerId = BitConverter.ToUInt16(data, 5 + 0 + (i * sizeOfServerChannelData)),
|
||||
ChannelId = BitConverter.ToUInt16(data, 5 + 2 + (i * sizeOfServerChannelData)),
|
||||
LoadPercentage = data[5 + 4 + (i * sizeOfServerChannelData)]
|
||||
ServerId = BitConverter.ToUInt16(data, 5 + 0 + i * sizeOfServerChannelData),
|
||||
ChannelId = BitConverter.ToUInt16(data, 5 + 2 + i * sizeOfServerChannelData),
|
||||
LoadPercentage = data[5 + 4 + i * sizeOfServerChannelData]
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
const int sizeOfServerChannelData = 5;
|
||||
Span<byte> dataSpan = stackalloc byte[5 + (this.ChannelData.Length * sizeOfServerChannelData)];
|
||||
Span<byte> dataSpan = stackalloc byte[5 + ChannelData.Length * sizeOfServerChannelData];
|
||||
dataSpan.Clear();
|
||||
dataSpan[0] = (byte)this.ResponseReason;
|
||||
dataSpan[1] = this.UnknownFlag;
|
||||
dataSpan[2] = BitConverter.GetBytes(this.IsGameMaster)[0];
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(dataSpan.Slice(3, 2), (ushort)this.ChannelData.Length);
|
||||
dataSpan[0] = (byte)ResponseReason;
|
||||
dataSpan[1] = UnknownFlag;
|
||||
dataSpan[2] = BitConverter.GetBytes(IsGameMaster)[0];
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(dataSpan.Slice(3, 2), (ushort)ChannelData.Length);
|
||||
|
||||
for (var i = 0; i < this.ChannelData.Length; i++)
|
||||
for (var i = 0; i < ChannelData.Length; i++)
|
||||
{
|
||||
var bytesOfServerId = BitConverter.GetBytes(this.ChannelData[i].ServerId);
|
||||
var bytesOfChannelId = BitConverter.GetBytes(this.ChannelData[i].ChannelId);
|
||||
dataSpan[5 + 0 + (i * sizeOfServerChannelData)] = bytesOfServerId[0];
|
||||
dataSpan[5 + 1 + (i * sizeOfServerChannelData)] = bytesOfServerId[1];
|
||||
dataSpan[5 + 2 + (i * sizeOfServerChannelData)] = bytesOfChannelId[0];
|
||||
dataSpan[5 + 3 + (i * sizeOfServerChannelData)] = bytesOfChannelId[1];
|
||||
dataSpan[5 + 4 + (i * sizeOfServerChannelData)] = this.ChannelData[i].LoadPercentage;
|
||||
var bytesOfServerId = BitConverter.GetBytes(ChannelData[i].ServerId);
|
||||
var bytesOfChannelId = BitConverter.GetBytes(ChannelData[i].ChannelId);
|
||||
dataSpan[5 + 0 + i * sizeOfServerChannelData] = bytesOfServerId[0];
|
||||
dataSpan[5 + 1 + i * sizeOfServerChannelData] = bytesOfServerId[1];
|
||||
dataSpan[5 + 2 + i * sizeOfServerChannelData] = bytesOfChannelId[0];
|
||||
dataSpan[5 + 3 + i * sizeOfServerChannelData] = bytesOfChannelId[1];
|
||||
dataSpan[5 + 4 + i * sizeOfServerChannelData] = ChannelData[i].LoadPercentage;
|
||||
}
|
||||
|
||||
return dataSpan.ToArray();
|
||||
|
|
|
@ -3,6 +3,10 @@ namespace Wonderking.Packets;
|
|||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class PacketIdAttribute : Attribute
|
||||
{
|
||||
public PacketIdAttribute(OperationCode code) => this.Code = code;
|
||||
public PacketIdAttribute(OperationCode code)
|
||||
{
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public OperationCode Code { get; }
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ public class ByteArrayConverter : JsonConverter<byte[]>
|
|||
{
|
||||
return hexData.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
|
||||
}
|
||||
|
||||
throw new JsonException("Hex string is null.");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue