diff --git a/Server/PacketHandlers/CharacterCreationHandler..cs b/Server/PacketHandlers/CharacterCreationHandler.cs similarity index 51% rename from Server/PacketHandlers/CharacterCreationHandler..cs rename to Server/PacketHandlers/CharacterCreationHandler.cs index 7bd5485..c2ea731 100644 --- a/Server/PacketHandlers/CharacterCreationHandler..cs +++ b/Server/PacketHandlers/CharacterCreationHandler.cs @@ -14,11 +14,14 @@ public class CharacterCreationHandler : IPacketHandler { private readonly WonderkingContext _wonderkingContext; private readonly ItemObjectPoolService _itemObjectPoolService; + private readonly CharacterStatsMappingConfiguration _characterStatsMapping; - public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService) + public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService, + CharacterStatsMappingConfiguration characterStatsMappingConfiguration) { _wonderkingContext = wonderkingContext; _itemObjectPoolService = itemObjectPoolService; + _characterStatsMapping = characterStatsMappingConfiguration; } public async Task HandleAsync(CharacterCreationPacket packet, TcpSession session) @@ -26,14 +29,38 @@ public class CharacterCreationHandler : IPacketHandler var authSession = session as AuthSession; var account = _wonderkingContext.Accounts.FirstOrDefault(a => authSession != null && a.Id == authSession.AccountId); - var items = new List + var mappedDefaultItems = _characterStatsMapping.DefaultCharacterMapping.Items + .Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray(); + + var firstJobConfig = packet.FirstJob switch { - _itemObjectPoolService.GetBaseInventoryItem(25), - _itemObjectPoolService.GetBaseInventoryItem(764), - _itemObjectPoolService.GetBaseInventoryItem(766), - _itemObjectPoolService.GetBaseInventoryItem(763), - _itemObjectPoolService.GetBaseInventoryItem(767) + 1 => _characterStatsMapping.Swordsman, + 2 => _characterStatsMapping.Mage, + 3 => _characterStatsMapping.Thief, + 4 => _characterStatsMapping.Scout, + _ => _characterStatsMapping.Swordsman }; + + var mappedJobItems = firstJobConfig.Items + .Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray(); + InventoryItem[] items = + [ + .. mappedDefaultItems, + .. mappedJobItems, + _itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 + + ((byte)packet.Gender - 1) * 3 + + packet.Hair + 1)), + _itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 + + ((byte)packet.Gender - 1) * 3 + + packet.Eyes + 25)), + _itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 + + packet.Shirt + 49)), + _itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 + + packet.Pants + 58)), + ]; + + var calculateCurrentMana = CalculateCurrentMana(1, firstJobConfig); + var calculateCurrentHealth = CalculateCurrentHealth(1, firstJobConfig); account?.Characters.Add(new Character { Account = account, @@ -42,22 +69,14 @@ public class CharacterCreationHandler : IPacketHandler LastXCoordinate = 113, LastYCoordinate = 0, PvPLevel = PvPLevel.None, - Gender = Gender.None, + Gender = packet.Gender, Experience = 0, Level = 1, InventoryItems = items, - BaseStats = new BaseStats - { - Strength = 5, - Dexterity = 5, - Intelligence = 5, - Vitality = 5, - Luck = 5, - Wisdom = 5 - }, + BaseStats = firstJobConfig.BaseStats, JobData = new JobData { FirstJob = packet.FirstJob, SecondJob = 0, ThirdJob = 0, FourthJob = 0 }, - Health = 250, - Mana = 250, + Health = calculateCurrentHealth, + Mana = calculateCurrentMana }); await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true); @@ -93,4 +112,16 @@ public class CharacterCreationHandler : IPacketHandler isDuplicate = false, }); } + + private static int CalculateCurrentHealth(ushort level, JobSpecificMapping firstJobConfig) + { + return (int)((level - 1) * firstJobConfig.DynamicStats.HealthPerLevel + + firstJobConfig.BaseStats.Vitality * firstJobConfig.DynamicStats.HealthPerVitality); + } + + private static int CalculateCurrentMana(ushort level, JobSpecificMapping firstJobConfig) + { + return (int)((level - 1) * firstJobConfig.DynamicStats.ManaPerLevel + + firstJobConfig.BaseStats.Wisdom * firstJobConfig.DynamicStats.ManaPerWisdom); + } } diff --git a/Server/PacketHandlers/CharacterDeletionHandler.cs b/Server/PacketHandlers/CharacterDeletionHandler.cs index 8a43182..f5de8d9 100644 --- a/Server/PacketHandlers/CharacterDeletionHandler.cs +++ b/Server/PacketHandlers/CharacterDeletionHandler.cs @@ -24,7 +24,8 @@ public class CharacterDeletionHandler : IPacketHandler return; } - var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name) + var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name && + x.Account.Id == authSession.AccountId) .ConfigureAwait(false); if (character == null) { diff --git a/Server/PacketHandlers/CharacterMappingItemEntry.cs b/Server/PacketHandlers/CharacterMappingItemEntry.cs new file mode 100644 index 0000000..055abd0 --- /dev/null +++ b/Server/PacketHandlers/CharacterMappingItemEntry.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Server.PacketHandlers; + +[StructLayout(LayoutKind.Auto)] +public struct CharacterMappingItemEntry +{ + public required ushort Id { get; set; } + public required ushort Quantity { get; set; } +} diff --git a/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs b/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs new file mode 100644 index 0000000..5e02715 --- /dev/null +++ b/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class CharacterStatsMappingConfiguration +{ + [JsonPropertyName("default")] public DefaultCharacterMapping DefaultCharacterMapping { get; set; } + [JsonPropertyName("1")] public JobSpecificMapping Swordsman { get; set; } + + [JsonPropertyName("2")] public JobSpecificMapping Mage { get; set; } + + [JsonPropertyName("3")] public JobSpecificMapping Thief { get; set; } + + [JsonPropertyName("4")] public JobSpecificMapping Scout { get; set; } +} diff --git a/Server/PacketHandlers/DefaultCharacterMapping.cs b/Server/PacketHandlers/DefaultCharacterMapping.cs new file mode 100644 index 0000000..22c267f --- /dev/null +++ b/Server/PacketHandlers/DefaultCharacterMapping.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class DefaultCharacterMapping +{ + [JsonPropertyName("items")] public List Items { get; set; } +} diff --git a/Server/PacketHandlers/DynamicStats.cs b/Server/PacketHandlers/DynamicStats.cs new file mode 100644 index 0000000..09d62e4 --- /dev/null +++ b/Server/PacketHandlers/DynamicStats.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class DynamicStats +{ + [JsonPropertyName("healthPerLevel")] public int HealthPerLevel { get; set; } + [JsonPropertyName("manaPerLevel")] public int ManaPerLevel { get; set; } + + [JsonPropertyName("meleeDamagePerStrength")] + public double MeleeDamagePerStrength { get; set; } + + [JsonPropertyName("rangedDamagePerDexterity")] + public double RangedDamagePerDexterity { get; set; } + + [JsonPropertyName("hitRatingPerDexterity")] + public double HitRatingPerDexterity { get; set; } + + [JsonPropertyName("magicPowerPerIntelligence")] + public double MagicPowerPerIntelligence { get; set; } + + [JsonPropertyName("meleeDamagePerLuck")] + public double MeleeDamagePerLuck { get; set; } + + [JsonPropertyName("rangedDamagePerLuck")] + public double RangedDamagePerLuck { get; set; } + + [JsonPropertyName("evasionPerLuck")] public double EvasionPerLuck { get; set; } + [JsonPropertyName("criticalPerLuck")] public double CriticalPerLuck { get; set; } + + [JsonPropertyName("healthPerVitality")] + public double HealthPerVitality { get; set; } + + [JsonPropertyName("physicalDefensePerVitality")] + public double PhysicalDefensePerVitality { get; set; } + + [JsonPropertyName("manaPerWisdom")] public double ManaPerWisdom { get; set; } + + [JsonPropertyName("elementalDefensePerWisdom")] + public double ElementalDefensePerWisdom { get; set; } + + [JsonPropertyName("elementalPowerPerMagicPower")] + public double ElementalPowerPerMagicPower { get; set; } + + [JsonPropertyName("elementalDefensePerMagicPower")] + public double ElementalDefensePerMagicPower { get; set; } +} diff --git a/Server/PacketHandlers/Item.cs b/Server/PacketHandlers/Item.cs new file mode 100644 index 0000000..259ef5c --- /dev/null +++ b/Server/PacketHandlers/Item.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class Item +{ + [JsonPropertyName("id")] + public ushort Id { get; set; } + [JsonPropertyName("quantity")] + public ushort Quantity { get; set; } +} diff --git a/Server/PacketHandlers/JobSpecificMapping.cs b/Server/PacketHandlers/JobSpecificMapping.cs new file mode 100644 index 0000000..03afe7e --- /dev/null +++ b/Server/PacketHandlers/JobSpecificMapping.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using Wonderking.Packets.Outgoing.Data; + +namespace Server.PacketHandlers; + +public class JobSpecificMapping +{ + [JsonPropertyName("items")] + public ICollection Items { get; set; } + [JsonPropertyName("baseStats")] + public BaseStats BaseStats { get; set; } + [JsonPropertyName("dynamicStats")] + public DynamicStats DynamicStats { get; set; } +} diff --git a/Server/Program.cs b/Server/Program.cs index d9f97d9..5d2892b 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -1,5 +1,6 @@ using System.Net; using System.Reflection; +using System.Text.Json; using MassTransit; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -7,15 +8,21 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Server.DB; +using Server.PacketHandlers; using Server.Services; var builder = Host.CreateApplicationBuilder(); #if DEBUG builder.Environment.EnvironmentName = "Development"; #endif + builder.Configuration.AddJsonFile("settings.json", true, true) .AddJsonFile($"settings.{builder.Environment.EnvironmentName}.json", true) .AddEnvironmentVariables().Build(); +builder.Services.AddSingleton( + JsonSerializer.Deserialize( + File.ReadAllText("config/character-stats.mapping.json")) ?? throw new InvalidOperationException()); + builder.Services.AddLogging(); var loggerFactory = LoggerFactory.Create(loggingBuilder => { @@ -23,6 +30,7 @@ var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder.AddFile("logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true); loggingBuilder.AddConsole(); }); + builder.Services.AddDbContextPool(o => { using var configuration = builder.Configuration; @@ -30,6 +38,7 @@ builder.Services.AddDbContextPool(o => $"Host={configuration["DB:Host"]};Username={configuration["DB:Username"]};Password={configuration["DB:Password"]};Database={configuration["DB:Database"]};Port={configuration["DB:Port"]}") .EnableSensitiveDataLogging().UseLazyLoadingProxies().UseLoggerFactory(loggerFactory); }); + builder.Services.AddSingleton(loggerFactory); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/Server/Services/PacketDistributorService.cs b/Server/Services/PacketDistributorService.cs index 5bc2d43..a6e89c6 100644 --- a/Server/Services/PacketDistributorService.cs +++ b/Server/Services/PacketDistributorService.cs @@ -22,17 +22,22 @@ public class PacketDistributorService : IHostedService { private readonly ConcurrentQueue _concurrentQueue; - private readonly - ImmutableDictionary> _deserializationMap; + private ImmutableDictionary> _deserializationMap; private readonly ILogger _logger; - private readonly ConcurrentDictionary _packetHandlersInstantiation; + private readonly IServiceProvider _serviceProvider; + private ConcurrentDictionary _packetHandlersInstantiation; public PacketDistributorService(ILogger logger, IServiceProvider serviceProvider) { this._concurrentQueue = new ConcurrentQueue(); this._logger = logger; + _serviceProvider = serviceProvider; + } + + public Task StartAsync(CancellationToken cancellationToken) + { var tempDeserializationMap = new Dictionary>(); @@ -43,7 +48,7 @@ public class PacketDistributorService : IHostedService packetHandlers.ForEach(x => { var packetHandler = - ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, + ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, x.Value); this._packetHandlersInstantiation.TryAdd(x.Key, packetHandler); }); @@ -60,15 +65,14 @@ public class PacketDistributorService : IHostedService Return(packetVariable); }).Compile(); - logger.PacketCreationFunctionCreated(packetsType.Key); + _logger.PacketCreationFunctionCreated(packetsType.Key); tempDeserializationMap.Add(packetsType.Key, lambda); } this._deserializationMap = tempDeserializationMap.ToImmutableDictionary(); + return Task.CompletedTask; } - public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; private Dictionary GetPacketsWithId(Assembly executingAssembly) diff --git a/Wonderking/Packets/Outgoing/Data/BaseStats.cs b/Wonderking/Packets/Outgoing/Data/BaseStats.cs index 1dfba40..0ed902f 100644 --- a/Wonderking/Packets/Outgoing/Data/BaseStats.cs +++ b/Wonderking/Packets/Outgoing/Data/BaseStats.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; @@ -7,10 +8,10 @@ namespace Wonderking.Packets.Outgoing.Data; [Owned] 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; } + [JsonPropertyName("strength")] public required short Strength { get; set; } + [JsonPropertyName("dexterity")] public required short Dexterity { get; set; } + [JsonPropertyName("intelligence")] public required short Intelligence { get; set; } + [JsonPropertyName("vitality")] public required short Vitality { get; set; } + [JsonPropertyName("luck")] public required short Luck { get; set; } + [JsonPropertyName("wisdom")] public required short Wisdom { get; set; } }