diff --git a/Obsidian.API/_Enums/ProtocolVersion.cs b/Obsidian.API/_Enums/ProtocolVersion.cs
index c72dabc93..ed02a7b32 100644
--- a/Obsidian.API/_Enums/ProtocolVersion.cs
+++ b/Obsidian.API/_Enums/ProtocolVersion.cs
@@ -52,6 +52,7 @@ public enum ProtocolVersion
[Description("1.20.2")]
v1_20_2 = 764,
- [Description("1.20.3")]
- v1_20_3 = 765
+ //1.20.3 same pvn
+ [Description("1.20.4")]
+ v1_20_4 = 765
}
diff --git a/Obsidian/Client.cs b/Obsidian/Client.cs
index 483eb5db0..511b9d80d 100644
--- a/Obsidian/Client.cs
+++ b/Obsidian/Client.cs
@@ -95,7 +95,7 @@ public sealed class Client : IDisposable
///
/// The mojang user that the client and player is associated with.
///
- private CachedUser? cachedUser;
+ private CachedProfile? cachedUser;
///
/// Which packets are in queue to be sent to the client.
@@ -168,7 +168,7 @@ public sealed class Client : IDisposable
///
public string? Brand { get; set; }
- public Client(ConnectionContext connectionContext, int playerId,
+ public Client(ConnectionContext connectionContext, int playerId,
ILoggerFactory loggerFactory, IUserCache playerCache,
Server server)
{
@@ -416,25 +416,25 @@ private async Task HandleLoginStartAsync(byte[] data)
var username = this.server.Configuration.MulitplayerDebugMode ? $"Player{Globals.Random.Next(1, 999)}" : loginStart.Username;
var world = (World)this.server.DefaultWorld;
- Logger.LogDebug("Received login request from user {Username}", loginStart.Username);
+ Logger.LogDebug("Received login request from user {Username}", username);
await this.server.DisconnectIfConnectedAsync(username);
if (this.server.Configuration.OnlineMode)
{
- cachedUser = await this.userCache.GetCachedUserFromNameAsync(loginStart.Username ?? throw new NullReferenceException(nameof(loginStart.PlayerUuid)));
+ cachedUser = await this.userCache.GetCachedUserFromNameAsync(loginStart.Username ?? throw new NullReferenceException(nameof(loginStart.Username)));
if (cachedUser is null)
{
await DisconnectAsync("Account not found in the Mojang database");
return;
}
- else if (this.server.Configuration.WhitelistEnabled && !this.server.Configuration.Whitelisted.Any(x => x.Id == cachedUser.Id))
+ else if (this.server.Configuration.WhitelistEnabled && !this.server.Configuration.Whitelisted.Any(x => x.Id == cachedUser.Uuid))
{
await DisconnectAsync("You are not whitelisted on this server\nContact server administrator");
return;
}
- Player = new Player(this.cachedUser.Id, loginStart.Username, this, world);
+ Player = new Player(this.cachedUser.Uuid, loginStart.Username, this, world);
packetCryptography.GenerateKeyPair();
var (publicKey, randomToken) = packetCryptography.GeneratePublicKeyAndToken();
@@ -487,7 +487,7 @@ private async Task HandleEncryptionResponseAsync(byte[] data)
}
var serverId = sharedKey.Concat(packetCryptography.PublicKey).MinecraftShaDigest();
- if (await this.userCache.HasJoinedAsync(Player.Username, serverId) is not MojangUser user)
+ if (await this.userCache.HasJoinedAsync(Player.Username, serverId) is not MojangProfile user)
{
Logger.LogWarning("Failed to auth {Username}", Player.Username);
await DisconnectAsync("Unable to authenticate...");
@@ -556,6 +556,17 @@ await QueuePacketAsync(new UpdateRecipeBookPacket
await SendPlayerListDecoration();
await SendPlayerInfoAsync();
await this.QueuePacketAsync(new GameEventPacket(ChangeGameStateReason.StartWaitingForLevelChunks));
+
+ Player.TeleportId = Globals.Random.Next(0, 999);
+ await QueuePacketAsync(new SynchronizePlayerPositionPacket
+ {
+ Position = Player.Position,
+ Yaw = 0,
+ Pitch = 0,
+ Flags = PositionFlags.None,
+ TeleportId = Player.TeleportId
+ });
+
await Player.UpdateChunksAsync(distance: 7);
await SendInfoAsync();
await this.server.Events.PlayerJoin.InvokeAsync(new PlayerJoinEventArgs(Player, this.server, DateTimeOffset.Now));
@@ -566,17 +577,8 @@ internal async Task SendInfoAsync()
{
if (Player is null)
throw new UnreachableException("Player is null, which means the client has not yet logged in.");
-
- Player.TeleportId = Globals.Random.Next(0, 999);
+
await QueuePacketAsync(new SetDefaultSpawnPositionPacket(Player.world.LevelData.SpawnPosition));
- await QueuePacketAsync(new SynchronizePlayerPositionPacket
- {
- Position = Player.Position,
- Yaw = 0,
- Pitch = 0,
- Flags = PositionFlags.None,
- TeleportId = Player.TeleportId
- });
await SendTimeUpdateAsync();
await SendWeatherUpdateAsync();
diff --git a/Obsidian/Server.cs b/Obsidian/Server.cs
index 7ff9f2008..271cc7801 100644
--- a/Obsidian/Server.cs
+++ b/Obsidian/Server.cs
@@ -50,7 +50,7 @@ public static string VERSION
}
}
#endif
- public const ProtocolVersion DefaultProtocol = ProtocolVersion.v1_20_3;
+ public const ProtocolVersion DefaultProtocol = ProtocolVersion.v1_20_4;
public const string PersistentDataPath = "persistentdata";
public const string PermissionPath = "permissions";
diff --git a/Obsidian/Services/IUserCache.cs b/Obsidian/Services/IUserCache.cs
index 700285c45..9a450b0bc 100644
--- a/Obsidian/Services/IUserCache.cs
+++ b/Obsidian/Services/IUserCache.cs
@@ -1,4 +1,5 @@
-using Obsidian.Entities;
+using Microsoft.Extensions.Logging;
+using Obsidian.Entities;
using Obsidian.Utilities.Mojang;
using System.Diagnostics;
using System.IO;
@@ -10,94 +11,79 @@
using System.Web;
namespace Obsidian.Services;
-public sealed class UserCache(HttpClient httpClient) : IUserCache
+public sealed class UserCache(HttpClient httpClient, ILogger logger) : IUserCache
{
private const string userWithNameEndpoint = "https://api.mojang.com/users/profiles/minecraft/";
private const string userWithIdEndpoint = "https://sessionserver.mojang.com/session/minecraft/profile/";
private const string verifySessionEndpoint = "https://sessionserver.mojang.com/session/minecraft/hasJoined";
private readonly FileInfo cacheFile = new("usercache.json");
+ private readonly ILogger logger = logger;
- private ConcurrentDictionary cachedUsers;
+ private List cachedUsers = new();
- public ConcurrentDictionary OnlinePlayers { get; } = new ();
-
- public async Task GetCachedUserFromNameAsync(string username)
+ public async Task GetCachedUserFromNameAsync(string username)
{
var escapedUsername = Sanitize(username);
- CachedUser? cachedUser;
- if (cachedUsers.Any(x => x.Value.Name == username))
- {
- cachedUser = cachedUsers.First(x => x.Value.Name == username).Value;
-
- if (!cachedUser.Expired)
- return cachedUser;
- }
+ var cachedUser = this.cachedUsers.FirstOrDefault(x => x.Name == username);
+ if (cachedUser != null && !cachedUser.Expired)
+ return cachedUser;
- var user = await httpClient.GetFromJsonAsync($"{userWithNameEndpoint}{escapedUsername}", Globals.JsonOptions);
+ var user = await httpClient.GetFromJsonAsync($"{userWithNameEndpoint}{escapedUsername}", Globals.JsonOptions);
if (user is null)
return null;
- if (cachedUsers.TryGetValue(user.Id, out cachedUser))
- {
- if (cachedUser.Expired)
- {
- cachedUser.ExpiresOn = DateTimeOffset.UtcNow.AddMonths(1);
- cachedUser.Name = user.Name;
- }
-
- return cachedUser;
- }
-
cachedUser = new()
{
- Id = user.Id,
+ Uuid = user.Id,
Name = user.Name,
ExpiresOn = DateTimeOffset.UtcNow.AddMonths(1)
};
- cachedUsers.TryAdd(cachedUser.Id, cachedUser);
+ cachedUsers.Add(cachedUser);
return cachedUser;
}
- public async Task GetCachedUserFromUuidAsync(Guid uuid)
+ public async Task GetCachedUserFromUuidAsync(Guid uuid)
{
- if (cachedUsers.TryGetValue(uuid, out var user) && !user.Expired)
- return user;
+ var cachedUser = this.cachedUsers.FirstOrDefault(x => x.Uuid == uuid);
+ if (cachedUser != null && !cachedUser.Expired)
+ return cachedUser;
var escapedUuid = Sanitize(uuid.ToString("N"));
- var mojangUser = await httpClient.GetFromJsonAsync($"{userWithIdEndpoint}{escapedUuid}", Globals.JsonOptions) ?? throw new UnreachableException();
- user = new()
+ var mojangProfile = await httpClient.GetFromJsonAsync($"{userWithIdEndpoint}{escapedUuid}", Globals.JsonOptions) ?? throw new UnreachableException();
+
+ cachedUser = new()
{
- Name = mojangUser!.Name,
- Id = uuid,
+ Name = mojangProfile!.Name,
+ Uuid = uuid,
ExpiresOn = DateTimeOffset.UtcNow.AddMonths(1)
};
- cachedUsers.TryAdd(uuid, user);
+ cachedUsers.Add(cachedUser);
- return user;
+ return cachedUser;
}
- public async Task HasJoinedAsync(string username, string serverId)
+ public async Task HasJoinedAsync(string username, string serverId)
{
var escapedUsername = Sanitize(username);
var escapedServerId = Sanitize(serverId);
- return await httpClient.GetFromJsonAsync($"{verifySessionEndpoint}?username={escapedUsername}&serverId={escapedServerId}", Globals.JsonOptions);
+ return await httpClient.GetFromJsonAsync($"{verifySessionEndpoint}?username={escapedUsername}&serverId={escapedServerId}", Globals.JsonOptions);
}
public async Task SaveAsync(CancellationToken cancellationToken = default)
{
await using var sw = cacheFile.Open(FileMode.Truncate, FileAccess.Write);
- await JsonSerializer.SerializeAsync(sw, cachedUsers, Globals.JsonOptions);
+ await JsonSerializer.SerializeAsync(sw, cachedUsers, Globals.JsonOptions, cancellationToken);
- await sw.FlushAsync();
+ await sw.FlushAsync(cancellationToken);
}
public async Task LoadAsync(CancellationToken cancellationToken = default)
@@ -107,8 +93,7 @@ public async Task LoadAsync(CancellationToken cancellationToken = default)
if (sr.Length == 0)
return;
- var userCache = await JsonSerializer.DeserializeAsync>(sr, Globals.JsonOptions, cancellationToken);
-
+ var userCache = await JsonSerializer.DeserializeAsync>(sr, Globals.JsonOptions, cancellationToken);
if (userCache is null)
return;
@@ -120,13 +105,11 @@ public async Task LoadAsync(CancellationToken cancellationToken = default)
public interface IUserCache
{
- public ConcurrentDictionary OnlinePlayers { get; }
-
- public Task GetCachedUserFromNameAsync(string username);
+ public Task GetCachedUserFromNameAsync(string username);
- public Task GetCachedUserFromUuidAsync(Guid uuid);
+ public Task GetCachedUserFromUuidAsync(Guid uuid);
- public Task HasJoinedAsync(string username, string serverId);
+ public Task HasJoinedAsync(string username, string serverId);
public Task SaveAsync(CancellationToken cancellationToken = default);
diff --git a/Obsidian/Utilities/Mojang/MojangUser.cs b/Obsidian/Utilities/Mojang/MojangProfile.cs
similarity index 81%
rename from Obsidian/Utilities/Mojang/MojangUser.cs
rename to Obsidian/Utilities/Mojang/MojangProfile.cs
index bd6e00c16..6932fa6d0 100644
--- a/Obsidian/Utilities/Mojang/MojangUser.cs
+++ b/Obsidian/Utilities/Mojang/MojangProfile.cs
@@ -2,7 +2,7 @@
namespace Obsidian.Utilities.Mojang;
-public sealed class MojangUser
+public sealed class MojangProfile
{
public required Guid Id { get; init; }
@@ -15,11 +15,11 @@ public sealed class MojangUser
public List? Properties { get; init; }
}
-public sealed class CachedUser
+public sealed class CachedProfile
{
public required string Name { get; set; }
- public required Guid Id { get; init; }
+ public required Guid Uuid { get; init; }
public DateTimeOffset ExpiresOn { get; set; }