Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template Accounts #579

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/DataModel/Entities/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ public class Account
/// </summary>
public bool IsVaultExtended { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this instance is a template account
/// and therefore read-only within the game server.
/// </summary>
[Display(Name = "Is template account", Description = "A template account can login multiple times for test purposes and is read-only.")]
public bool IsTemplate { get; set; }

/// <summary>
/// Gets or sets the characters.
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions src/GameLogic/ITrader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace MUnique.OpenMU.GameLogic;

using MUnique.OpenMU.Interfaces;
using MUnique.OpenMU.Persistence;
using System.Threading;

/// <summary>
/// Interface of a trader.
Expand Down Expand Up @@ -71,4 +72,17 @@ public interface ITrader : IWorldObserver
/// Gets the game context of the trader.
/// </summary>
IGameContext GameContext { get; }

/// <summary>
/// Gets a value indicating whether this instance is template player.
/// In this case, trading is not allowed.
/// </summary>
bool IsTemplatePlayer { get; }

/// <summary>
/// Saves the progress of the trader.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Success of the save operation.</returns>
ValueTask<bool> SaveProgressAsync(CancellationToken cancellationToken = default);
}
20 changes: 19 additions & 1 deletion src/GameLogic/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ public Player(IGameContext gameContext)
/// </summary>
public bool IsInvisible => this.Attributes?[Stats.IsInvisible] > 0;

/// <inheritdoc />
public bool IsTemplatePlayer => this.Account?.IsTemplate is true;

/// <summary>
/// Gets the skill hit validator.
/// </summary>
Expand Down Expand Up @@ -1651,14 +1654,29 @@ public async ValueTask RemoveFromGameAsync()

try
{
await this.PersistenceContext.SaveChangesAsync().ConfigureAwait(false);
await this.SaveProgressAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
this.Logger.LogError(ex, "Couldn't save when leaving the game. Player: {player}", this);
}
}

/// <summary>
/// Saves the progress of the player.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Success of the save operation.</returns>
public async ValueTask<bool> SaveProgressAsync(CancellationToken cancellationToken = default)
{
if (!this.IsTemplatePlayer)
{
return await this.PersistenceContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}

return true;
}

/// <inheritdoc />
protected override async ValueTask DisposeAsyncCore()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public async ValueTask CreateCharacterAsync(Player player, string characterName,
player.GameContext.PlugInManager.GetPlugInPoint<ICharacterCreatedPlugIn>()?.CharacterCreated(player, character);
try
{
await player.PersistenceContext.SaveChangesAsync().ConfigureAwait(false);
await player.SaveProgressAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Expand Down
2 changes: 1 addition & 1 deletion src/GameLogic/PlayerActions/CloseNpcDialogAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public async ValueTask CloseNpcDialogAsync(Player player)
{
await Task.Delay(1000).ConfigureAwait(false);
player.Logger.LogInformation("Saving changes after closing the chaos goblin ...");
await player.PersistenceContext.SaveChangesAsync().ConfigureAwait(false);
await player.SaveProgressAsync().ConfigureAwait(false);
player.Logger.LogInformation("Saved changes after closing the chaos goblin ...");
}
catch (Exception ex)
Expand Down
9 changes: 8 additions & 1 deletion src/GameLogic/PlayerActions/Items/DropItemAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public async ValueTask DropItemAsync(Player player, byte slot, Point target)
return;
}

if (player.IsTemplatePlayer)
{
player.Logger.LogWarning("Can't drop items of a template account.");
await player.InvokeViewPlugInAsync<IItemDropResultPlugIn>(p => p.ItemDropResultAsync(slot, false)).ConfigureAwait(false);
return;
}

if (player.GameContext.PlugInManager.GetPlugInPoint<IItemDropPlugIn>() is { } plugInPoint)
{
var dropArguments = new ItemDropArguments();
Expand Down Expand Up @@ -67,7 +74,7 @@ private async ValueTask DropItemAsync(Player player, Item item, Point target)

// We have to save here already. Otherwise, if the item got modified since last
// save point by the dropper, changes would not be saved by the picking up player!
await player.PersistenceContext.SaveChangesAsync().ConfigureAwait(false);
await player.SaveProgressAsync().ConfigureAwait(false);

// Some room for improvement: When the item is not persisted, we don't need to save.
// However, to check this in the right order, we need to extend IContext to
Expand Down
18 changes: 16 additions & 2 deletions src/GameLogic/PlayerActions/LoginAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

namespace MUnique.OpenMU.GameLogic.PlayerActions;

using System.Threading;
using MUnique.OpenMU.GameLogic.Views.Login;

/// <summary>
/// Action to log in a player to the game.
/// </summary>
public class LoginAction
{
private static int _templateCounter = 0;

/// <summary>
/// Logins the specified player.
/// </summary>
Expand Down Expand Up @@ -50,11 +53,22 @@ public async ValueTask LoginAsync(Player player, string username, string passwor
try
{
await using var context = await player.PlayerState.TryBeginAdvanceToAsync(PlayerState.Authenticated).ConfigureAwait(false);
if (context.Allowed && player.GameContext is IGameServerContext gameServerContext &&
await gameServerContext.LoginServer.TryLoginAsync(username, gameServerContext.Id).ConfigureAwait(false))
if (context.Allowed
&& player.GameContext is IGameServerContext gameServerContext
&& (account.IsTemplate || await gameServerContext.LoginServer.TryLoginAsync(username, gameServerContext.Id).ConfigureAwait(false)))
{
player.Account = account;
player.Logger.LogDebug("Login successful, username: [{0}]", username);

if (player.IsTemplatePlayer)
{
foreach (var character in account.Characters)
{
var counter = Interlocked.Increment(ref _templateCounter);
character.Name = $"_{counter}";
}
}

await player.InvokeViewPlugInAsync<IShowLoginResultPlugIn>(p => p.ShowLoginResultAsync(LoginResult.Ok)).ConfigureAwait(false);
}
else
Expand Down
6 changes: 6 additions & 0 deletions src/GameLogic/PlayerActions/PlayerStore/BuyRequestAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public class BuyRequestAction
public async ValueTask BuyItemAsync(Player player, Player requestedPlayer, byte slot)
{
using var loggerScope = player.Logger.BeginScope(this.GetType());
if (requestedPlayer.IsTemplatePlayer || player.IsTemplatePlayer)
{
await player.InvokeViewPlugInAsync<IPlayerShopBuyRequestResultPlugIn>(p => p.ShowResultAsync(requestedPlayer, ItemBuyResult.ItemBlock, null)).ConfigureAwait(false);
return;
}

if (!(requestedPlayer.ShopStorage?.StoreOpen ?? false))
{
player.Logger.LogDebug("Store not open, Character {0}", requestedPlayer.SelectedCharacter?.Name);
Expand Down
2 changes: 1 addition & 1 deletion src/GameLogic/PlayerActions/Trade/TradeAcceptAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public async ValueTask HandleTradeAcceptAsync(ITrader tradeAccepter, bool accept
internal async ValueTask OpenTradeAsync(ITrader trader)
{
// first make sure that all items which could be transferred are already present in the database
await trader.PersistenceContext.SaveChangesAsync().ConfigureAwait(false);
await trader.SaveProgressAsync().ConfigureAwait(false);

trader.BackupInventory = new BackupItemStorage(trader.Inventory!.ItemStorage);
trader.TradingMoney = 0;
Expand Down
6 changes: 6 additions & 0 deletions src/GameLogic/PlayerActions/Trade/TradeRequestAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ public class TradeRequestAction
/// <returns>The success of sending the request to the <paramref name="partner"/>.</returns>
public async ValueTask<bool> RequestTradeAsync(ITrader player, ITrader partner)
{
if (player.IsTemplatePlayer || partner.IsTemplatePlayer)
{
player.Logger.LogWarning("This or the requested player is template player, cannot trade.");
return false;
}

if (player.ViewPlugIns.GetPlugIn<IShowTradeRequestPlugIn>() is null || partner.ViewPlugIns.GetPlugIn<IShowTradeRequestAnswerPlugIn>() is null)
{
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/GameLogic/PlugIns/PeriodicSaveProgressPlugIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public async ValueTask ExecuteTaskAsync(GameContext gameContext)
{
if (player.PlayerState.CurrentState == PlayerState.EnteredWorld)
{
await player.PersistenceContext.SaveChangesAsync().ConfigureAwait(false);
await player.SaveProgressAsync().ConfigureAwait(false);
}
else
{
Expand Down
12 changes: 8 additions & 4 deletions src/GameServer/GameServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,12 @@ private async ValueTask OnPlayerConnectedAsync(PlayerConnectedEventArgs e)

private async ValueTask OnPlayerDisconnectedAsync(Player remotePlayer)
{
await this.SaveSessionOfPlayerAsync(remotePlayer).ConfigureAwait(false);
await this.SetOfflineAtLoginServerAsync(remotePlayer).ConfigureAwait(false);
if (!remotePlayer.IsTemplatePlayer)
{
await this.SaveSessionOfPlayerAsync(remotePlayer).ConfigureAwait(false);
await this.SetOfflineAtLoginServerAsync(remotePlayer).ConfigureAwait(false);
}

await remotePlayer.DisposeAsync().ConfigureAwait(false);
this.OnPropertyChanged(nameof(this.CurrentConnections));
}
Expand All @@ -454,7 +458,7 @@ private async ValueTask SaveSessionOfPlayerAsync(Player player)
{
try
{
// Recover items placed in an NPC or trade dialog when player is diconnected
// Recover items placed in an NPC or trade dialog when player is disconnected
if (player.BackupInventory is not null)
{
player.Inventory!.Clear();
Expand All @@ -479,7 +483,7 @@ private async ValueTask SaveSessionOfPlayerAsync(Player player)
// nothing else to restore.
}

if (!await player.PersistenceContext.SaveChangesAsync().ConfigureAwait(false))
if (!await player.SaveProgressAsync().ConfigureAwait(false))
{
this._logger.LogWarning($"Could not save session of player {player}");
}
Expand Down
Loading
Loading