246 lines
8.8 KiB
C#
246 lines
8.8 KiB
C#
// <copyright file="CreateGameServerConfig.razor.cs" company="MUnique">
|
|
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
|
// </copyright>
|
|
|
|
namespace MUnique.OpenMU.Web.AdminPanel.Pages;
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Threading;
|
|
using Blazored.Toast.Services;
|
|
using Microsoft.AspNetCore.Components;
|
|
using MUnique.OpenMU.DataModel.Configuration;
|
|
using MUnique.OpenMU.Interfaces;
|
|
using MUnique.OpenMU.Persistence;
|
|
using MUnique.OpenMU.Web.AdminPanel.Properties;
|
|
using MUnique.OpenMU.Web.Shared.Components.Modal;
|
|
|
|
/// <summary>
|
|
/// Razor page that shows objects of the specified type in a grid.
|
|
/// </summary>
|
|
public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
|
|
{
|
|
private Task? _loadTask;
|
|
private CancellationTokenSource? _disposeCts;
|
|
|
|
private GameServerViewModel? _viewModel;
|
|
private string? _initState;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the context provider.
|
|
/// </summary>
|
|
[Inject]
|
|
public IPersistenceContextProvider ContextProvider { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the server initializer.
|
|
/// </summary>
|
|
[Inject]
|
|
public IGameServerInstanceManager ServerInstanceManager { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the data source.
|
|
/// </summary>
|
|
[Inject]
|
|
public IDataSource<GameConfiguration> DataSource { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the modal service.
|
|
/// </summary>
|
|
[Inject]
|
|
public IModalService ModalService { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the toast service.
|
|
/// </summary>
|
|
[Inject]
|
|
public IToastService ToastService { get; set; } = null!;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the navigation manager.
|
|
/// </summary>
|
|
[Inject]
|
|
public NavigationManager NavigationManager { get; set; } = null!;
|
|
|
|
/// <inheritdoc />
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
await (this._disposeCts?.CancelAsync() ?? Task.CompletedTask).ConfigureAwait(false);
|
|
this._disposeCts?.Dispose();
|
|
this._disposeCts = null;
|
|
|
|
try
|
|
{
|
|
await (this._loadTask ?? Task.CompletedTask).ConfigureAwait(false);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// We can ignore that.
|
|
}
|
|
catch
|
|
{
|
|
// And we should not throw exceptions in the dispose method.
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override async Task OnParametersSetAsync()
|
|
{
|
|
var cts = new CancellationTokenSource();
|
|
this._disposeCts = cts;
|
|
this._loadTask = Task.Run(() => this.LoadDataAsync(cts.Token), cts.Token);
|
|
await base.OnParametersSetAsync().ConfigureAwait(true);
|
|
}
|
|
|
|
private async Task LoadDataAsync(CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
var gameConfiguration = await this.DataSource.GetOwnerAsync(default, cancellationToken).ConfigureAwait(true);
|
|
using var persistenceContext = this.ContextProvider.CreateNewContext(gameConfiguration);
|
|
|
|
var serverConfigs = await persistenceContext.GetAsync<GameServerConfiguration>(cancellationToken).ConfigureAwait(false);
|
|
var clients = await persistenceContext.GetAsync<GameClientDefinition>(cancellationToken).ConfigureAwait(false);
|
|
var existingServerDefinitions = (await persistenceContext.GetAsync<GameServerDefinition>(cancellationToken).ConfigureAwait(false)).ToList();
|
|
|
|
var nextServerId = 0;
|
|
var networkPort = 55901;
|
|
if (existingServerDefinitions.Count > 0)
|
|
{
|
|
nextServerId = existingServerDefinitions.Max(s => s.ServerID) + 1;
|
|
networkPort = existingServerDefinitions.Max(s => s.Endpoints.FirstOrDefault()?.NetworkPort ?? 55900) + 1;
|
|
}
|
|
|
|
this._viewModel = new GameServerViewModel
|
|
{
|
|
ServerConfiguration = serverConfigs.FirstOrDefault(),
|
|
ServerId = (byte)nextServerId,
|
|
ExperienceRate = 1.0f,
|
|
PvpEnabled = true,
|
|
NetworkPort = networkPort,
|
|
Client = clients.FirstOrDefault(),
|
|
};
|
|
|
|
await this.InvokeAsync(this.StateHasChanged).ConfigureAwait(false);
|
|
}
|
|
|
|
private async ValueTask<GameServerDefinition> CreateDefinitionByViewModelAsync(IContext context)
|
|
{
|
|
if (this._viewModel is null)
|
|
{
|
|
throw new InvalidOperationException("View model not initialized.");
|
|
}
|
|
|
|
var result = context.CreateNew<GameServerDefinition>();
|
|
result.ServerID = this._viewModel.ServerId;
|
|
result.Description = this._viewModel.Description;
|
|
result.PvpEnabled = this._viewModel.PvpEnabled;
|
|
result.ExperienceRate = this._viewModel.ExperienceRate;
|
|
result.GameConfiguration = await this.DataSource.GetOwnerAsync();
|
|
result.ServerConfiguration = this._viewModel.ServerConfiguration!;
|
|
|
|
var endpoint = context.CreateNew<GameServerEndpoint>();
|
|
endpoint.NetworkPort = (ushort)this._viewModel.NetworkPort;
|
|
endpoint.Client = this._viewModel.Client!;
|
|
result.Endpoints.Add(endpoint);
|
|
|
|
return result;
|
|
}
|
|
|
|
private async Task OnSaveButtonClickAsync()
|
|
{
|
|
try
|
|
{
|
|
var gameConfiguration = await this.DataSource.GetOwnerAsync().ConfigureAwait(false);
|
|
|
|
using var saveContext = this.ContextProvider.CreateNewTypedContext(typeof(DataModel.Configuration.GameServerDefinition), true, gameConfiguration);
|
|
|
|
var existingServerDefinitions = (await saveContext.GetAsync<GameServerDefinition>().ConfigureAwait(false)).ToList();
|
|
if (existingServerDefinitions.Any(def => def.ServerID == this._viewModel?.ServerId))
|
|
{
|
|
this.ToastService.ShowError(string.Format(Resources.ServerWithIdAlreadyExists, this._viewModel?.ServerId));
|
|
return;
|
|
}
|
|
|
|
if (existingServerDefinitions.Any(def => def.Endpoints.Any(endpoint => endpoint.NetworkPort == this._viewModel?.NetworkPort)))
|
|
{
|
|
this.ToastService.ShowError(string.Format(Resources.ServerWithPortAlreadyExists, this._viewModel?.NetworkPort));
|
|
return;
|
|
}
|
|
|
|
this._initState = Resources.CreatingConfigurationInfo;
|
|
await this.InvokeAsync(this.StateHasChanged);
|
|
var gameServerDefinition = await this.CreateDefinitionByViewModelAsync(saveContext).ConfigureAwait(false);
|
|
this._initState = Resources.SavingConfigurationInfo;
|
|
await this.InvokeAsync(this.StateHasChanged);
|
|
var success = await saveContext.SaveChangesAsync().ConfigureAwait(true);
|
|
|
|
// if success, init new game server instance
|
|
if (success)
|
|
{
|
|
this.ToastService.ShowSuccess(Resources.GameServerConfigurationSavedInfo);
|
|
this._initState = Resources.InitializingGameServerInfo;
|
|
await this.InvokeAsync(this.StateHasChanged);
|
|
await this.ServerInstanceManager.InitializeGameServerAsync(gameServerDefinition.ServerID);
|
|
this.NavigationManager.NavigateTo("servers");
|
|
return;
|
|
}
|
|
|
|
this.ToastService.ShowError(Resources.NoChangesSaved);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.ToastService.ShowError(string.Format(Resources.UnexpectedErrorOccurred, ex.Message));
|
|
}
|
|
|
|
this._initState = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The view model for a <see cref="GameServerDefinition"/>.
|
|
/// </summary>
|
|
public class GameServerViewModel
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the server identifier.
|
|
/// </summary>
|
|
public byte ServerId { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the description.
|
|
/// </summary>
|
|
public string Description { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the experience rate.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The experience rate.
|
|
/// </value>
|
|
[Range(0, float.MaxValue)]
|
|
public float ExperienceRate { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether PVP is enabled on this server.
|
|
/// </summary>
|
|
public bool PvpEnabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the server configuration.
|
|
/// </summary>
|
|
[Required]
|
|
public GameServerConfiguration? ServerConfiguration { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the client which is expected to connect.
|
|
/// </summary>
|
|
[Required]
|
|
public GameClientDefinition? Client { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the network port on which the server is listening.
|
|
/// </summary>
|
|
[Range(1, ushort.MaxValue)]
|
|
public int NetworkPort { get; set; }
|
|
}
|
|
}
|