Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c92915e
CHG use keyed options to allow multiple definitions in one app
MeikelLP Nov 16, 2023
52c99b5
CHG make packet provider and related services keyed so they can be re…
MeikelLP Nov 16, 2023
7c65e28
ADD Single executable to host both auth and game in one executable
MeikelLP Nov 16, 2023
48beaef
Merge branch 'refs/heads/master' into feature/all-in-one
MeikelLP Oct 28, 2024
91f844d
feat(single): implement single exe
MeikelLP Oct 28, 2024
318eba3
refactor(build): renamed Game + Auth to Game.Server + Auth.Server
MeikelLP Oct 29, 2024
da4fc0d
feat(ci): single exe
MeikelLP Oct 29, 2024
daaf181
feat(single): Add commandline parser to allow --version and --help
MeikelLP Oct 29, 2024
8d10a1a
chore: Set author to MeikelLP to make auto generated copyright more u…
MeikelLP Oct 29, 2024
e9534be
refactor(single): Moved Game.Server + Auth.Server to Libraries/ dir
MeikelLP Oct 29, 2024
2847054
fix: launchSettings.json
MeikelLP Oct 29, 2024
e126c97
fix: project references
MeikelLP Oct 29, 2024
019b0de
feat: Use IFileProvider
MeikelLP Oct 29, 2024
79df8ba
refactor(game): Use IFileProvider
MeikelLP Oct 29, 2024
ebdac81
refactor(game): Remove additional config files from data dir
MeikelLP Oct 29, 2024
8bd8232
fix: tests
MeikelLP Oct 29, 2024
0144ea0
refactor(game): Use IFileProvider
MeikelLP Oct 29, 2024
1738fc8
refactor(game): Use IFileProvider
MeikelLP Oct 29, 2024
df944ab
refactor(game): Use ILoadable
MeikelLP Oct 29, 2024
2c44a1b
fix(game): Duplicate service registration
MeikelLP Oct 29, 2024
eed2c30
fix: tests
MeikelLP Oct 29, 2024
fa126e8
Merge branch 'master' into feature/content-root
MeikelLP Oct 29, 2024
d70d9d8
Merge branch 'master' into feature/all-in-one
MeikelLP Oct 29, 2024
ad73959
Merge branch 'feature/content-root' into feature/all-in-one
MeikelLP Oct 29, 2024
5d8a681
fix: startup crash if no data/ dir existed
MeikelLP Nov 3, 2024
249eeb2
docs: renamed API to configuration
MeikelLP Nov 3, 2024
869367b
docs: Improve configuration docs
MeikelLP Nov 3, 2024
91c2e6e
chore(docs): Add recommended truncate for blog posts
MeikelLP Nov 3, 2024
eec6c11
Merge branch 'feature/content-root' into feature/all-in-one
MeikelLP Nov 3, 2024
a8966fb
fix(tests)
MeikelLP Nov 3, 2024
b329c87
fix: InMemory cache wrong expiry
MeikelLP Nov 4, 2024
c0fc4b8
Merge branch 'refs/heads/master' into feature/all-in-one
MeikelLP Nov 5, 2024
00d1c51
fix(game): service registration
MeikelLP Nov 6, 2024
96ba89b
fix: default IP is loopback
MeikelLP Nov 6, 2024
85939c5
fix(auth): startup
MeikelLP Nov 6, 2024
09ed5a8
fix(tests)
MeikelLP Nov 6, 2024
66ca78d
fix(game): remove unnecessary file IO
MeikelLP Nov 6, 2024
73b798b
refactor: use constants for "auth" and "game" named options
MeikelLP Nov 6, 2024
0bb9b34
chore: cleanup csproj
MeikelLP Nov 6, 2024
31743d4
fix(single): bump GitVersion.MsBuild to latest to possibly fix CI issue
MeikelLP Nov 6, 2024
60ac376
fix(ci): unshallow fetch
MeikelLP Nov 6, 2024
9c535d2
Merge branch 'master' into feature/all-in-one
MeikelLP Nov 7, 2024
64bf313
fix(ci): unshallow fetch
MeikelLP Nov 7, 2024
966f760
fix(single): app launch
MeikelLP Nov 10, 2024
551ab1d
fix(ci): self-contained publish for single
MeikelLP Nov 10, 2024
70addd3
chore(ci): rename publish artifacts job
MeikelLP Nov 10, 2024
a01cd46
fix(game): ping handler casting wrongly
MeikelLP Nov 14, 2024
922d78a
chore(docs): clarify the client directory
MeikelLP Nov 16, 2024
bdab5af
fix(ci): run single builds only on master/tag
MeikelLP Nov 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 26 additions & 0 deletions .github/workflows/dotnet-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
fetch-depth: '0'

# setup .NET
- name: Setup .NET Core SDK 8
Expand All @@ -30,6 +32,30 @@ jobs:

- name: Test
run: dotnet test --no-build src/QuantumCore.sln
publish_single:
needs:
- test
runs-on: ubuntu-latest
# only deploy if on master or tag
if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master'
strategy:
matrix:
os: [ win-x64, osx-x64, linux-x64, linux-musl-x64 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: '0'
- name: Setup .NET Core SDK 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Build
run: dotnet publish -r ${{ matrix.os }} --verbosity minimal -p DebugType=None -p DebugSymbols=false -p PublishSingleFile=true --self-contained src/Executables/Single/
- name: Archive build artifact
uses: actions/upload-artifact@v4
with:
name: QuantumCore Single ${{ matrix.os }}
path: src/Executables/Single/bin/Release/net8.0/${{ matrix.os }}/publish
deploy:
needs:
- test
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/Getting Started/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ git clone https://github.com/MeikelLP/quantum-core-x.git

### 2. Generate a `mob_proto` and `item_proto`

In the client directory there should be a folder called `Eternexus` with an folder `--dump_proto--`. Just execute the `dump_proto.exe`. It should generate you 2 files:
In the (TMP4) client directory there should be a folder called `Eternexus` with an folder `--dump_proto--`. Just execute the `dump_proto.exe`. It should generate you 2 files:

* `item_proto`
* `mob_proto`
Expand Down
17 changes: 9 additions & 8 deletions src/Core.Networking/PacketReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Runtime.CompilerServices;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

Expand All @@ -14,11 +15,11 @@ public class PacketReader : IPacketReader
private readonly IHostEnvironment _env;
private readonly int _bufferSize;

public PacketReader(ILogger<PacketReader> logger, IPacketManager packetManager, IConfiguration configuration,
IHostEnvironment env)
public PacketReader([ServiceKey] string serviceKey, ILogger<PacketReader> logger, IConfiguration configuration,
IServiceProvider serviceProvider, IHostEnvironment env)
{
_logger = logger;
_packetManager = packetManager;
_packetManager = serviceProvider.GetRequiredKeyedService<IPacketManager>(serviceKey);
_env = env;
_bufferSize = configuration.GetValue<int?>("BufferSize") ?? 4096;
_logger.LogDebug("Using buffer size {BufferSize}", _bufferSize);
Expand All @@ -42,7 +43,7 @@ public async IAsyncEnumerable<object> EnumerateAsync(Stream stream,
}
catch (IOException)
{
_logger.LogDebug("Connection was most likely closed while reading a packet. This may be fine");
_logger.LogDebug("Connection was closed while reading a packet header. This may be fine");
break;
}

Expand All @@ -66,7 +67,7 @@ public async IAsyncEnumerable<object> EnumerateAsync(Stream stream,
}
catch (IOException)
{
_logger.LogDebug("Connection was most likely closed while reading a packet. This may be fine");
_logger.LogDebug("Connection was closed while reading a packet sub header. This may be fine");
break;
}

Expand Down Expand Up @@ -111,7 +112,7 @@ public async IAsyncEnumerable<object> EnumerateAsync(Stream stream,
}
catch (IOException)
{
_logger.LogDebug("Connection was most likely closed while reading a packet. This may be fine");
_logger.LogDebug("Connection was closed while reading a packet body. This may be fine");
break;
}

Expand All @@ -132,7 +133,7 @@ public async IAsyncEnumerable<object> EnumerateAsync(Stream stream,
}
catch (IOException)
{
_logger.LogDebug("Connection was most likely closed while reading a packet. This may be fine");
_logger.LogDebug("Connection was closed while reading a packet sequence. This may be fine");
break;
}
}
Expand All @@ -159,4 +160,4 @@ private async Task<byte[]> GetAsMuchDataAsPossibleAsync(Stream stream)
Array.Resize(ref bytes, totalRead);
return bytes;
}
}
}
34 changes: 19 additions & 15 deletions src/Core/Core/Networking/ServerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Microsoft.Extensions.Options;
using QuantumCore.API;
using QuantumCore.API.PluginTypes;
using QuantumCore.Core.Utils;
using QuantumCore.Networking;

namespace QuantumCore.Core.Networking
Expand All @@ -27,28 +26,30 @@ public abstract class ServerBase<T> : BackgroundService, IServerBase
private readonly PluginExecutor _pluginExecutor;
private readonly IServiceProvider _serviceProvider;
private readonly string _serverMode;
protected IServiceScope Scope { get; }

public int Port { get; }
public ushort Port { get; }
public IPAddress IpAddress { get; }

public ServerBase(IPacketManager packetManager, ILogger logger, PluginExecutor pluginExecutor,
IServiceProvider serviceProvider, string mode,
IOptions<HostingOptions> hostingOptions)
IServiceProvider serviceProvider, string mode)
{
_logger = logger;
_pluginExecutor = pluginExecutor;
_serviceProvider = serviceProvider;
_serverMode = mode;
PacketManager = packetManager;
Port = hostingOptions.Value.Port;
Scope = serviceProvider.CreateScope();
var hostingOptions = Scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<HostingOptions>>().Get(mode);
Port = hostingOptions.Port;

// Start server timer
_serverTimer.Start();

var localAddr = IPAddress.Parse(hostingOptions.Value.IpAddress ?? "0.0.0.0");
IpUtils.PublicIP = localAddr;
Listener = new TcpListener(localAddr, Port);

_logger.LogInformation("Initialize tcp server listening on {IP}:{Port}", localAddr, Port);
var desiredIpAddress = IPAddress.TryParse(hostingOptions.IpAddress, out var ipAddress)
? ipAddress
: IPAddress.Loopback;
IpAddress = desiredIpAddress;
Listener = new TcpListener(IpAddress, Port);
}

public long ServerTime => _serverTimer.ElapsedMilliseconds;
Expand All @@ -64,11 +65,13 @@ await _pluginExecutor.ExecutePlugins<IConnectionLifetimeListener>(_logger,
public override Task StartAsync(CancellationToken token)
{
base.StartAsync(token);
_logger.LogInformation("Start listening for connections...");

Listener.Start();
Listener.BeginAcceptTcpClient(OnClientAccepted, Listener);

_logger.LogInformation("Start listening for connections on {IP}:{Port} ({Mode})", IpAddress, Port,
_serverMode);

return Task.CompletedTask;
}

Expand Down Expand Up @@ -116,11 +119,11 @@ public async Task CallListener(IConnection connection, IPacketSerializable packe
}

object context;
if (_serverMode == "game")
if (_serverMode == HostingOptions.ModeGame)
{
context = GetGameContextPacket(connection, packet, details.PacketType);
}
else if (_serverMode == "auth")
else if (_serverMode == HostingOptions.ModeAuth)
{
context = GetAuthContextPacket(connection, packet, details.PacketType);
}
Expand Down Expand Up @@ -188,6 +191,7 @@ public async override Task StopAsync(CancellationToken cancellationToken)
await _stoppingToken.CancelAsync();
await base.StopAsync(cancellationToken);
_stoppingToken.Dispose();
Scope.Dispose();
}
}
}
}
48 changes: 0 additions & 48 deletions src/Core/Core/Utils/IpUtils.cs

This file was deleted.

43 changes: 37 additions & 6 deletions src/Core/Extensions/ServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,51 @@ public static class ServiceExtensions
private const string MessageTemplate = "[{Timestamp:HH:mm:ss.fff}][{Level:u3}]{Message:lj} " +
"{NewLine:1}{Exception:1}";

/// <summary>
/// Used to register a packet provider per application type.
/// The application types might have duplicate packet definitions (by header) but they still might be handled
/// differently. Thus multiple packet providers may be registered if necessary with each registered as a keyed
/// service.
/// </summary>
/// <param name="services"></param>
/// <param name="mode"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IServiceCollection AddPacketProvider<T>(this IServiceCollection services, string mode)
where T : class, IPacketLocationProvider
{
services.AddKeyedSingleton<IPacketLocationProvider, T>(mode);
services.AddKeyedSingleton<IPacketReader, PacketReader>(mode);
services.AddKeyedSingleton<IPacketManager>(mode, (provider, key) =>
{
var packetLocationProvider = provider.GetRequiredKeyedService<IPacketLocationProvider>(key);
var assemblies = packetLocationProvider.GetPacketAssemblies();
var packetTypes = assemblies.SelectMany(x => x.ExportedTypes)
.Where(x => x.IsAssignableTo(typeof(IPacketSerializable)) &&
x.GetCustomAttribute<PacketAttribute>()?.Direction.HasFlag(EDirection.Incoming) == true)
.OrderBy(x => x.FullName)
.ToArray();
var handlerTypes = assemblies.SelectMany(x => x.ExportedTypes)
.Where(x =>
x.IsAssignableTo(typeof(IPacketHandler)) &&
x is {IsClass: true, IsAbstract: false, IsInterface: false})
.OrderBy(x => x.FullName)
.ToArray();
return ActivatorUtilities.CreateInstance<PacketManager>(provider,
new object[] {(IEnumerable<Type>) packetTypes, handlerTypes});
});
return services;
}

/// <summary>
/// Services required by Auth & Game
/// </summary>
/// <param name="services"></param>
/// <param name="pluginCatalog"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection AddCoreServices(this IServiceCollection services, IPluginCatalog pluginCatalog,
IConfiguration configuration)
{
services.AddOptions<HostingOptions>()
.BindConfiguration("Hosting")
.ValidateDataAnnotations();
services.AddCustomLogging(configuration);
services.AddSingleton<IPacketManager>(provider =>
{
Expand All @@ -50,7 +82,6 @@ public static IServiceCollection AddCoreServices(this IServiceCollection service
.ToArray();
return ActivatorUtilities.CreateInstance<PacketManager>(provider, [packetTypes, handlerTypes]);
});
services.AddSingleton<IPacketReader, PacketReader>();
services.AddSingleton<PluginExecutor>();
services.AddPluginFramework()
.AddPluginCatalog(pluginCatalog)
Expand Down Expand Up @@ -112,4 +143,4 @@ private static IServiceCollection AddCustomLogging(this IServiceCollection servi
});
return services;
}
}
}
7 changes: 4 additions & 3 deletions src/Core/HostingOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ namespace QuantumCore;

public class HostingOptions
{
[Required]
public int Port { get; set; }
public const string ModeAuth = "auth";
public const string ModeGame = "game";
[Required] public ushort Port { get; set; }
public string? IpAddress { get; set; }
}
}
12 changes: 12 additions & 0 deletions src/Core/IPacketLocationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Reflection;

namespace QuantumCore;

/// <summary>
/// This will be registered as a keyed service.
/// Used to define where to load packet types and handlers from
/// </summary>
public interface IPacketLocationProvider
{
IReadOnlyCollection<Assembly> GetPacketAssemblies();
}
11 changes: 0 additions & 11 deletions src/CorePluginAPI/Game/IGame.cs

This file was deleted.

4 changes: 2 additions & 2 deletions src/CorePluginAPI/Game/World/IWorld.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace QuantumCore.API.Game.World
{
public interface IWorld
public interface IWorld : ILoadable
{
Task Load();
Task InitAsync();
void Update(double elapsedTime);
#nullable enable
IMap? GetMapAt(uint x, uint y);
Expand Down
3 changes: 1 addition & 2 deletions src/CorePluginAPI/IChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace QuantumCore.API;

public interface IChatManager
{
void Init();
void Talk(IEntity entity, string message);
Task Shout(string message);
}
}
6 changes: 0 additions & 6 deletions src/CorePluginAPI/IGameServer.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
using System.Collections.Immutable;
using QuantumCore.API.Game.World;
using QuantumCore.Networking;

namespace QuantumCore.API;

public interface IGameServer
{
IWorld World { get; }
long ServerTime { get; }
Task CallListener(IConnection connection, IPacketSerializable packet);
Task RemoveConnection(IConnection connection);
ImmutableArray<IGameConnection> Connections { get; }
}
Loading
Loading