Skip to content

Commit

Permalink
Fix player location on login (#624)
Browse files Browse the repository at this point in the history
* Fix player location on login

* Fix unit test
  • Loading branch information
caioavidal authored Dec 23, 2024
1 parent 063e5ae commit 6bd6452
Show file tree
Hide file tree
Showing 21 changed files with 144 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using NeoServer.Data.Entities;
using NeoServer.Game.Common;
using NeoServer.Game.Common.Contracts.World.Tiles;
using NeoServer.Game.Common.Location.Structs;
using NeoServer.Game.Common.Results;
using NeoServer.Game.Creatures;
using NeoServer.Game.World;
using NeoServer.Loaders.Guilds;
using NeoServer.Loaders.Interfaces;
using NeoServer.Server.Common.Contracts;
using NeoServer.Server.Common.Contracts.Commands;
using NeoServer.Server.Common.Contracts.Network;
using NeoServer.Server.Services;
using Serilog;

namespace NeoServer.Server.Commands.Player;
Expand All @@ -15,29 +22,40 @@ public class PlayerLogInCommand : ICommand
private readonly ILogger _logger;
private readonly IGameServer game;
private readonly GuildLoader guildLoader;
private readonly PlayerLocationResolver _playerLocationResolver;
private readonly IEnumerable<IPlayerLoader> playerLoaders;

public PlayerLogInCommand(IGameServer game, IEnumerable<IPlayerLoader> playerLoaders, GuildLoader guildLoader,
PlayerLocationResolver playerLocationResolver,
ILogger logger)
{
this.game = game;
this.playerLoaders = playerLoaders;
this.guildLoader = guildLoader;
_playerLocationResolver = playerLocationResolver;
_logger = logger;
}

public void Execute(PlayerEntity playerRecord, IConnection connection)
public Result Execute(PlayerEntity playerRecord, IConnection connection)
{
if (playerRecord is null)
//todo validations here
return;
return Result.Fail(InvalidOperation.PlayerNotFound);

if (!game.CreatureManager.TryGetLoggedPlayer((uint)playerRecord.Id, out var player))
{
if (playerLoaders.FirstOrDefault(x => x.IsApplicable(playerRecord)) is not { } playerLoader)
return;
return Result.Fail(InvalidOperation.InvalidPlayer);

guildLoader.Load(playerRecord.GuildMember?.Guild);

var playerLocation = _playerLocationResolver.GetPlayerLocation(playerRecord);
if (playerLocation == Location.Zero) return Result.Fail(InvalidOperation.PlayerLocationInvalid);

playerRecord.PosX = playerLocation.X;
playerRecord.PosY = playerLocation.Y;
playerRecord.PosZ = playerLocation.Z;

player = playerLoader.Load(playerRecord);
}

Expand All @@ -46,5 +64,7 @@ public void Execute(PlayerEntity playerRecord, IConnection connection)
player.Login();
player.Vip.LoadVipList(playerRecord.Account.VipList.Select(x => ((uint)x.PlayerId, x.Player?.Name)));
_logger.Information("Player {PlayerName} logged in", player.Name);

return Result.Success;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using NeoServer.Data.Entities;
using NeoServer.Game.Common.Contracts.World;
using NeoServer.Game.Common.Contracts.World.Tiles;
using NeoServer.Game.Common.Location.Structs;
using NeoServer.Game.Creatures;
using NeoServer.Game.World;
using Serilog;

namespace NeoServer.Server.Services;

//todo: this project is not the best place for this
public class PlayerLocationResolver(World world, ILogger logger)
{
public Location GetPlayerLocation(PlayerEntity playerEntity)
{
var location = new Location((ushort)playerEntity.PosX, (ushort)playerEntity.PosY, (byte)playerEntity.PosZ);

var playerTile = world.TryGetTile(ref location, out var tile) &&
PlayerEnterTileRule.Rule.CanEnter(tile, location)
? tile
: null;

if (playerTile is not null) return location;

foreach (var neighbour in location.Neighbours)
{
world.TryGetTile(ref location, out var neighbourTile);
if (neighbourTile is IDynamicTile && PlayerEnterTileRule.Rule.CanEnter(neighbourTile, neighbour))
{
return location;
}
}

var town = GetTown(playerEntity);
if (town is null) return Location.Zero;

var townLocation = town.Coordinate.Location;

playerTile = world.TryGetTile(ref townLocation, out var townTile) && townTile is IDynamicTile townDynamicTile &&
PlayerEnterTileRule.Rule.CanEnter(townDynamicTile, townLocation)
? townDynamicTile
: null;

return playerTile?.Location ?? Location.Zero;
}

protected ITown GetTown(PlayerEntity playerEntity)
{
if (!world.TryGetTown((ushort)playerEntity.TownId, out var town))
logger.Error("player town not found: {PlayerModelTownId}", playerEntity.TownId);
return town;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public async Task<PlayerEntity> GetPlayer(string accountName, string password, s
.ThenInclude(x => x.VipList)
.ThenInclude(x => x.Player)
.Include(x => x.GuildMember)
.ThenInclude(x => x.Guild).SingleOrDefaultAsync();
.ThenInclude(x => x.Guild).AsNoTracking().SingleOrDefaultAsync();
}

public async Task<PlayerEntity> GetOnlinePlayer(string accountName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ private static void AddEnterRule(Location location, LuaFunction rule)
var tile = map.GetTile(location);
if (tile is not IDynamicTile dynamicTile) return;

dynamicTile.CanEnter = creature => (bool)(rule.Call(creature).FirstOrDefault() ?? false);
dynamicTile.CanEnterFunction = creature => (bool)(rule.Call(creature).FirstOrDefault() ?? false);
}

private static bool RemoveTopItem(Location location)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public interface IDynamicTile : ITile, IHasItem
bool HasBlockPathFinding { get; }
bool HasHole { get; }
List<IPlayer> Players { get; }
Func<ICreature, bool> CanEnter { get; set; }
Func<ICreature, bool> CanEnterFunction { get; set; }
IItem[] AllItems { get; }
bool HasTeleport(out ITeleport teleport);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@ public bool IsNextTo(ITile dest)
bool TryGetStackPositionOfThing(IPlayer player, IThing thing, out byte stackPosition);
byte GetCreatureStackPositionIndex(IPlayer observer);
bool HasFlag(TileFlags flag);
public bool CanEnter(ICreature creature)
{
if (creature is not IWalkableCreature walkableCreature) return false;
if (!walkableCreature.TileEnterRule.CanEnter(this, creature)) return false;
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ public interface ITileEnterRule
{
bool ShouldIgnore(ITile tile, ICreature creature);
bool CanEnter(ITile tile, ICreature creature);
bool CanEnter(ITile tile, Location.Structs.Location location);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ public enum InvalidOperation
CannotUseWeapon,
AggressorIsNotHostile,
CannotMove,
AttackTargetIsInvisible
AttackTargetIsInvisible,
InvalidPlayer,
PlayerLocationInvalid
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using NeoServer.Game.Common.Creatures;
using NeoServer.Game.Common.Helpers;
using NeoServer.Game.Common.Location;
using NeoServer.Game.Common.Location.Structs;

namespace NeoServer.Game.Creatures;

Expand All @@ -28,6 +29,15 @@ public virtual bool CanEnter(ITile tile, ICreature creature)
{
if (tile is not IDynamicTile dynamicTile) return false;

return ConditionEvaluation.And(
!dynamicTile.HasCreature,
!dynamicTile.HasFlag(TileFlags.Unpassable),
dynamicTile.Ground is not null);
}
public virtual bool CanEnter(ITile tile, Location location)
{
if (tile is not IDynamicTile dynamicTile) return false;

return ConditionEvaluation.And(
!dynamicTile.HasCreature,
!dynamicTile.HasFlag(TileFlags.Unpassable),
Expand Down Expand Up @@ -63,6 +73,19 @@ public override bool CanEnter(ITile tile, ICreature creature)
!dynamicTile.HasFlag(TileFlags.Unpassable),
dynamicTile.Ground is not null);
}
public override bool CanEnter(ITile tile, Location location)
{
if (tile is not IDynamicTile dynamicTile) return false;

var goingToDifferentFloor = !location.SameFloorAs(tile.Location);
var hasMonsterOrNpc = !goingToDifferentFloor &&
(dynamicTile.HasCreatureOfType<IMonster>() || dynamicTile.HasCreatureOfType<INpc>());

return ConditionEvaluation.And(
!hasMonsterOrNpc,
!dynamicTile.HasFlag(TileFlags.Unpassable),
dynamicTile.Ground is not null);
}
}

public class MonsterEnterTileRule : CreatureEnterTileRule<MonsterEnterTileRule>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public void Execute(IWalkableCreature creature, Location location)

if (destination == Location.Zero) return;
if (tile is null || !creature.TileEnterRule.CanEnter(tile, creature)) return;
map.TryMoveCreature(creature, location);

map.TryMoveCreature(creature, tile.Location);
}

private IDynamicTile FindNeighbourTile(IWalkableCreature creature, Location location)
Expand Down
4 changes: 3 additions & 1 deletion src/GameWorldSimulator/NeoServer.Game.World/Map/Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ public void PlaceCreature(ICreature creature)
{
if (this[creature.Location] is not IDynamicTile tile) return;

if(!tile.CanEnter(creature)) return;

if (tile.HasCreature)
foreach (var location in tile.Location.Neighbours)
if (this[location] is IDynamicTile { HasCreature: false } t
Expand Down Expand Up @@ -459,7 +461,7 @@ public void MoveCreature(IWalkableCreature creature)
nextTile = newDestinationTile;
}

if (nextTile is IDynamicTile dynamicTile && !(dynamicTile.CanEnter?.Invoke(creature) ?? true))
if (nextTile is IDynamicTile dynamicTile && !(dynamicTile.CanEnterFunction?.Invoke(creature) ?? true))
{
creature.CancelWalk();
OperationFailService.Send(creature.CreatureId, TextConstants.NOT_POSSIBLE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public bool HasFlag(TileFlags flag)
{
return ((uint)flag & Flags) != 0;
}

public bool BlockMissile => HasFlag(TileFlags.BlockMissile);

public Location Location { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ public void ReplaceGround(IGround ground)
AddItem(ground);
}

public Func<ICreature, bool> CanEnter { get; set; }
public Func<ICreature, bool> CanEnterFunction { get; set; }

public bool CanRemoveItem(IItem thing)
{
Expand Down Expand Up @@ -597,7 +597,7 @@ private void SetGround(IGround ground)

TileOperationEvent.OnChanged(this, ground, operations);
}

public Result<OperationResultList<ICreature>> AddCreature(ICreature creature)
{
if (creature is not IWalkableCreature walkableCreature)
Expand All @@ -606,7 +606,7 @@ public Result<OperationResultList<ICreature>> AddCreature(ICreature creature)
if (!walkableCreature.TileEnterRule.CanEnter(this, creature))
return Result<OperationResultList<ICreature>>.NotPossible;

if (!CanEnter?.Invoke(creature) ?? false) return Result<OperationResultList<ICreature>>.NotPossible;
if (!CanEnterFunction?.Invoke(creature) ?? false) return Result<OperationResultList<ICreature>>.NotPossible;

Creatures ??= new List<IWalkableCreature>();
Creatures.Add(walkableCreature);
Expand Down
1 change: 1 addition & 0 deletions src/Loaders/NeoServer.Loaders/Interfaces/IPlayerLoader.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using NeoServer.Data.Entities;
using NeoServer.Game.Common.Contracts.Creatures;
using NeoServer.Game.Common.Results;

namespace NeoServer.Loaders.Interfaces;

Expand Down
32 changes: 10 additions & 22 deletions src/Loaders/NeoServer.Loaders/Players/PlayerLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
using NeoServer.Game.Common.Helpers;
using NeoServer.Game.Common.Item;
using NeoServer.Game.Common.Location.Structs;
using NeoServer.Game.Common.Results;
using NeoServer.Game.Creatures.Player;
using NeoServer.Game.Creatures.Player.Inventory;
using NeoServer.Loaders.Interfaces;
using NeoServer.Server.Services;
using Serilog;

namespace NeoServer.Loaders.Players;
Expand Down Expand Up @@ -72,7 +74,9 @@ public virtual IPlayer Load(PlayerEntity playerEntity)

var playerLocation =
new Location((ushort)playerEntity.PosX, (ushort)playerEntity.PosY, (byte)playerEntity.PosZ);


var currentTile = GetCurrentTile(playerLocation);

var player = new Player(
(uint)playerEntity.Id,
playerEntity.Name,
Expand Down Expand Up @@ -110,7 +114,8 @@ public virtual IPlayer Load(PlayerEntity playerEntity)
GuildLevel = (ushort)(playerEntity.GuildMember?.RankId ?? 0)
};

SetCurrentTile(player);
player.SetCurrentTile(currentTile);

AddRegenerationCondition(playerEntity, player);

player.AddInventory(ConvertToInventory(player, playerEntity));
Expand All @@ -134,27 +139,10 @@ protected IVocation GetVocation(PlayerEntity playerEntity)
return vocation;
}

protected void SetCurrentTile(IPlayer player)
protected IDynamicTile GetCurrentTile(Location location)
{
var location = player.Location;

var playerTile = World.TryGetTile(ref location, out var tile) && tile is IDynamicTile dynamicTile
? dynamicTile
: null;

if (playerTile is not null)
{
player.SetCurrentTile(playerTile);
return;
}

var townLocation = player.Town.Coordinate.Location;

playerTile = World.TryGetTile(ref townLocation, out var townTile) && townTile is IDynamicTile townDynamicTile
? townDynamicTile
: null;

player.SetCurrentTile(playerTile);
World.TryGetTile(ref location, out var dynamicTile);
return dynamicTile as IDynamicTile;
}

private static void AddRegenerationCondition(PlayerEntity playerEntity, IPlayer player)
Expand Down
2 changes: 1 addition & 1 deletion src/Loaders/NeoServer.Loaders/TileRule/TileRuleLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private int LoadTileRules()

if (_map[tileLocation] is not IDynamicTile dynamicTile) continue;

dynamicTile.CanEnter = creature => CanEnter(creature, tileRule);
dynamicTile.CanEnterFunction = creature => CanEnter(creature, tileRule);
}

return tilesData.Count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public bool IsSupported(uint version)
{
if (version == _serverConfiguration.Version) return true;

_logger.Warning($"Client protocol version {version} is not supported");
_logger.Warning("Client protocol version {Version} is not supported", version);
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@ public AccountLoginHandler(IAccountRepository repositoryNeo, ServerConfiguration
public override async void HandleMessage(IReadOnlyNetworkMessage message, IConnection connection)
{
var account = new AccountLoginPacket(message);

connection.SetXtea(account.Xtea);

if (!_clientProtocolVersion.IsSupported(account.ProtocolVersion))
{
connection.Close();
connection.Disconnect($"Client protocol version {account.ProtocolVersion} is not supported.");
return;
}

connection.SetXtea(account.Xtea);

if (account == null)
{
//todo: use option
Expand Down
Loading

0 comments on commit 6bd6452

Please sign in to comment.