From cfdd1bae9ced6860d3ccb9420574cb0e4cf21fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Sun, 22 Jun 2025 18:20:24 -0300 Subject: [PATCH 1/4] Merge migrate functions --- .../Field/FieldManager/FieldManager.cs | 4 +- .../PacketHandlers/GuildHandler.cs | 2 +- .../PacketHandlers/HomeHandler.cs | 2 +- .../PacketHandlers/MoveFieldHandler.cs | 2 +- .../PacketHandlers/QuestHandler.cs | 2 +- Maple2.Server.Game/Session/GameSession.cs | 42 ++++++------------- 6 files changed, 18 insertions(+), 36 deletions(-) diff --git a/Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.cs b/Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.cs index e35be8ffa..68a054b84 100644 --- a/Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.cs +++ b/Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.cs @@ -514,7 +514,7 @@ public bool UsePortal(GameSession session, int portalId, string password) { session.Send(PortalPacket.MoveByPortal(session.Player, destinationCube.Position, default)); return true; case CubePortalDestination.SelectedMap: - session.MigrateOutOfInstance(srcPortal.TargetMapId); + session.Migrate(srcPortal.TargetMapId); return true; case CubePortalDestination.FriendHome: Home? home = GetHome(session, fieldPortal); @@ -523,7 +523,7 @@ public bool UsePortal(GameSession session, int portalId, string password) { return false; } - session.MigrateToInstance(home.Indoor.MapId, home.Indoor.OwnerId); + session.Migrate(home.Indoor.MapId, home.Indoor.OwnerId); return true; } return false; diff --git a/Maple2.Server.Game/PacketHandlers/GuildHandler.cs b/Maple2.Server.Game/PacketHandlers/GuildHandler.cs index b0062ca54..911611257 100644 --- a/Maple2.Server.Game/PacketHandlers/GuildHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/GuildHandler.cs @@ -647,7 +647,7 @@ private void HandleEnterHouse(GameSession session) { return; } - session.MigrateToInstance(house.MapId, session.Guild.Id); + session.Migrate(house.MapId, session.Guild.Id); } private void HandleSendGift(GameSession session, IByteReader packet) { diff --git a/Maple2.Server.Game/PacketHandlers/HomeHandler.cs b/Maple2.Server.Game/PacketHandlers/HomeHandler.cs index 7aced9c68..e5b000053 100644 --- a/Maple2.Server.Game/PacketHandlers/HomeHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/HomeHandler.cs @@ -119,6 +119,6 @@ private void HandleWarp(GameSession session, IByteReader packet) { session.Housing.InitNewHome(session.Player.Value.Character.Name, exportedUgcMap); } - session.MigrateToInstance(home.Indoor.MapId, home.Indoor.OwnerId); + session.Migrate(home.Indoor.MapId, home.Indoor.OwnerId); } } diff --git a/Maple2.Server.Game/PacketHandlers/MoveFieldHandler.cs b/Maple2.Server.Game/PacketHandlers/MoveFieldHandler.cs index 987f8ee30..6beeb6f7c 100644 --- a/Maple2.Server.Game/PacketHandlers/MoveFieldHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/MoveFieldHandler.cs @@ -101,7 +101,7 @@ private void HandleVisitHome(GameSession session, IByteReader packet) { } } - session.MigrateToInstance(home.Indoor.MapId, home.Indoor.OwnerId); + session.Migrate(home.Indoor.MapId, home.Indoor.OwnerId); } private void HandleReturn(GameSession session) { diff --git a/Maple2.Server.Game/PacketHandlers/QuestHandler.cs b/Maple2.Server.Game/PacketHandlers/QuestHandler.cs index 6a7538e03..e894a84f4 100644 --- a/Maple2.Server.Game/PacketHandlers/QuestHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/QuestHandler.cs @@ -203,7 +203,7 @@ private void HandleMapleGuide(GameSession session, IByteReader packet) { } if (metadata.GoToMapId is Constant.DefaultHomeMapId) { - session.MigrateToInstance(Constant.DefaultHomeMapId, session.AccountId); + session.Migrate(Constant.DefaultHomeMapId, session.AccountId); return; } diff --git a/Maple2.Server.Game/Session/GameSession.cs b/Maple2.Server.Game/Session/GameSession.cs index 09a6623dd..a862b18b3 100644 --- a/Maple2.Server.Game/Session/GameSession.cs +++ b/Maple2.Server.Game/Session/GameSession.cs @@ -386,7 +386,7 @@ private bool PrepareFieldInternal(int mapId, out FieldManager? newField, int por } else if (mapId == dungeonField.Lobby.MapId) { newField = dungeonField.Lobby; } else { - MigrateOutOfInstance(mapId); + Migrate(mapId); newField = null; return false; } @@ -504,7 +504,7 @@ public void ReturnField() { character.ReturnPosition = default; if (character.MapId is Constant.DefaultHomeMapId) { - MigrateOutOfInstance(mapId); + Migrate(mapId); return; } @@ -515,7 +515,7 @@ public void ReturnField() { houseRank.TryGetValue(Guild.Guild.HouseTheme, out GuildTable.House? house); if (house?.MapId == character.MapId) { - MigrateOutOfInstance(mapId); + Migrate(mapId); return; } } @@ -659,8 +659,8 @@ public void MigrateToPlanner(PlotMode plotMode) { } } - public void MigrateToInstance(int mapId, long ownerId) { - bool instancedContent = ServerTableMetadata.InstanceFieldTable.Entries.ContainsKey(mapId); + public void Migrate(int mapId, long ownerId = 0) { + bool isInstanced = ServerTableMetadata.InstanceFieldTable.Entries.ContainsKey(mapId); try { var request = new MigrateOutRequest { @@ -670,38 +670,20 @@ public void MigrateToInstance(int mapId, long ownerId) { Server = Server.World.Service.Server.Game, MapId = mapId, OwnerId = ownerId, - InstancedContent = instancedContent, + InstancedContent = isInstanced, }; MigrateOutResponse response = World.MigrateOut(request); var endpoint = new IPEndPoint(IPAddress.Parse(response.IpAddress), response.Port); Send(MigrationPacket.GameToGame(endpoint, response.Token, mapId)); - Player.Value.Character.ReturnChannel = Player.Value.Character.Channel; - State = SessionState.ChangeMap; - } catch (RpcException ex) { - Send(MigrationPacket.GameToGameError(MigrationError.s_move_err_default)); - Send(NoticePacket.Disconnect(new InterfaceText(ex.Message))); - } finally { - Disconnect(); - } - } - public void MigrateOutOfInstance(int mapId) { - try { - var request = new MigrateOutRequest { - AccountId = AccountId, - CharacterId = CharacterId, - MachineId = MachineId.ToString(), - Server = Server.World.Service.Server.Game, - Channel = Player.Value.Character.ReturnChannel, - MapId = mapId, - }; + if (isInstanced) { + Player.Value.Character.ReturnChannel = Player.Value.Character.Channel; + } else { + Player.Value.Character.MapId = mapId; + Player.Value.Character.ReturnChannel = 0; + } - MigrateOutResponse response = World.MigrateOut(request); - var endpoint = new IPEndPoint(IPAddress.Parse(response.IpAddress), response.Port); - Send(MigrationPacket.GameToGame(endpoint, response.Token, mapId)); - Player.Value.Character.MapId = mapId; - Player.Value.Character.ReturnChannel = 0; State = SessionState.ChangeMap; } catch (RpcException ex) { Send(MigrationPacket.GameToGameError(MigrationError.s_move_err_default)); From 0f5934e43726fdd0d2e892624595630d75358e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Sun, 22 Jun 2025 19:26:02 -0300 Subject: [PATCH 2/4] Fix: migrating to another server/game session Implemented a lock mechanism to only allow one database operation per account id --- Maple2.Server.Core/proto/world/world.proto | 12 +++ Maple2.Server.Game/Session/GameSession.cs | 87 +++++++++++++++---- Maple2.Server.Login/Session/LoginSession.cs | 47 +++++++++- .../Service/WorldService.Locks.cs | 24 +++++ 4 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 Maple2.Server.World/Service/WorldService.Locks.cs diff --git a/Maple2.Server.Core/proto/world/world.proto b/Maple2.Server.Core/proto/world/world.proto index e100578d4..9cdff43a0 100644 --- a/Maple2.Server.Core/proto/world/world.proto +++ b/Maple2.Server.Core/proto/world/world.proto @@ -59,6 +59,9 @@ service World { rpc PlayerConfig(PlayerConfigRequest) returns (PlayerConfigResponse); // Disconnect rpc Disconnect(maple2.DisconnectRequest) returns (maple2.DisconnectResponse); + // Acquire and release locks for database operations. + rpc AcquireLock (LockRequest) returns (LockResponse); + rpc ReleaseLock (LockRequest) returns (LockResponse); } enum Server { @@ -572,3 +575,12 @@ message DeathInfo { int32 ms_remaining = 2; int64 stop_time = 3; } + +message LockRequest { + int64 accountId = 1; +} + +message LockResponse { + bool success = 1; + string error = 2; +} diff --git a/Maple2.Server.Game/Session/GameSession.cs b/Maple2.Server.Game/Session/GameSession.cs index a862b18b3..dbe199a75 100644 --- a/Maple2.Server.Game/Session/GameSession.cs +++ b/Maple2.Server.Game/Session/GameSession.cs @@ -3,6 +3,7 @@ using System.Net; using System.Net.Sockets; using System.Numerics; +using System.Runtime.CompilerServices; using Autofac; using Grpc.Core; using Maple2.Database.Extensions; @@ -56,6 +57,7 @@ public sealed partial class GameSession : Core.Network.Session { #region Autofac Autowired // ReSharper disable MemberCanBePrivate.Global + // ReSharper disable UnusedAutoPropertyAccessor.Global public required GameStorage GameStorage { get; init; } public required WorldClient World { get; init; } public required ItemMetadataStorage ItemMetadata { get; init; } @@ -73,6 +75,7 @@ public sealed partial class GameSession : Core.Network.Session { public required ItemStatsCalculator ItemStatsCalc { private get; init; } public required PlayerInfoStorage PlayerInfo { get; init; } // ReSharper restore All + // ReSharper restore UnusedAutoPropertyAccessor.Global #endregion public ConfigManager Config { get; set; } = null!; @@ -146,13 +149,19 @@ public bool EnterServer(long accountId, Guid machineId, MigrateInResponse migrat using GameStorage.Request db = GameStorage.Context(); db.BeginTransaction(); int objectId = FieldManager.NextGlobalId(); - Player? player = db.LoadPlayer(AccountId, CharacterId, objectId, GameServer.GetChannel()); + Player? player; + try { + AcquireLock(CharacterId, 5); + player = db.LoadPlayer(AccountId, CharacterId, objectId, GameServer.GetChannel()); + db.Commit(); + } finally { + ReleaseLock(CharacterId); + } if (player == null) { Logger.Warning("Failed to load player from database: {AccountId}, {CharacterId}", AccountId, CharacterId); Send(MigrationPacket.MoveResult(MigrationError.s_move_err_default)); return false; } - db.Commit(); Player = new FieldPlayer(this, player); Animation = new AnimationManager(this); @@ -693,6 +702,42 @@ public void Migrate(int mapId, long ownerId = 0) { } } + public void AcquireLock(long accountId, int maxRetries = 3) { + int retryCount = 0; + const int backoffMs = 500; + + while (retryCount < maxRetries) { + LockResponse? response = World.AcquireLock(new LockRequest { + AccountId = accountId, + }); + + if (response.Success) { + Logger.Information("Acquired lock for account {AccountId}", accountId); + return; + } + Logger.Information("Failed to acquire lock for account {AccountId}, retrying... (Attempt {RetryCount}/{MaxRetries})", accountId, retryCount + 1, maxRetries); + + retryCount++; + Thread.Sleep(backoffMs); + } + + Logger.Error("Failed to acquire lock for account {AccountId} after {MaxRetries} retries", accountId, maxRetries); + } + + private void ReleaseLock(long accountId) { + try { + LockResponse response = World.ReleaseLock(new LockRequest { + AccountId = accountId, + }); + if (response.Success) { + Logger.Information("Released lock for account {AccountId}", accountId); + } else { + Logger.Warning("Failed to release lock for account {AccountId}: {ErrorMessage}", accountId, response.Error); + } + } catch (RpcException ex) { + Logger.Error(ex, "Failed to release lock for account {AccountId}", accountId); + } + } #region Dispose ~GameSession() => Dispose(false); @@ -723,7 +768,28 @@ protected override void Dispose(bool disposing) { Player.Value.Account.Online = false; State = SessionState.Disconnected; Complete(); + + SaveCacheConfig(); + AcquireLock(CharacterId); + using GameStorage.Request db = GameStorage.Context(); + db.BeginTransaction(); + db.SavePlayer(Player); + UgcMarket.Save(db); + Config.Save(db); + Shop.Save(db); + Item.Save(db); + Survival.Save(db); + Housing.Save(db); + GameEvent.Save(db); + Achievement.Save(db); + Quest.Save(db); + Dungeon.Save(db); + db.Commit(); + db.SaveChanges(); + } catch (Exception ex) { + Logger.Error(ex, "Error during session cleanup for {Player}", PlayerName); } finally { + ReleaseLock(CharacterId); Guild.Dispose(); Buddy.Dispose(); Party.Dispose(); @@ -735,23 +801,6 @@ protected override void Dispose(bool disposing) { club.Dispose(); } - SaveCacheConfig(); - using (GameStorage.Request db = GameStorage.Context()) { - db.BeginTransaction(); - db.SavePlayer(Player); - UgcMarket.Save(db); - Config.Save(db); - Shop.Save(db); - Item.Save(db); - Survival.Save(db); - Housing.Save(db); - GameEvent.Save(db); - Achievement.Save(db); - Quest.Save(db); - Dungeon.Save(db); - db.Commit(); - db.SaveChanges(); - } Player.Dispose(); base.Dispose(disposing); } diff --git a/Maple2.Server.Login/Session/LoginSession.cs b/Maple2.Server.Login/Session/LoginSession.cs index 58a67c9a6..8468f19a6 100644 --- a/Maple2.Server.Login/Session/LoginSession.cs +++ b/Maple2.Server.Login/Session/LoginSession.cs @@ -3,6 +3,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; +using Grpc.Core; using Maple2.Database.Storage; using Maple2.Model.Enum; using Maple2.Model.Game; @@ -52,6 +53,43 @@ public void Init(long accountId, Guid machineId) { Server.OnConnected(this); } + public void AcquireLock(long accountId, int maxRetries = 3) { + int retryCount = 0; + const int backoffMs = 1000; + + while (retryCount < maxRetries) { + LockResponse? response = World.AcquireLock(new LockRequest { + AccountId = accountId, + }); + + if (response.Success) { + Logger.Information("Acquired lock for account {AccountId}", accountId); + return; + } + Logger.Information("Failed to acquire lock for account {AccountId}, retrying... (Attempt {RetryCount}/{MaxRetries})", accountId, retryCount + 1, maxRetries); + + retryCount++; + Thread.Sleep(backoffMs); + } + + Logger.Error("Failed to acquire lock for account {AccountId} after {MaxRetries} retries", accountId, maxRetries); + } + + private void ReleaseLock(long accountId) { + try { + LockResponse response = World.ReleaseLock(new LockRequest { + AccountId = accountId, + }); + if (response.Success) { + Logger.Information("Released lock for account {AccountId}", accountId); + } else { + Logger.Warning("Failed to release lock for account {AccountId}: {ErrorMessage}", accountId, response.Error); + } + } catch (RpcException ex) { + Logger.Error(ex, "Failed to release lock for account {AccountId}", accountId); + } + } + public void ListServers() { ChannelsResponse response = World.Channels(new ChannelsRequest()); Send(BannerListPacket.Load(Server.GetSystemBanners())); @@ -60,7 +98,14 @@ public void ListServers() { public void ListCharacters() { using GameStorage.Request db = GameStorage.Context(); - (Account? readAccount, IList? characters) = db.ListCharacters(AccountId); + AcquireLock(AccountId); + Account? readAccount; + IList? characters; + try { + (readAccount, characters) = db.ListCharacters(AccountId); + } finally { + ReleaseLock(AccountId); + } if (readAccount == null || characters == null) { throw new InvalidOperationException($"Failed to load characters for account: {AccountId}"); } diff --git a/Maple2.Server.World/Service/WorldService.Locks.cs b/Maple2.Server.World/Service/WorldService.Locks.cs new file mode 100644 index 000000000..e2d31857d --- /dev/null +++ b/Maple2.Server.World/Service/WorldService.Locks.cs @@ -0,0 +1,24 @@ +using System.Collections.Concurrent; +using Grpc.Core; + +namespace Maple2.Server.World.Service; + +public partial class WorldService { + private static readonly ConcurrentDictionary Locks = new ConcurrentDictionary(); + + public override Task AcquireLock(LockRequest request, ServerCallContext context) { + bool acquired = Locks.TryAdd(request.AccountId, true); + if (acquired) { + return Task.FromResult(new LockResponse { Success = true }); + } + return Task.FromResult(new LockResponse { Success = false, Error = "Lock already held." }); + } + + public override Task ReleaseLock(LockRequest request, ServerCallContext context) { + bool removed = Locks.TryRemove(request.AccountId, out _); + if (removed) { + return Task.FromResult(new LockResponse { Success = true }); + } + return Task.FromResult(new LockResponse { Success = false, Error = "Lock not held." }); + } +} From f2fd8343923210a266b4879d959d0e180112c971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Sun, 22 Jun 2025 19:53:15 -0300 Subject: [PATCH 3/4] suggestions --- Maple2.Server.Login/Session/LoginSession.cs | 2 +- .../Service/WorldService.Locks.cs | 33 +++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Maple2.Server.Login/Session/LoginSession.cs b/Maple2.Server.Login/Session/LoginSession.cs index 8468f19a6..0b970989a 100644 --- a/Maple2.Server.Login/Session/LoginSession.cs +++ b/Maple2.Server.Login/Session/LoginSession.cs @@ -55,7 +55,7 @@ public void Init(long accountId, Guid machineId) { public void AcquireLock(long accountId, int maxRetries = 3) { int retryCount = 0; - const int backoffMs = 1000; + const int backoffMs = 500; while (retryCount < maxRetries) { LockResponse? response = World.AcquireLock(new LockRequest { diff --git a/Maple2.Server.World/Service/WorldService.Locks.cs b/Maple2.Server.World/Service/WorldService.Locks.cs index e2d31857d..36d441c36 100644 --- a/Maple2.Server.World/Service/WorldService.Locks.cs +++ b/Maple2.Server.World/Service/WorldService.Locks.cs @@ -4,21 +4,42 @@ namespace Maple2.Server.World.Service; public partial class WorldService { - private static readonly ConcurrentDictionary Locks = new ConcurrentDictionary(); + private static readonly TimeSpan LockTimeout = TimeSpan.FromSeconds(30); + private static readonly ConcurrentDictionary Locks = new ConcurrentDictionary(); public override Task AcquireLock(LockRequest request, ServerCallContext context) { - bool acquired = Locks.TryAdd(request.AccountId, true); + DateTime now = DateTime.UtcNow; + + // Remove expired lock if present + if (Locks.TryGetValue(request.AccountId, out DateTime timestamp)) { + if (now - timestamp > LockTimeout) { + Locks.TryRemove(request.AccountId, out _); + } + } + + // Try to acquire lock + bool acquired = Locks.TryAdd(request.AccountId, now); if (acquired) { - return Task.FromResult(new LockResponse { Success = true }); + return Task.FromResult(new LockResponse { + Success = true, + }); } - return Task.FromResult(new LockResponse { Success = false, Error = "Lock already held." }); + return Task.FromResult(new LockResponse { + Success = false, + Error = "Lock already held.", + }); } public override Task ReleaseLock(LockRequest request, ServerCallContext context) { bool removed = Locks.TryRemove(request.AccountId, out _); if (removed) { - return Task.FromResult(new LockResponse { Success = true }); + return Task.FromResult(new LockResponse { + Success = true, + }); } - return Task.FromResult(new LockResponse { Success = false, Error = "Lock not held." }); + return Task.FromResult(new LockResponse { + Success = false, + Error = "Lock not held.", + }); } } From f922bf8aa91cd79cee0076d4557a63eab4819497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Sun, 22 Jun 2025 23:51:29 -0300 Subject: [PATCH 4/4] suggestions --- Maple2.Server.Core/proto/world/world.proto | 3 +-- Maple2.Server.Game/Session/GameSession.cs | 10 +++------- Maple2.Server.Login/Session/LoginSession.cs | 10 +++------- Maple2.Server.World/Service/WorldService.Locks.cs | 10 ++-------- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/Maple2.Server.Core/proto/world/world.proto b/Maple2.Server.Core/proto/world/world.proto index 9cdff43a0..a14a9f36f 100644 --- a/Maple2.Server.Core/proto/world/world.proto +++ b/Maple2.Server.Core/proto/world/world.proto @@ -581,6 +581,5 @@ message LockRequest { } message LockResponse { - bool success = 1; - string error = 2; + string error = 1; } diff --git a/Maple2.Server.Game/Session/GameSession.cs b/Maple2.Server.Game/Session/GameSession.cs index dbe199a75..a074af7b3 100644 --- a/Maple2.Server.Game/Session/GameSession.cs +++ b/Maple2.Server.Game/Session/GameSession.cs @@ -702,7 +702,7 @@ public void Migrate(int mapId, long ownerId = 0) { } } - public void AcquireLock(long accountId, int maxRetries = 3) { + private void AcquireLock(long accountId, int maxRetries = 3) { int retryCount = 0; const int backoffMs = 500; @@ -711,11 +711,9 @@ public void AcquireLock(long accountId, int maxRetries = 3) { AccountId = accountId, }); - if (response.Success) { - Logger.Information("Acquired lock for account {AccountId}", accountId); + if (string.IsNullOrEmpty(response.Error)) { return; } - Logger.Information("Failed to acquire lock for account {AccountId}, retrying... (Attempt {RetryCount}/{MaxRetries})", accountId, retryCount + 1, maxRetries); retryCount++; Thread.Sleep(backoffMs); @@ -729,9 +727,7 @@ private void ReleaseLock(long accountId) { LockResponse response = World.ReleaseLock(new LockRequest { AccountId = accountId, }); - if (response.Success) { - Logger.Information("Released lock for account {AccountId}", accountId); - } else { + if (!string.IsNullOrEmpty(response.Error)) { Logger.Warning("Failed to release lock for account {AccountId}: {ErrorMessage}", accountId, response.Error); } } catch (RpcException ex) { diff --git a/Maple2.Server.Login/Session/LoginSession.cs b/Maple2.Server.Login/Session/LoginSession.cs index 0b970989a..401bf5041 100644 --- a/Maple2.Server.Login/Session/LoginSession.cs +++ b/Maple2.Server.Login/Session/LoginSession.cs @@ -53,7 +53,7 @@ public void Init(long accountId, Guid machineId) { Server.OnConnected(this); } - public void AcquireLock(long accountId, int maxRetries = 3) { + private void AcquireLock(long accountId, int maxRetries = 3) { int retryCount = 0; const int backoffMs = 500; @@ -62,11 +62,9 @@ public void AcquireLock(long accountId, int maxRetries = 3) { AccountId = accountId, }); - if (response.Success) { - Logger.Information("Acquired lock for account {AccountId}", accountId); + if (string.IsNullOrEmpty(response.Error)) { return; } - Logger.Information("Failed to acquire lock for account {AccountId}, retrying... (Attempt {RetryCount}/{MaxRetries})", accountId, retryCount + 1, maxRetries); retryCount++; Thread.Sleep(backoffMs); @@ -80,9 +78,7 @@ private void ReleaseLock(long accountId) { LockResponse response = World.ReleaseLock(new LockRequest { AccountId = accountId, }); - if (response.Success) { - Logger.Information("Released lock for account {AccountId}", accountId); - } else { + if (!string.IsNullOrEmpty(response.Error)) { Logger.Warning("Failed to release lock for account {AccountId}: {ErrorMessage}", accountId, response.Error); } } catch (RpcException ex) { diff --git a/Maple2.Server.World/Service/WorldService.Locks.cs b/Maple2.Server.World/Service/WorldService.Locks.cs index 36d441c36..6ab2f3a9d 100644 --- a/Maple2.Server.World/Service/WorldService.Locks.cs +++ b/Maple2.Server.World/Service/WorldService.Locks.cs @@ -20,12 +20,9 @@ public override Task AcquireLock(LockRequest request, ServerCallCo // Try to acquire lock bool acquired = Locks.TryAdd(request.AccountId, now); if (acquired) { - return Task.FromResult(new LockResponse { - Success = true, - }); + return Task.FromResult(new LockResponse()); } return Task.FromResult(new LockResponse { - Success = false, Error = "Lock already held.", }); } @@ -33,12 +30,9 @@ public override Task AcquireLock(LockRequest request, ServerCallCo public override Task ReleaseLock(LockRequest request, ServerCallContext context) { bool removed = Locks.TryRemove(request.AccountId, out _); if (removed) { - return Task.FromResult(new LockResponse { - Success = true, - }); + return Task.FromResult(new LockResponse()); } return Task.FromResult(new LockResponse { - Success = false, Error = "Lock not held.", }); }