diff --git a/.github/workflows/docker_image.yml b/.github/workflows/docker_image.yml index 98a70b6..5ff733f 100644 --- a/.github/workflows/docker_image.yml +++ b/.github/workflows/docker_image.yml @@ -1,4 +1,4 @@ -name: OrionIrc Build docker image +name: Prima Build docker image on: push: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..8d987da --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,50 @@ +name: Create docs + +# Your GitHub workflow file under .github/workflows/ +# Trigger the action on push to main +on: + push: + branches: [ main ] + repository_dispatch: + types: [version-updated] + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + actions: read + pages: write + id-token: write + contents: read + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + publish-docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Dotnet Setup + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.x + + - run: dotnet tool update -g docfx + - run: docfx ./docs/docfx.json + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: './docs/_site' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/versionize-workflow.yml b/.github/workflows/versionize-workflow.yml index 4a4d364..c963f87 100644 --- a/.github/workflows/versionize-workflow.yml +++ b/.github/workflows/versionize-workflow.yml @@ -30,7 +30,7 @@ jobs: id: versionize continue-on-error: true run: | - # Esegui versionize per generare il changelog e incrementare la versione + versionize EXIT_CODE=$? if [ $EXIT_CODE -eq 0 ]; then diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 0000000..3bc1fc2 --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,75 @@ +{ + "metadata": [ + { + "src": [ + { + "src": "../src", + "files": [ + "**/*.csproj" + ], + "exclude": [ + "**/bin/**", + "**/obj/**" + ] + } + ], + "dest": "api", + "disableGitFeatures": false, + "disableDefaultFilter": false + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "dest": "_site", + "globalMetadata": { + "_appTitle": "Prima Ultima Online Emulator Documentation", + "_enableSearch": true, + "_enableNewTab": true, + "_disableContribution": false + }, + "fileMetadataFiles": [], + "template": [ + "default", + "templates/discordfx" + ], + "postProcessors": [], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false + } +} diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..128ed24 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,74 @@ +# Orion IRC Server Documentation + +![Version](https://img.shields.io/badge/version-0.4.0-blue) +![License](https://img.shields.io/badge/license-MIT-green) +![.NET](https://img.shields.io/badge/.NET-9.0-purple) + +> IRC is not dead, long live IRC! + +Welcome to the official documentation for Orion IRC Server, a modern, scalable IRC server built with .NET 9.0. + +## Introduction + +Orion is designed to provide robust IRC functionality while maintaining high performance and flexibility. It features a modular architecture that makes it easy to extend and customize. + +## Getting Started + +- [Quick Start Guide](./guides/quick-start.md) +- [Installation](./guides/installation.md) +- [Configuration](./guides/configuration.md) +- [Docker Deployment](./guides/docker.md) + +## Core Components + +Orion is built as a collection of modular components: + +- [**Orion.Foundations**](./components/foundations.md): Base utilities, extensions, and common functionality +- [**Orion.Core.Server**](./components/core-server.md): Server-side core functionality +- [**Orion.Core.Server.Web**](./components/core-server-web.md): Web API and HTTP interface +- [**Orion.Irc.Core**](./components/irc-core.md): IRC protocol implementation +- [**Orion.Network.Core**](./components/network-core.md): Networking abstractions +- [**Orion.Network.Tcp**](./components/network-tcp.md): TCP implementation for network transports + +## Advanced Topics + +- [Event System](./advanced/event-system.md) +- [Network Transport Architecture](./advanced/network-transport.md) +- [IRC Command Handling](./advanced/command-handling.md) +- [Scripting with JavaScript](./advanced/scripting.md) +- [Text Templating](./advanced/text-templating.md) +- [Security Features](./advanced/security.md) + +## Development + +- [Building from Source](./development/building.md) +- [Adding New Commands](./development/adding-commands.md) +- [Creating Plugins](./development/creating-plugins.md) +- [Contributing Guidelines](./development/contributing.md) +- [Coding Standards](./development/coding-standards.md) + +## API Reference + +- [HTTP API Documentation](./api/http-api.md) +- [JavaScript API](./api/javascript-api.md) + +## Troubleshooting + +- [Common Issues](./troubleshooting/common-issues.md) +- [Logs and Diagnostics](./troubleshooting/logs-diagnostics.md) +- [Performance Tuning](./troubleshooting/performance-tuning.md) + +## Community and Support + +- [Community Resources](./community/resources.md) +- [Support Channels](./community/support.md) +- [Contributing to Orion](./community/contributing.md) + +## Release Notes + +- [Version History](./releases/version-history.md) +- [Roadmap](./releases/roadmap.md) + +## License + +Orion IRC Server is licensed under the MIT License. See the [LICENSE](https://github.com/tgiachi/orion/blob/main/LICENSE) file for details. diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 0000000..abd17b8 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,4 @@ +- name: Docs + href: docs/ +- name: API + href: api/ diff --git a/src/Prima.Core.Server/Data/Config/Sections/ShardConfig.cs b/src/Prima.Core.Server/Data/Config/Sections/ShardConfig.cs index c2af882..adfe5c4 100644 --- a/src/Prima.Core.Server/Data/Config/Sections/ShardConfig.cs +++ b/src/Prima.Core.Server/Data/Config/Sections/ShardConfig.cs @@ -6,6 +6,8 @@ public class ShardConfig public string UoDirectory { get; set; } = ""; + public string? ClientVersion { get; set; } + public string Name { get; set; } = "Prima Shard"; public string AdminEmail { get; set; } = "admin@primauo.com"; diff --git a/src/Prima.Core.Server/Data/PrimaServerContext.cs b/src/Prima.Core.Server/Data/PrimaServerContext.cs index 877a1bc..c41068e 100644 --- a/src/Prima.Core.Server/Data/PrimaServerContext.cs +++ b/src/Prima.Core.Server/Data/PrimaServerContext.cs @@ -1,16 +1,18 @@ using Microsoft.Extensions.DependencyInjection; using Orion.Core.Server.Interfaces.Services.System; using Prima.Core.Server.Data.Session; +using Prima.Core.Server.Data.Uo; using Prima.Core.Server.Interfaces.Services; namespace Prima.Core.Server.Data; public static class PrimaServerContext { + public static ClientVersion ClientVersion { get; set; } + public static IServiceProvider ServiceProvider { get; set; } public static IEventLoopService EventLoopService => ServiceProvider.GetRequiredService(); - public static INetworkSessionService NetworkSessionService => ServiceProvider.GetRequiredService>(); } diff --git a/src/Prima.Core.Server/Data/Session/NetworkSession.cs b/src/Prima.Core.Server/Data/Session/NetworkSession.cs index 9063644..3f63545 100644 --- a/src/Prima.Core.Server/Data/Session/NetworkSession.cs +++ b/src/Prima.Core.Server/Data/Session/NetworkSession.cs @@ -1,4 +1,5 @@ using Orion.Core.Server.Interfaces.Sessions; +using Prima.Core.Server.Data.Uo; using Prima.Network.Interfaces.Packets; using Prima.Network.Packets; @@ -27,7 +28,7 @@ public class NetworkSession : INetworkSession public string AccountId { get; set; } - public ClientVersionRequest ClientVersionRequest { get; set; } + public ClientVersion ClientVersion { get; set; } public void Dispose() diff --git a/src/Prima.UOData/Data/ClientVersion.cs b/src/Prima.Core.Server/Data/Uo/ClientVersion.cs similarity index 99% rename from src/Prima.UOData/Data/ClientVersion.cs rename to src/Prima.Core.Server/Data/Uo/ClientVersion.cs index 58f2a62..0591d61 100644 --- a/src/Prima.UOData/Data/ClientVersion.cs +++ b/src/Prima.Core.Server/Data/Uo/ClientVersion.cs @@ -2,9 +2,9 @@ using Orion.Foundations.Buffers; using Orion.Foundations.Extensions; using Prima.Core.Server.Extensions; -using Prima.UOData.Types; +using Prima.Core.Server.Types.Uo; -namespace Prima.UOData.Data; +namespace Prima.Core.Server.Data.Uo; /************************************************************************* * ModernUO * @@ -65,6 +65,7 @@ public ClientVersion(int maj, int min, int rev, int pat, ClientType type = Clien SourceString = ToStringImpl().Intern(); } + public ClientVersion(string fmt) { fmt = fmt.ToLower(); diff --git a/src/Prima.Core.Server/Prima.Core.Server.csproj b/src/Prima.Core.Server/Prima.Core.Server.csproj index 1568240..31c5a95 100644 --- a/src/Prima.Core.Server/Prima.Core.Server.csproj +++ b/src/Prima.Core.Server/Prima.Core.Server.csproj @@ -9,12 +9,13 @@ - - + + - + + diff --git a/src/Prima.UOData/Types/ClientType.cs b/src/Prima.Core.Server/Types/Uo/ClientType.cs similarity index 75% rename from src/Prima.UOData/Types/ClientType.cs rename to src/Prima.Core.Server/Types/Uo/ClientType.cs index a4040ca..9f3af71 100644 --- a/src/Prima.UOData/Types/ClientType.cs +++ b/src/Prima.Core.Server/Types/Uo/ClientType.cs @@ -1,4 +1,4 @@ -namespace Prima.UOData.Types; +namespace Prima.Core.Server.Types.Uo; [Flags] public enum ClientType diff --git a/src/Prima.UOData/Types/ProtocolChanges.cs b/src/Prima.Core.Server/Types/Uo/ProtocolChanges.cs similarity index 97% rename from src/Prima.UOData/Types/ProtocolChanges.cs rename to src/Prima.Core.Server/Types/Uo/ProtocolChanges.cs index 71d65d5..153df5f 100644 --- a/src/Prima.UOData/Types/ProtocolChanges.cs +++ b/src/Prima.Core.Server/Types/Uo/ProtocolChanges.cs @@ -1,4 +1,4 @@ -namespace Prima.UOData.Types; +namespace Prima.Core.Server.Types.Uo; [Flags] public enum ProtocolChanges diff --git a/src/Prima.Network/Prima.Network.csproj b/src/Prima.Network/Prima.Network.csproj index c5f4069..01761e1 100644 --- a/src/Prima.Network/Prima.Network.csproj +++ b/src/Prima.Network/Prima.Network.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Prima.Server/Handlers/ConnectionHandler.cs b/src/Prima.Server/Handlers/ConnectionHandler.cs index 5dee72d..d1b586b 100644 --- a/src/Prima.Server/Handlers/ConnectionHandler.cs +++ b/src/Prima.Server/Handlers/ConnectionHandler.cs @@ -1,4 +1,6 @@ +using Prima.Core.Server.Data; using Prima.Core.Server.Data.Session; +using Prima.Core.Server.Data.Uo; using Prima.Core.Server.Handlers.Base; using Prima.Core.Server.Interfaces.Listeners; using Prima.Core.Server.Interfaces.Services; @@ -22,7 +24,24 @@ protected override void RegisterHandlers() public async Task OnPacketReceived(NetworkSession session, ClientVersionRequest packet) { session.Seed = packet.Seed; - session.ClientVersionRequest = packet; + session.ClientVersion = new ClientVersion( + packet.MajorVersion, + packet.MinorVersion, + packet.Revision, + packet.Prototype + ); + + if (PrimaServerContext.ClientVersion != session.ClientVersion) + { + Logger.LogWarning( + "Client version mismatch. Expected: {@expected}, Received: {@received}", + PrimaServerContext.ClientVersion, + session.ClientVersion + ); + await session.Disconnect(); + return; + } + session.FirstPacketReceived = true; } diff --git a/src/Prima.Server/Interfaces/Services/IAssetService.cs b/src/Prima.Server/Interfaces/Services/IAssetService.cs new file mode 100644 index 0000000..abacd71 --- /dev/null +++ b/src/Prima.Server/Interfaces/Services/IAssetService.cs @@ -0,0 +1,8 @@ +using Orion.Core.Server.Interfaces.Services.Base; + +namespace Prima.Server.Interfaces.Services; + +public interface IAssetService : IOrionService +{ + +} diff --git a/src/Prima.Server/Prima.Server.csproj b/src/Prima.Server/Prima.Server.csproj index e0af817..93e9ad1 100644 --- a/src/Prima.Server/Prima.Server.csproj +++ b/src/Prima.Server/Prima.Server.csproj @@ -41,6 +41,7 @@ + @@ -48,7 +49,7 @@ - + diff --git a/src/Prima.Server/Program.cs b/src/Prima.Server/Program.cs index 33e2026..7d96690 100644 --- a/src/Prima.Server/Program.cs +++ b/src/Prima.Server/Program.cs @@ -20,6 +20,7 @@ using Prima.Network.Modules; using Prima.Server.Handlers; using Prima.Server.Hosted; +using Prima.Server.Interfaces.Services; using Prima.Server.Modules.Container; using Prima.Server.Modules.Scripts; using Prima.Server.Routes; @@ -85,6 +86,11 @@ static async Task Main(string[] args) builder.Services.AddService(); + builder.Services + .AddService() + .AddService() + ; + builder.Services .AddScriptModule() .AddScriptModule() diff --git a/src/Prima.Server/Services/AssetService.cs b/src/Prima.Server/Services/AssetService.cs new file mode 100644 index 0000000..ed69b24 --- /dev/null +++ b/src/Prima.Server/Services/AssetService.cs @@ -0,0 +1,49 @@ +using Orion.Core.Server.Data.Directories; +using Orion.Core.Server.Events.Server; +using Orion.Core.Server.Interfaces.Services.System; +using Orion.Core.Server.Listeners.EventBus; +using Orion.Foundations.Utils; +using Prima.Server.Interfaces.Services; + +namespace Prima.Server.Services; + +public class AssetService : IAssetService, IEventBusListener +{ + private readonly ILogger _logger; + + private readonly IEventBusService _eventBusService; + + private readonly DirectoriesConfig _directoriesConfig; + + public AssetService(ILogger logger, IEventBusService eventBusService, DirectoriesConfig directoriesConfig) + { + _logger = logger; + _eventBusService = eventBusService; + _directoriesConfig = directoriesConfig; + _eventBusService.Subscribe(this); + } + + public async Task HandleAsync(ServerStartedEvent @event, CancellationToken cancellationToken) + { + var assets = ResourceUtils.GetEmbeddedResourceNames(typeof(AssetService).Assembly, "Assets"); + var files = assets.Select(s => new + { Asset = s, FileName = ResourceUtils.ConvertResourceNameToPath(s, "Prima.Server.Assets") } + ) + .ToList(); + + + foreach (var assetFile in files) + { + var fileName = Path.Combine(_directoriesConfig.Root, assetFile.FileName); + + if (!File.Exists(fileName)) + { + _logger.LogInformation("Copying asset {FileName}", fileName); + + var content = ResourceUtils.GetEmbeddedResourceContent(assetFile.Asset, typeof(AssetService).Assembly); + + await File.WriteAllTextAsync(fileName, content, cancellationToken); + } + } + } +} diff --git a/src/Prima.UOData/Extensions/ClientVersionExtensions.cs b/src/Prima.UOData/Extensions/ClientVersionExtensions.cs index 950244c..4fc7b74 100644 --- a/src/Prima.UOData/Extensions/ClientVersionExtensions.cs +++ b/src/Prima.UOData/Extensions/ClientVersionExtensions.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using Prima.Core.Server.Types.Uo; using Prima.UOData.Types; namespace Prima.UOData.Extensions; diff --git a/src/Prima.UOData/Id/Serial.cs b/src/Prima.UOData/Id/Serial.cs index 08b3c3e..89c6f77 100644 --- a/src/Prima.UOData/Id/Serial.cs +++ b/src/Prima.UOData/Id/Serial.cs @@ -16,7 +16,7 @@ using System.Runtime.CompilerServices; using Prima.Core.Server.Extensions; -namespace Prima.Core.Server.Data.Serialization; +namespace Prima.UOData.Id; public readonly struct Serial : IComparable, IComparable, @@ -182,4 +182,10 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out result = default; return false; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Serial RandomSerial() + { + return new Serial((uint)Random.Shared.Next(1, int.MaxValue)); + } } diff --git a/src/Prima.UOData/Interfaces/Services/IClientVersionService.cs b/src/Prima.UOData/Interfaces/Services/IClientVersionService.cs new file mode 100644 index 0000000..131ecdf --- /dev/null +++ b/src/Prima.UOData/Interfaces/Services/IClientVersionService.cs @@ -0,0 +1,8 @@ +using Orion.Core.Server.Interfaces.Services.Base; + +namespace Prima.UOData.Interfaces.Services; + +public class IClientVersionService : IOrionService +{ + +} diff --git a/src/Prima.UOData/Interfaces/Services/IMapService.cs b/src/Prima.UOData/Interfaces/Services/IMapService.cs new file mode 100644 index 0000000..12d90be --- /dev/null +++ b/src/Prima.UOData/Interfaces/Services/IMapService.cs @@ -0,0 +1,8 @@ +using Orion.Core.Server.Interfaces.Services.Base; + +namespace Prima.UOData.Interfaces.Services; + +public interface IMapService : IOrionService, IOrionStartService +{ + +} diff --git a/src/Prima.UOData/Prima.UOData.csproj b/src/Prima.UOData/Prima.UOData.csproj index a0a4f3c..87ea1b7 100644 --- a/src/Prima.UOData/Prima.UOData.csproj +++ b/src/Prima.UOData/Prima.UOData.csproj @@ -8,12 +8,14 @@ true + - + + - + diff --git a/src/Prima.UOData/Services/ClientVersionService.cs b/src/Prima.UOData/Services/ClientVersionService.cs new file mode 100644 index 0000000..d964029 --- /dev/null +++ b/src/Prima.UOData/Services/ClientVersionService.cs @@ -0,0 +1,87 @@ +using System.Buffers.Binary; +using Microsoft.Extensions.Logging; +using Orion.Core.Server.Events.Server; +using Orion.Core.Server.Interfaces.Services.System; +using Orion.Core.Server.Listeners.EventBus; +using Prima.Core.Server.Data; +using Prima.Core.Server.Data.Config; +using Prima.Core.Server.Data.Uo; +using Prima.UOData.Interfaces.Services; +using Prima.UOData.Mul; + +namespace Prima.UOData.Services; + +public class ClientVersionService : IClientVersionService, IEventBusListener +{ + private readonly ILogger _logger; + + private readonly IEventBusService _eventBusService; + private readonly PrimaServerConfig _primaServerConfig; + + public ClientVersionService( + ILogger logger, IEventBusService eventBusService, PrimaServerConfig primaServerConfig + ) + { + _logger = logger; + _eventBusService = eventBusService; + _primaServerConfig = primaServerConfig; + _eventBusService.Subscribe(this); + } + + public async Task HandleAsync(ServerStartedEvent @event, CancellationToken cancellationToken) + { + ClientVersion clientVersion = null; + _logger.LogInformation("Determining client version..."); + + if (!string.IsNullOrEmpty(_primaServerConfig.Shard.ClientVersion)) + { + _logger.LogInformation("Client version set to {@clientVersion}", _primaServerConfig.Shard.ClientVersion); + + clientVersion = new ClientVersion(_primaServerConfig.Shard.ClientVersion); + + if (clientVersion == null) + { + _logger.LogError("Invalid client version format: {@clientVersion}", _primaServerConfig.Shard.ClientVersion); + throw new ArgumentException("Invalid client version format"); + } + } + + var uoClassic = UoFiles.GetFilePath("client.exe"); + + if (!string.IsNullOrEmpty(uoClassic)) + { + await using FileStream fs = new FileStream(uoClassic, FileMode.Open, FileAccess.Read, FileShare.Read); + var buffer = GC.AllocateUninitializedArray((int)fs.Length, true); + _ = fs.Read(buffer); + // VS_VERSION_INFO (unicode) + Span vsVersionInfo = + [ + 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, + 0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, + 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00, + 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00 + ]; + + var versionIndex = buffer.AsSpan().IndexOf(vsVersionInfo); + if (versionIndex > -1) + { + var offset = versionIndex + 42; // 30 + 12 + + var minorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset)); + var majorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 2)); + var privatePart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 4)); + var buildPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 6)); + + clientVersion = new ClientVersion(majorPart, minorPart, buildPart, privatePart); + } + } + + if (clientVersion == null) + { + _logger.LogError("Client version not found"); + throw new InvalidOperationException("Client version not found"); + } + + PrimaServerContext.ClientVersion = clientVersion; + } +} diff --git a/src/Prima.UOData/Services/MapService.cs b/src/Prima.UOData/Services/MapService.cs new file mode 100644 index 0000000..5b96698 --- /dev/null +++ b/src/Prima.UOData/Services/MapService.cs @@ -0,0 +1,68 @@ +using Microsoft.Extensions.Logging; +using Prima.UOData.Data.Map; +using Prima.UOData.Interfaces.Services; + +namespace Prima.UOData.Services; + +public class MapService : IMapService +{ + public static readonly CityInfo[] OldHavenStartingCities = + [ + new("Haven", "The Bountiful Harvest Inn", 3677, 2625, 0, 0, 1), + new("Britain", "Sweet Dreams Inn", 1075074, 1496, 1628, 10, 1), + new("Magincia", "The Great Horns Tavern", 1075077, 3734, 2222, 20, 1), + ]; + + public static readonly CityInfo[] FeluccaStartingCities = + [ + new("Yew", "The Empath Abbey", 1075072, 633, 858, 0, 0), + new("Minoc", "The Barnacle", 1075073, 2476, 413, 15, 0), + new("Britain", "Sweet Dreams Inn", 1075074, 1496, 1628, 10, 0), + new("Moonglow", "The Scholars Inn", 1075075, 4408, 1168, 0, 0), + new("Trinsic", "The Traveler's Inn", 1075076, 1845, 2745, 0, 0), + new("Magincia", "The Great Horns Tavern", 1075077, 3734, 2222, 20, 0), + new("Jhelom", "The Mercenary Inn", 1075078, 1374, 3826, 0, 0), + new("Skara Brae", "The Falconer's Inn", 1075079, 618, 2234, 0, 0), + new("Vesper", "The Ironwood Inn", 1075080, 2771, 976, 0, 0) + ]; + + public static readonly CityInfo[] TrammelStartingCities = + [ + new("Yew", "The Empath Abbey", 1075072, 633, 858, 0, 1), + new("Minoc", "The Barnacle", 1075073, 2476, 413, 15, 1), + new("Moonglow", "The Scholars Inn", 1075075, 4408, 1168, 0, 1), + new("Trinsic", "The Traveler's Inn", 1075076, 1845, 2745, 0, 1), + new("Jhelom", "The Mercenary Inn", 1075078, 1374, 3826, 0, 1), + new("Skara Brae", "The Falconer's Inn", 1075079, 618, 2234, 0, 1), + new("Vesper", "The Ironwood Inn", 1075080, 2771, 976, 0, 1), + ]; + + public static readonly CityInfo[] NewHavenStartingCities = + [ + new("New Haven", "The Bountiful Harvest Inn", 1150168, 3503, 2574, 14, 1), + new("Britain", "The Wayfarer's Inn", 1075074, 1602, 1591, 20, 1) + // Magincia removed because it burned down. + ]; + + public static readonly CityInfo[] StartingCitiesSA = + [ + new("Royal City", "Royal City Inn", 1150169, 738, 3486, -19, 5) + ]; + + private readonly ILogger _logger; + + public MapService(ILogger logger) + { + _logger = logger; + } + + public Task StartAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return Task.CompletedTask; + } +} diff --git a/tests/Prima.Tests/Prima.Tests.csproj b/tests/Prima.Tests/Prima.Tests.csproj index 451b79b..de341c4 100644 --- a/tests/Prima.Tests/Prima.Tests.csproj +++ b/tests/Prima.Tests/Prima.Tests.csproj @@ -28,7 +28,10 @@ + + + diff --git a/tests/Prima.Tests/SerialTests.cs b/tests/Prima.Tests/SerialTests.cs new file mode 100644 index 0000000..3548114 --- /dev/null +++ b/tests/Prima.Tests/SerialTests.cs @@ -0,0 +1,17 @@ +using Prima.UOData.Id; + +namespace Prima.Tests; + +[TestFixture] +public class SerialTests +{ + + [Test] + public void TestGeneratedSerials() + { + var newSerial = Serial.RandomSerial(); + + Assert.That(newSerial.IsValid, Is.True); + } + +}