Skip to content

Commit

Permalink
Added IsTemplate-Flag to Account and implemented multiple login
Browse files Browse the repository at this point in the history
  • Loading branch information
sven-n committed Jan 14, 2025
1 parent d803424 commit 4732a85
Show file tree
Hide file tree
Showing 15 changed files with 5,204 additions and 12 deletions.
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

0 comments on commit 4732a85

Please sign in to comment.