M NosSmooth.sln => NosSmooth.sln +15 -0
@@ 44,6 44,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataBrowser", "Samples\Data
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Game", "Core\NosSmooth.Game\NosSmooth.Game.csproj", "{7C9C7375-6FC0-4704-9332-1F74CDF41D11}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileClient", "Samples\FileClient\FileClient.csproj", "{D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ 210,6 212,18 @@ Global
{055C66A7-640C-49BB-81A7-28E630F51C37}.Release|x64.Build.0 = Release|Any CPU
{055C66A7-640C-49BB-81A7-28E630F51C37}.Release|x86.ActiveCfg = Release|Any CPU
{055C66A7-640C-49BB-81A7-28E630F51C37}.Release|x86.Build.0 = Release|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Debug|x64.Build.0 = Debug|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Debug|x86.Build.0 = Debug|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Release|x64.ActiveCfg = Release|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Release|x64.Build.0 = Release|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Release|x86.ActiveCfg = Release|Any CPU
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ 228,6 242,7 @@ Global
{F1884ADF-6412-4E9B-81FD-357DC5761ADF} = {1C785A74-19B9-42D2-93B1-F4EC9D6A8CFD}
{7C9C7375-6FC0-4704-9332-1F74CDF41D11} = {01B5E872-271F-4D30-A1AA-AD48D81840C5}
{055C66A7-640C-49BB-81A7-28E630F51C37} = {99E72557-BCE9-496A-B49C-79537B0E6063}
+ {D33E1AC5-8946-4D6F-A6D3-D81F98E4F86B} = {99E72557-BCE9-496A-B49C-79537B0E6063}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C5F46653-4DEC-429B-8580-4ED18ED9B4CA}
M Packets/NosSmooth.Packets/Server/Maps/OutPacket.cs => Packets/NosSmooth.Packets/Server/Maps/OutPacket.cs +1 -1
@@ 14,7 14,7 @@ namespace NosSmooth.Packets.Server.Maps;
/// </summary>
/// <param name="EntityType">The entity type.</param>
/// <param name="EntityId">The entity id.</param>
-[PacketHeader("c_map", PacketSource.Server)]
+[PacketHeader("out", PacketSource.Server)]
[GenerateSerializer(true)]
public record OutPacket
(
A Samples/FileClient/App.cs => Samples/FileClient/App.cs +77 -0
@@ 0,0 1,77 @@
+//
+// App.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using NosSmooth.Core.Client;
+using NosSmooth.Core.Extensions;
+using NosSmooth.Data.NOSFiles;
+using NosSmooth.Packets.Extensions;
+using NosSmooth.Packets.Packets;
+using Remora.Results;
+
+namespace FileClient;
+
+/// <summary>
+/// The application.
+/// </summary>
+public class App : BackgroundService
+{
+ private readonly INostaleClient _client;
+ private readonly IPacketTypesRepository _packetRepository;
+ private readonly NostaleDataFilesManager _filesManager;
+ private readonly ILogger<App> _logger;
+ private readonly IHostLifetime _lifetime;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="App"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="packetRepository">The packet repository.</param>
+ /// <param name="filesManager">The file manager.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="lifetime">The lifetime.</param>
+ public App
+ (
+ INostaleClient client,
+ IPacketTypesRepository packetRepository,
+ NostaleDataFilesManager filesManager,
+ ILogger<App> logger,
+ IHostLifetime lifetime
+ )
+ {
+ _client = client;
+ _packetRepository = packetRepository;
+ _filesManager = filesManager;
+ _logger = logger;
+ _lifetime = lifetime;
+ }
+
+ /// <inheritdoc />
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ var packetResult = _packetRepository.AddDefaultPackets();
+ if (!packetResult.IsSuccess)
+ {
+ _logger.LogResultError(packetResult);
+ return;
+ }
+
+ var filesResult = _filesManager.Initialize();
+ if (!filesResult.IsSuccess)
+ {
+ _logger.LogResultError(filesResult);
+ return;
+ }
+
+ var runResult = await _client.RunAsync(stoppingToken);
+ if (!runResult.IsSuccess)
+ {
+ _logger.LogResultError(runResult);
+ await _lifetime.StopAsync(default);
+ }
+ }
+}<
\ No newline at end of file
A Samples/FileClient/Client.cs => Samples/FileClient/Client.cs +126 -0
@@ 0,0 1,126 @@
+//
+// Client.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Text.RegularExpressions;
+using Microsoft.Extensions.Logging;
+using NosSmooth.Core.Client;
+using NosSmooth.Core.Commands;
+using NosSmooth.Core.Extensions;
+using NosSmooth.Core.Packets;
+using NosSmooth.Packets;
+using NosSmooth.Packets.Errors;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+using Remora.Results;
+
+namespace FileClient;
+
+/// <summary>
+/// A NosTale client using stream to read lines.
+/// </summary>
+public class Client : BaseNostaleClient
+{
+ private const string LineRegex = ".*\\[(Recv|Send)\\]\t(.*)";
+ private readonly IPacketHandler _packetHandler;
+ private readonly IPacketSerializer _packetSerializer;
+ private readonly ILogger<Client> _logger;
+ private readonly Stream _stream;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Client"/> class.
+ /// </summary>
+ /// <param name="stream">The stream with packets.</param>
+ /// <param name="packetHandler">The packet handler.</param>
+ /// <param name="commandProcessor">The command processor.</param>
+ /// <param name="packetSerializer">The packet serializer.</param>
+ /// <param name="logger">The logger.</param>
+ public Client(
+ Stream stream,
+ IPacketHandler packetHandler,
+ CommandProcessor commandProcessor,
+ IPacketSerializer packetSerializer,
+ ILogger<Client> logger
+ )
+ : base(commandProcessor, packetSerializer)
+ {
+ _stream = stream;
+ _packetHandler = packetHandler;
+ _packetSerializer = packetSerializer;
+ _logger = logger;
+ }
+
+ /// <inheritdoc />
+ public override async Task<Result> RunAsync(CancellationToken stopRequested = default)
+ {
+ using var reader = new StreamReader(_stream);
+ var regex = new Regex(LineRegex);
+ while (!reader.EndOfStream)
+ {
+ stopRequested.ThrowIfCancellationRequested();
+ var line = await reader.ReadLineAsync();
+ if (line is null)
+ {
+ continue;
+ }
+
+ var match = regex.Match(line);
+ if (!match.Success)
+ {
+ _logger.LogError("Could not find match on line {Line}", line);
+ continue;
+ }
+
+ var type = match.Groups[1].Value;
+ var packetStr = match.Groups[2].Value;
+
+ var source = type == "Recv" ? PacketSource.Server : PacketSource.Client;
+ var packet = CreatePacket(packetStr, source);
+ Result result;
+ if (source == PacketSource.Client)
+ {
+ result = await _packetHandler.HandleSentPacketAsync(packet, packetStr, stopRequested);
+ }
+ else
+ {
+ result = await _packetHandler.HandleReceivedPacketAsync(packet, packetStr, stopRequested);
+ }
+
+ if (!result.IsSuccess)
+ {
+ _logger.LogResultError(result);
+ }
+ }
+
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc/>
+ public override async Task<Result> SendPacketAsync(string packetString, CancellationToken ct = default)
+ {
+ return await _packetHandler.HandleReceivedPacketAsync(CreatePacket(packetString, PacketSource.Client), packetString, ct);
+ }
+
+ /// <inheritdoc/>
+ public override async Task<Result> ReceivePacketAsync(string packetString, CancellationToken ct = default)
+ {
+ return await _packetHandler.HandleReceivedPacketAsync(CreatePacket(packetString, PacketSource.Server), packetString, ct);
+ }
+
+ private IPacket CreatePacket(string packetStr, PacketSource source)
+ {
+ var packetResult = _packetSerializer.Deserialize(packetStr, source);
+ if (!packetResult.IsSuccess)
+ {
+ if (packetResult.Error is PacketConverterNotFoundError err)
+ {
+ return new UnresolvedPacket(err.Header, packetStr);
+ }
+
+ return new ParsingFailedPacket(packetResult, packetStr);
+ }
+
+ return packetResult.Entity;
+ }
+}<
\ No newline at end of file
A Samples/FileClient/FileClient.csproj => Samples/FileClient/FileClient.csproj +27 -0
@@ 0,0 1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net6.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Folder Include="Handlers" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\Core\NosSmooth.Core\NosSmooth.Core.csproj" />
+ <ProjectReference Include="..\..\Core\NosSmooth.Game\NosSmooth.Game.csproj" />
+ <ProjectReference Include="..\..\Data\NosSmooth.Data.Abstractions\NosSmooth.Data.Abstractions.csproj" />
+ <ProjectReference Include="..\..\Data\NosSmooth.Data.NOSFiles\NosSmooth.Data.NOSFiles.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
+ </ItemGroup>
+
+</Project>
A Samples/FileClient/Program.cs => Samples/FileClient/Program.cs +66 -0
@@ 0,0 1,66 @@
+//
+// Program.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using NosSmooth.Core.Client;
+using NosSmooth.Core.Commands;
+using NosSmooth.Core.Extensions;
+using NosSmooth.Core.Packets;
+using NosSmooth.Data.Abstractions.Language;
+using NosSmooth.Data.NOSFiles.Extensions;
+using NosSmooth.Data.NOSFiles.Options;
+using NosSmooth.Game.Extensions;
+using NosSmooth.Packets;
+
+namespace FileClient;
+
+/// <summary>
+/// An entrypoint class.
+/// </summary>
+public static class Program
+{
+ // TODO: create console hosting.
+
+ /// <summary>
+ /// An entrypoint method.
+ /// </summary>
+ /// <param name="args">The command line arguments.</param>
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ public static async Task Main(string[] args)
+ {
+ await using FileStream stream = File.OpenRead(string.Join(' ', args));
+ await CreateHost(stream).StartAsync();
+ }
+
+ private static IHost CreateHost(Stream fileStream)
+ {
+ return Host.CreateDefaultBuilder()
+ .ConfigureServices(coll =>
+ {
+ coll.AddHostedService<App>();
+
+ coll.AddNostaleCore()
+ .AddNostaleGame()
+ .AddNostaleDataFiles()
+ .Configure<LanguageServiceOptions>(o => o.Language = Language.Cz)
+ .Configure<NostaleDataOptions>(o => o.SupportedLanguages = new[]
+ {
+ Language.Cz
+ });
+ coll.AddSingleton<INostaleClient>(p => new Client(
+ fileStream,
+ p.GetRequiredService<IPacketHandler>(),
+ p.GetRequiredService<CommandProcessor>(),
+ p.GetRequiredService<IPacketSerializer>(),
+ p.GetRequiredService<ILogger<Client>>()
+ ));
+ })
+ .UseConsoleLifetime()
+ .Build();
+ }
+}<
\ No newline at end of file
M Tests/NosSmooth.Packets.Tests/Converters/Packets/InPacketConverterTests.cs => Tests/NosSmooth.Packets.Tests/Converters/Packets/InPacketConverterTests.cs +3 -2
@@ 11,6 11,7 @@ using NosSmooth.Packets.Enums.Entities;
using NosSmooth.Packets.Enums.Players;
using NosSmooth.Packets.Extensions;
using NosSmooth.Packets.Server.Entities;
+using NosSmooth.Packets.Server.Maps;
using NosSmooth.Packets.Server.Players;
using NosSmooth.Packets.Server.Weapons;
using NosSmooth.PacketSerializer.Abstractions.Attributes;
@@ 90,7 91,7 @@ public class InPacketConverterTests
new UpgradeRareSubPacket(10, 8),
new FamilySubPacket(null, null),
null,
- "26",
+ 26,
false,
0,
0,
@@ 161,7 162,7 @@ public class InPacketConverterTests
new UpgradeRareSubPacket(10, 8),
new FamilySubPacket("-1", null),
null,
- "26",
+ 26,
false,
0,
0,