Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,19 +1,78 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Orleans;
using Turbo.Messages.Registry;
using Turbo.Primitives.Messages.Incoming.Users;
using Turbo.Primitives.Messages.Outgoing.Users;
using Turbo.Primitives.Orleans;
using Turbo.Primitives.Players;

namespace Turbo.PacketHandlers.Users;

public class GetExtendedProfileByNameMessageHandler
: IMessageHandler<GetExtendedProfileByNameMessage>
{
private readonly IGrainFactory _grainFactory;

public GetExtendedProfileByNameMessageHandler(IGrainFactory grainFactory)
{
_grainFactory = grainFactory;
}

public async ValueTask HandleAsync(
GetExtendedProfileByNameMessage message,
MessageContext ctx,
CancellationToken ct
)
{
await ValueTask.CompletedTask.ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(message.UserName))
return;

var directoryGrain = _grainFactory.GetPlayerDirectoryGrain();
var playerId = await directoryGrain.GetPlayerIdAsync(message.UserName, ct).ConfigureAwait(false);
if (playerId is null)
return;
var snapshot = await _grainFactory
.GetPlayerGrain(playerId.Value)
.GetExtendedProfileSnapshotAsync(ct)
.ConfigureAwait(false);

var response = new ExtendedProfileMessageComposer
{
UserId = (int)snapshot.UserId,
UserName = snapshot.UserName,
Figure = snapshot.Figure,
Motto = snapshot.Motto,
CreationDate = snapshot.CreationDate,
AchievementScore = snapshot.AchievementScore,
FriendCount = snapshot.FriendCount,
IsFriend = snapshot.IsFriend,
IsFriendRequestSent = snapshot.IsFriendRequestSent,
IsOnline = snapshot.IsOnline,
Guilds = snapshot
.Guilds.Select(x => new GuildInfo
{
GroupId = x.GroupId,
GroupName = x.GroupName,
BadgeCode = x.BadgeCode,
PrimaryColor = x.PrimaryColor,
SecondaryColor = x.SecondaryColor,
Favourite = x.Favourite,
OwnerId = x.OwnerId,
HasForum = x.HasForum
})
.ToList(),
LastAccessSinceInSeconds = snapshot.LastAccessSinceInSeconds,
OpenProfileWindow = snapshot.OpenProfileWindow,
IsHidden = snapshot.IsHidden,
AccountLevel = snapshot.AccountLevel,
IntegerField24 = snapshot.IntegerField24,
StarGemCount = snapshot.StarGemCount,
BooleanField26 = snapshot.BooleanField26,
BooleanField27 = snapshot.BooleanField27
};

await ctx.SendComposerAsync(response, ct).ConfigureAwait(false);
}
}
58 changes: 57 additions & 1 deletion Turbo.PacketHandlers/Users/GetExtendedProfileMessageHandler.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,74 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Orleans;
using Turbo.Messages.Registry;
using Turbo.Primitives.Messages.Incoming.Users;
using Turbo.Primitives.Messages.Outgoing.Users;
using Turbo.Primitives.Orleans;
using Turbo.Primitives.Players;

namespace Turbo.PacketHandlers.Users;

public class GetExtendedProfileMessageHandler : IMessageHandler<GetExtendedProfileMessage>
{
private readonly IGrainFactory _grainFactory;

public GetExtendedProfileMessageHandler(IGrainFactory grainFactory)
{
_grainFactory = grainFactory;
}

public async ValueTask HandleAsync(
GetExtendedProfileMessage message,
MessageContext ctx,
CancellationToken ct
)
{
await ValueTask.CompletedTask.ConfigureAwait(false);
var targetUserId = message.UserId;
if (targetUserId <= 0)
return;

var snapshot = await _grainFactory
.GetPlayerGrain(targetUserId)
.GetExtendedProfileSnapshotAsync(ct)
.ConfigureAwait(false);

var response = new ExtendedProfileMessageComposer
{
UserId = (int)snapshot.UserId,
UserName = snapshot.UserName,
Figure = snapshot.Figure,
Motto = snapshot.Motto,
CreationDate = snapshot.CreationDate,
AchievementScore = snapshot.AchievementScore,
FriendCount = snapshot.FriendCount,
IsFriend = snapshot.IsFriend,
IsFriendRequestSent = snapshot.IsFriendRequestSent,
IsOnline = snapshot.IsOnline,
Guilds = snapshot
.Guilds.Select(x => new GuildInfo
{
GroupId = x.GroupId,
GroupName = x.GroupName,
BadgeCode = x.BadgeCode,
PrimaryColor = x.PrimaryColor,
SecondaryColor = x.SecondaryColor,
Favourite = x.Favourite,
OwnerId = x.OwnerId,
HasForum = x.HasForum
})
.ToList(),
LastAccessSinceInSeconds = snapshot.LastAccessSinceInSeconds,
OpenProfileWindow = snapshot.OpenProfileWindow,
IsHidden = snapshot.IsHidden,
AccountLevel = snapshot.AccountLevel,
IntegerField24 = snapshot.IntegerField24,
StarGemCount = snapshot.StarGemCount,
BooleanField26 = snapshot.BooleanField26,
BooleanField27 = snapshot.BooleanField27
};

await ctx.SendComposerAsync(response, ct).ConfigureAwait(false);
}
}
48 changes: 44 additions & 4 deletions Turbo.Players/Grains/PlayerDirectoryGrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ internal class PlayerDirectoryGrain(IDbContextFactory<TurboDbContext> dbCtxFacto
private readonly IDbContextFactory<TurboDbContext> _dbCtxFactory = dbCtxFactory;

private readonly Dictionary<PlayerId, string> _idToName = [];
private readonly Dictionary<string, PlayerId> _nameToId = new(
System.StringComparer.OrdinalIgnoreCase
);

public async Task<string> GetPlayerNameAsync(PlayerId playerId, CancellationToken ct)
{
Expand All @@ -36,7 +39,7 @@ public async Task<string> GetPlayerNameAsync(PlayerId playerId, CancellationToke
if (string.IsNullOrWhiteSpace(dbName))
return string.Empty;

_idToName[playerId] = dbName;
SetCache(playerId, dbName);

return dbName;
}
Expand Down Expand Up @@ -84,7 +87,7 @@ CancellationToken ct

foreach (var player in players)
{
_idToName[player.Key] = player.Value;
SetCache(player.Key, player.Value);

names.TryAdd(player.Key, player.Value);
}
Expand All @@ -96,15 +99,52 @@ CancellationToken ct

public Task SetPlayerNameAsync(PlayerId playerId, string name, CancellationToken ct)
{
_idToName[playerId] = name;
SetCache(playerId, name);

return Task.CompletedTask;
}

public Task InvalidatePlayerNameAsync(PlayerId playerId, CancellationToken ct)
{
_idToName.Remove(playerId);
if (_idToName.Remove(playerId, out var oldName))
_nameToId.Remove(oldName);

return Task.CompletedTask;
}

public async Task<PlayerId?> GetPlayerIdAsync(string userName, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(userName))
return null;

var normalizedUserName = userName.Trim();
if (_nameToId.TryGetValue(normalizedUserName, out var cachedPlayerId))
return cachedPlayerId;

await using var dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct);

var loweredUserName = normalizedUserName.ToLowerInvariant();
var player = await dbCtx
.Players.AsNoTracking()
.Where(x => x.Name != null && x.Name.ToLower() == loweredUserName)
.Select(x => new { x.Id, x.Name })
.FirstOrDefaultAsync(ct);

if (player is null or { Id: <= 0 })
return null;

var playerId = (PlayerId)player.Id;
SetCache(playerId, player.Name ?? normalizedUserName);

return playerId;
}

private void SetCache(PlayerId playerId, string name)
{
if (_idToName.TryGetValue(playerId, out var existingName))
_nameToId.Remove(existingName);

_idToName[playerId] = name;
_nameToId[name] = playerId;
}
}
29 changes: 29 additions & 0 deletions Turbo.Players/Grains/PlayerGrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,33 @@ public Task<PlayerSummarySnapshot> GetSummaryAsync(CancellationToken ct) =>
CreatedAt = state.State.CreatedAt,
}
);

public Task<PlayerExtendedProfileSnapshot> GetExtendedProfileSnapshotAsync(CancellationToken ct)
{
var s = state.State;
return Task.FromResult(
new PlayerExtendedProfileSnapshot
{
UserId = (PlayerId)this.GetPrimaryKeyLong(),
UserName = s.Name,
Figure = s.Figure,
Motto = s.Motto,
CreationDate = s.CreatedAt.ToString("yyyy-MM-dd"),
AchievementScore = 0,
FriendCount = 0,
IsFriend = false,
IsFriendRequestSent = false,
IsOnline = true,
Guilds = new(),
LastAccessSinceInSeconds = 0,
OpenProfileWindow = true,
IsHidden = false,
AccountLevel = 1,
IntegerField24 = 0,
StarGemCount = 0,
BooleanField26 = false,
BooleanField27 = false
}
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@

namespace Turbo.Primitives.Messages.Incoming.Users;

public record GetExtendedProfileByNameMessage : IMessageEvent { }
public record GetExtendedProfileByNameMessage : IMessageEvent
{
public required string UserName { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using Turbo.Primitives.Networking;
using Turbo.Primitives.Players;

namespace Turbo.Primitives.Messages.Incoming.Users;

public record GetExtendedProfileMessage : IMessageEvent { }
public record GetExtendedProfileMessage : IMessageEvent
{
public required PlayerId UserId { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ namespace Turbo.Primitives.Messages.Outgoing.Users;
[GenerateSerializer, Immutable]
public sealed record ExtendedProfileChangedMessageComposer : IComposer
{
// TODO: add properties if/when identified
[Id(0)]
public required int UserId { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using Orleans;
using Turbo.Primitives.Networking;

Expand All @@ -6,5 +7,63 @@ namespace Turbo.Primitives.Messages.Outgoing.Users;
[GenerateSerializer, Immutable]
public sealed record ExtendedProfileMessageComposer : IComposer
{
// TODO: add properties if/when identified
[Id(0)]
public required int UserId { get; init; }
[Id(1)]
public required string UserName { get; init; }
[Id(2)]
public required string Figure { get; init; }
[Id(3)]
public required string Motto { get; init; }
[Id(4)]
public required string CreationDate { get; init; }
[Id(5)]
public required int AchievementScore { get; init; }
[Id(6)]
public required int FriendCount { get; init; }
[Id(7)]
public required bool IsFriend { get; init; }
[Id(8)]
public required bool IsFriendRequestSent { get; init; }
[Id(9)]
public required bool IsOnline { get; init; }
[Id(10)]
public required List<GuildInfo> Guilds { get; init; }
[Id(11)]
public required int LastAccessSinceInSeconds { get; init; }
[Id(12)]
public required bool OpenProfileWindow { get; init; }
[Id(13)]
public required bool IsHidden { get; init; }
[Id(14)]
public required int AccountLevel { get; init; }
[Id(15)]
public required int IntegerField24 { get; init; }
[Id(16)]
public required int StarGemCount { get; init; }
[Id(17)]
public required bool BooleanField26 { get; init; }
[Id(18)]
public required bool BooleanField27 { get; init; }
}

[GenerateSerializer, Immutable]
public sealed record GuildInfo
{
[Id(0)]
public required int GroupId { get; init; }
[Id(1)]
public required string GroupName { get; init; }
[Id(2)]
public required string BadgeCode { get; init; }
[Id(3)]
public required string PrimaryColor { get; init; }
[Id(4)]
public required string SecondaryColor { get; init; }
[Id(5)]
public required bool Favourite { get; init; }
[Id(6)]
public required int OwnerId { get; init; }
[Id(7)]
public required bool HasForum { get; init; }
}
Loading