Skip to content

Commit

Permalink
Improved game server management through admin panel
Browse files Browse the repository at this point in the history
  • Loading branch information
sven-n committed Sep 9, 2024
1 parent 78ec0c8 commit e2e81cc
Show file tree
Hide file tree
Showing 13 changed files with 590 additions and 136 deletions.
55 changes: 55 additions & 0 deletions src/Dapr/AdminPanel.Host/DockerGameServerInstanceManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// <copyright file="ServerRestarter.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.AdminPanel.Host;

using MUnique.OpenMU.Interfaces;

/// <summary>
/// An implementation of <see cref="IGameServerInstanceManager"/>.
/// </summary>
public class DockerGameServerInstanceManager : IGameServerInstanceManager
{
private readonly IServerProvider _serverProvider;

/// <summary>
/// Initializes a new instance of the <see cref="DockerGameServerInstanceManager"/> class.
/// </summary>
/// <param name="serverProvider">The server provider.</param>
public DockerGameServerInstanceManager(IServerProvider serverProvider)
{
this._serverProvider = serverProvider;
}

/// <inheritdoc />
public async ValueTask RestartAllAsync(bool onDatabaseInit)
{
var gameServers = this._serverProvider.Servers.Where(server => server.Type == ServerType.GameServer).ToList();
foreach (var gameServer in gameServers)
{
await gameServer.ShutdownAsync().ConfigureAwait(false);
// It's started again automatically by the docker host.
}
}

/// <inheritdoc />
public async ValueTask InitializeGameServerAsync(byte serverId)
{
// TODO: Implement this... by starting a new docker container

}

/// <inheritdoc />
public async ValueTask RemoveGameServerAsync(byte serverId)
{
var gameServer = this._serverProvider.Servers
.Where(server => server.Type == ServerType.GameServer)
.FirstOrDefault(server => server.Id == serverId);
if (gameServer is not null)
{
await gameServer.ShutdownAsync().ConfigureAwait(false);
// TODO: Remove the docker container
}
}
}
36 changes: 0 additions & 36 deletions src/Dapr/AdminPanel.Host/ServerRestarter.cs

This file was deleted.

31 changes: 31 additions & 0 deletions src/Interfaces/IGameServerInstanceManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// <copyright file="IGameServerInitializer.cs" company="MUnique">

Check warning on line 1 in src/Interfaces/IGameServerInstanceManager.cs

View workflow job for this annotation

GitHub Actions / build

File header file name documentation should match file name. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1638.md)
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Interfaces;

/// <summary>
/// Interface for an instance which manages game servers.
/// </summary>
public interface IGameServerInstanceManager
{

Check warning on line 11 in src/Interfaces/IGameServerInstanceManager.cs

View workflow job for this annotation

GitHub Actions / build


/// <summary>
/// Restarts all servers of this container.
/// </summary>
/// <param name="onDatabaseInit">If set to <c>true</c>, this method is called during a database initialization.</param>
/// <returns></returns>

Check warning on line 17 in src/Interfaces/IGameServerInstanceManager.cs

View workflow job for this annotation

GitHub Actions / build

ValueTask RestartAllAsync(bool onDatabaseInit);

/// <summary>
/// Initializes a game server.
/// </summary>
/// <param name="serverId">The server identifier.</param>
ValueTask InitializeGameServerAsync(byte serverId);

/// <summary>
/// Removes the game server instance.
/// </summary>
/// <param name="serverId">The server identifier.</param>
ValueTask RemoveGameServerAsync(byte serverId);
}
18 changes: 0 additions & 18 deletions src/Interfaces/ISupportServerRestart.cs

This file was deleted.

57 changes: 45 additions & 12 deletions src/Startup/GameServerContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace MUnique.OpenMU.Startup;
/// <summary>
/// A container which keeps all <see cref="IGameServer"/>s in one <see cref="IHostedService"/>.
/// </summary>
public sealed class GameServerContainer : ServerContainerBase, IDisposable
public sealed class GameServerContainer : ServerContainerBase, IGameServerInstanceManager, IDisposable
{
private readonly ILogger<GameServerContainer> _logger;
private readonly ILoggerFactory _loggerFactory;
Expand Down Expand Up @@ -91,6 +91,34 @@ public void Dispose()
}
}

/// <inheritdoc />
public async ValueTask InitializeGameServerAsync(byte serverId)
{
using var persistenceContext = this._persistenceContextProvider.CreateNewConfigurationContext();
var gameServerDefinitions = await persistenceContext.GetAsync<GameServerDefinition>().ConfigureAwait(false);
var gameServerDefinition = gameServerDefinitions.FirstOrDefault(def => def.ServerID == serverId)
?? throw new InvalidOperationException($"GameServerDefinition of server {serverId} was not found.");

this.InitializeGameServer(gameServerDefinition);
}

/// <inheritdoc />
public async ValueTask RemoveGameServerAsync(byte serverId)
{
using var loggerScope = this._logger.BeginScope("GameServer: {0}", serverId);
if (this._gameServers.TryGetValue(serverId, out var gameServer))
{
await gameServer.ShutdownAsync().ConfigureAwait(false);
this._gameServers.Remove(serverId);
this._servers.Remove(gameServer);
this._logger.LogInformation($"Game Server {gameServer.Id} - [{gameServer.Description}] removed");
}
else
{
this._logger.LogInformation($"Game Server {serverId} not found");
}
}

/// <inheritdoc />
protected override async ValueTask BeforeStartAsync(bool onDatabaseInit, CancellationToken cancellationToken)
{
Expand All @@ -107,19 +135,10 @@ protected override async Task StartInnerAsync(CancellationToken cancellationToke
using var persistenceContext = this._persistenceContextProvider.CreateNewConfigurationContext();
await this.LoadGameClientDefinitionsAsync(persistenceContext).ConfigureAwait(false);

var gameServerDefinitions = await persistenceContext.GetAsync<GameServerDefinition>().ConfigureAwait(false);
var gameServerDefinitions = await persistenceContext.GetAsync<GameServerDefinition>(cancellationToken).ConfigureAwait(false);
foreach (var gameServerDefinition in gameServerDefinitions)
{
using var loggerScope = this._logger.BeginScope("GameServer: {0}", gameServerDefinition.ServerID);
var gameServer = new GameServer(gameServerDefinition, this._guildServer, this._eventPublisher, this._loginServer, this._persistenceContextProvider, this._friendServer, this._loggerFactory, this._plugInManager, this._changeMediator);
foreach (var endpoint in gameServerDefinition.Endpoints)
{
gameServer.AddListener(new DefaultTcpGameServerListener(endpoint, gameServer.CreateServerInfo(), gameServer.Context, this._connectServerContainer.GetObserver(endpoint.Client!), this._ipResolver, this._loggerFactory));
}

this._servers.Add(gameServer);
this._gameServers.Add(gameServer.Id, gameServer);
this._logger.LogInformation($"Game Server {gameServer.Id} - [{gameServer.Description}] initialized");
this.InitializeGameServer(gameServerDefinition);
}
}

Expand All @@ -144,6 +163,20 @@ protected override async Task StopInnerAsync(CancellationToken cancellationToken
this._gameServers.Clear();
}

private void InitializeGameServer(GameServerDefinition gameServerDefinition)
{
using var loggerScope = this._logger.BeginScope("GameServer: {0}", gameServerDefinition.ServerID);
var gameServer = new GameServer(gameServerDefinition, this._guildServer, this._eventPublisher, this._loginServer, this._persistenceContextProvider, this._friendServer, this._loggerFactory, this._plugInManager, this._changeMediator);
foreach (var endpoint in gameServerDefinition.Endpoints)
{
gameServer.AddListener(new DefaultTcpGameServerListener(endpoint, gameServer.CreateServerInfo(), gameServer.Context, this._connectServerContainer.GetObserver(endpoint.Client!), this._ipResolver, this._loggerFactory));
}

this._servers.Add(gameServer);
this._gameServers.Add(gameServer.Id, gameServer);
this._logger.LogInformation($"Game Server {gameServer.Id} - [{gameServer.Description}] initialized");
}

private async ValueTask LoadGameClientDefinitionsAsync(IContext persistenceContext)
{
var versions = (await persistenceContext.GetAsync<GameClientDefinition>().ConfigureAwait(false)).ToList();
Expand Down
2 changes: 1 addition & 1 deletion src/Startup/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ private async Task<IHost> CreateHostAsync(string[] args)
.AddSingleton<ConnectServerFactory>()
.AddSingleton<ConnectServerContainer>()
.AddSingleton<GameServerContainer>()
.AddSingleton<ISupportServerRestart>(provider => provider.GetService<GameServerContainer>()!)
.AddSingleton<IGameServerInstanceManager>(provider => provider.GetService<GameServerContainer>()!)
.AddScoped<IMapFactory, JavascriptMapFactory>()
.AddSingleton<SetupService>()
.AddSingleton<IEnumerable<IConnectServer>>(provider => provider.GetService<ConnectServerContainer>() ?? throw new Exception($"{nameof(ConnectServerContainer)} not registered."))
Expand Down
3 changes: 1 addition & 2 deletions src/Startup/ServerContainerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ namespace MUnique.OpenMU.Startup;
using System.Threading;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MUnique.OpenMU.Interfaces;
using MUnique.OpenMU.Web.AdminPanel.Services;

/// <summary>
/// Base class for a server container, which reacts on database recreations.
/// </summary>
public abstract class ServerContainerBase : IHostedService, ISupportServerRestart
public abstract class ServerContainerBase : IHostedService
{
private readonly SetupService _setupService;
private readonly ILogger _logger;
Expand Down
32 changes: 32 additions & 0 deletions src/Web/AdminPanel/Components/ModalQuestion.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

<div>
<p><span>@Question</span></p>
</div>

<div class="float-right" role="toolbar" aria-label="buttons">
<div class="btn-group" role="group" aria-label="yes">
<button type="button" class="btn btn-primary" onclick=@(() => this.SetResponseAsync(true))>Yes</button>
</div>
<div class="btn-group" role="group" aria-label="no">
<button type="button" class="btn btn-secondary" onclick=@(() => this.SetResponseAsync(false))>No</button>
</div>
</div>




@code {

[CascadingParameter]
BlazoredModalInstance BlazoredModal { get; set; } = null!;

/// <summary>
/// Gets or sets the text.
/// </summary>
[Parameter]
public string Question { get; set; } = string.Empty;

private async Task SetResponseAsync(bool yes) {
await this.BlazoredModal.CloseAsync(ModalResult.Ok(yes));
}
}
Loading

0 comments on commit e2e81cc

Please sign in to comment.