From ef4222d59fe7b3976de731795c79ac1d8442e722 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Wed, 18 Dec 2024 02:13:51 -0800 Subject: [PATCH 1/3] TrackingRoute -> CommandRoute. Move Epitaph reset command to CommandRoute since its not really a packet. Move some Rpc machinery to Shared so it can be called from the Login Server. Add setting to have the login server request the game server to kick clients on 6008. --- .../Characters/ClanManager.cs | 2 +- Arrowgene.Ddon.GameServer/RpcManager.cs | 56 +------------------ .../Tasks/EpitaphSchedulerTask.cs | 3 +- .../Handler/ClientLoginHandler.cs | 40 +++++++++++++ .../{TrackingRoute.cs => CommandRoute.cs} | 34 ++++++++--- .../Route/Internal/PacketRoute.cs | 7 +-- Arrowgene.Ddon.Rpc.Web/RpcWebServer.cs | 2 +- Arrowgene.Ddon.Server/LoginServerSetting.cs | 5 +- .../Model/Rpc/RpcInternalCommand.cs | 3 +- .../Model/Rpc/RpcUnwrappedObject.cs | 39 +++++++++++++ .../Model/Rpc/RpcWrappedObject.cs | 16 ++++++ 11 files changed, 131 insertions(+), 76 deletions(-) rename Arrowgene.Ddon.Rpc.Web/Route/Internal/{TrackingRoute.cs => CommandRoute.cs} (60%) create mode 100644 Arrowgene.Ddon.Shared/Model/Rpc/RpcUnwrappedObject.cs create mode 100644 Arrowgene.Ddon.Shared/Model/Rpc/RpcWrappedObject.cs diff --git a/Arrowgene.Ddon.GameServer/Characters/ClanManager.cs b/Arrowgene.Ddon.GameServer/Characters/ClanManager.cs index 3c9d0a6a1..5b6fd57f0 100644 --- a/Arrowgene.Ddon.GameServer/Characters/ClanManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/ClanManager.cs @@ -520,7 +520,7 @@ public PacketQueue CompleteClanQuest(Quest quest, GameClient client) { ClanQuestClearCount[characterId][quest.QuestScheduleId] = ClanQuestClearCount[characterId].GetValueOrDefault(quest.QuestScheduleId) + 1; - Server.RpcManager.AnnounceOthers("internal/tracking", RpcInternalCommand.NotifyClanQuestCompletion, new RpcQuestCompletionData() + Server.RpcManager.AnnounceOthers("internal/command", RpcInternalCommand.NotifyClanQuestCompletion, new RpcQuestCompletionData() { CharacterId = characterId, QuestStatus = ClanQuestClearCount[characterId] diff --git a/Arrowgene.Ddon.GameServer/RpcManager.cs b/Arrowgene.Ddon.GameServer/RpcManager.cs index 0e47f642a..82ff7a66e 100644 --- a/Arrowgene.Ddon.GameServer/RpcManager.cs +++ b/Arrowgene.Ddon.GameServer/RpcManager.cs @@ -9,13 +9,10 @@ using Arrowgene.Logging; using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Net.Http; using System.Net.Http.Json; -using System.Security.Claims; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; namespace Arrowgene.Ddon.GameServer @@ -60,52 +57,6 @@ public bool Update(DateTime newTimestamp, List characterData) private readonly Dictionary CharacterTrackingMap; - public class RpcWrappedObject - { - public RpcInternalCommand Command { get; set; } - public ushort Origin { get; set; } - public object Data { get; set; } - public DateTime Timestamp { get; set; } - public RpcWrappedObject() - { - Timestamp = DateTime.UtcNow; - } - } - - public class RpcUnwrappedObject - { - public RpcInternalCommand Command { get; set; } - public ushort Origin { get; set; } - public DateTime Timestamp { get; set; } - - [JsonConverter(typeof(DataJsonConverter))] - public string Data { get; set; } - public T GetData() - { - return JsonSerializer.Deserialize(Data); - } - - // Hack to deserialize nested objects. - internal class DataJsonConverter : JsonConverter - { - public override string Read( - ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - using (var jsonDoc = JsonDocument.ParseValue(ref reader)) - { - return jsonDoc.RootElement.GetRawText(); - } - } - - public override void Write( - Utf8JsonWriter writer, string value, JsonSerializerOptions options) - { - throw new NotImplementedException(); - } - } - } - - public RpcManager(DdonGameServer server) { Server = server; @@ -296,7 +247,7 @@ public void AnnouncePlayerList(Character exception = null) rpcCharacterDatas.Add(new(character)); } Logger.Info($"Announcing player list for channel {Server.Id} with {rpcCharacterDatas.Count} players over RPC."); - AnnounceOthers("internal/tracking", RpcInternalCommand.NotifyPlayerList, rpcCharacterDatas); + AnnounceOthers("internal/command", RpcInternalCommand.NotifyPlayerList, rpcCharacterDatas); CharacterTrackingMap[(ushort) Server.Id].Update(DateTime.Now, rpcCharacterDatas); } @@ -419,10 +370,5 @@ public void AnnounceClanPacket(uint clanId, T packet, uint characterId = 0) AnnounceClan(clanId, "internal/packet", RpcInternalCommand.AnnouncePacketClan, data); } } - - public void AnnounceEpitaphWeeklyReset() - { - AnnounceAll("internal/packet", RpcInternalCommand.EpitaphRoadWeeklyReset, null); - } } } diff --git a/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs b/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs index ff835cb9f..204e86585 100644 --- a/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs +++ b/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs @@ -25,8 +25,7 @@ public override void RunTask(DdonGameServer server) { Logger.Info("Performing weekly epitaph reset"); server.Database.DeleteWeeklyEpitaphClaimedRewards(); - - server.RpcManager.AnnounceEpitaphWeeklyReset(); + server.RpcManager.AnnounceAll("internal/packet", RpcInternalCommand.EpitaphRoadWeeklyReset, null); } } } diff --git a/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs b/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs index 2e3b18d8f..59ee093e3 100644 --- a/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs +++ b/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Channels; using Arrowgene.Ddon.Database.Model; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Ddon.Shared.Model.Rpc; using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; @@ -16,12 +20,17 @@ public class ClientLoginHandler : LoginStructurePacketHandler private readonly LoginServerSetting _setting; private readonly object _tokensInFLightLock; private readonly HashSet _tokensInFlight; + private readonly HttpClient _httpClient = new HttpClient(); public ClientLoginHandler(DdonLoginServer server) : base(server) { _setting = server.Setting; _tokensInFLightLock = new object(); _tokensInFlight = new HashSet(); + + string authToken = server.AssetRepository.ServerList.Find(x => x.Id == server.Id)?.RpcAuthToken ?? + throw new Exception($"Server with ID {server.Id} was not found in the ServerList asset."); + _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Internal", $"{server.Id}:{authToken}"); } public override void Handle(LoginClient client, StructurePacket packet) @@ -106,6 +115,15 @@ public override void Handle(LoginClient client, StructurePacket pac Logger.Error(client, $"Already logged in"); res.Error = (uint) ErrorCode.ERROR_CODE_AUTH_MULTIPLE_LOGIN; client.Send(res); + + if (_setting.KickOnMultipleLogin) + { + foreach (var conn in connections) + { + RequestKick(conn); + } + } + return; } @@ -172,5 +190,27 @@ private bool LockToken(string token) return true; } } + + private void RequestKick(Connection connection) + { + if (connection.Type == ConnectionType.LoginServer) + { + // Can't talk to the login server, but there's usually not a stuck connection here. + return; + } + + var channel = Server.AssetRepository.ServerList.Find(x => x.Id == connection.ServerId); + var route = $"http://{channel.Addr}:{channel.RpcPort}/rpc/internal/command"; + + var wrappedObject = new RpcWrappedObject() + { + Command = RpcInternalCommand.KickInternal, + Origin = (ushort)Server.Id, + Data = connection.AccountId + }; + + var json = JsonSerializer.Serialize(wrappedObject); + _ = _httpClient.PostAsync(route, new StringContent(json)); + } } } diff --git a/Arrowgene.Ddon.Rpc.Web/Route/Internal/TrackingRoute.cs b/Arrowgene.Ddon.Rpc.Web/Route/Internal/CommandRoute.cs similarity index 60% rename from Arrowgene.Ddon.Rpc.Web/Route/Internal/TrackingRoute.cs rename to Arrowgene.Ddon.Rpc.Web/Route/Internal/CommandRoute.cs index 55adc062d..09f21b237 100644 --- a/Arrowgene.Ddon.Rpc.Web/Route/Internal/TrackingRoute.cs +++ b/Arrowgene.Ddon.Rpc.Web/Route/Internal/CommandRoute.cs @@ -5,21 +5,20 @@ using Arrowgene.WebServer; using System.Collections.Generic; using System.Threading.Tasks; -using static Arrowgene.Ddon.GameServer.RpcManager; namespace Arrowgene.Ddon.Rpc.Web.Route.Internal { - public class TrackingRoute : RpcRouteTemplate + public class CommandRoute : RpcRouteTemplate { - public class InternalTrackingCommand : RpcBodyCommand + public class InternalCommand : RpcBodyCommand { - private static readonly ILogger Logger = LogProvider.Logger(typeof(InternalTrackingCommand)); + private static readonly ILogger Logger = LogProvider.Logger(typeof(InternalCommand)); - public InternalTrackingCommand(RpcUnwrappedObject entry) : base(entry) + public InternalCommand(RpcUnwrappedObject entry) : base(entry) { } - public override string Name => "InternalTrackingCommand"; + public override string Name => "InternalCommand"; public override RpcCommandResult Execute(DdonGameServer gameServer) { @@ -43,21 +42,38 @@ public override RpcCommandResult Execute(DdonGameServer gameServer) Message = $"NotifyClanQuestCompletion for CharacterId {data.CharacterId}" }; } + case RpcInternalCommand.EpitaphRoadWeeklyReset: + { + gameServer.EpitaphRoadManager.PerformWeeklyReset(); + return new RpcCommandResult(this, true); + } + case RpcInternalCommand.KickInternal: + { + uint target = _entry.GetData(); + foreach (var client in gameServer.ClientLookup.GetAll()) + { + if (client.Account.Id == target) + { + client.Close(); + } + } + return new RpcCommandResult(this, true); + } default: return new RpcCommandResult(this, false); } } } - public TrackingRoute(IRpcExecuter executer) : base(executer) + public CommandRoute(IRpcExecuter executer) : base(executer) { } - public override string Route => "/rpc/internal/tracking"; + public override string Route => "/rpc/internal/command"; public async override Task Post(WebRequest request) { - return await HandleBody(request); + return await HandleBody(request); } } } diff --git a/Arrowgene.Ddon.Rpc.Web/Route/Internal/PacketRoute.cs b/Arrowgene.Ddon.Rpc.Web/Route/Internal/PacketRoute.cs index 89d0d44b7..4e8f2b351 100644 --- a/Arrowgene.Ddon.Rpc.Web/Route/Internal/PacketRoute.cs +++ b/Arrowgene.Ddon.Rpc.Web/Route/Internal/PacketRoute.cs @@ -1,17 +1,14 @@ using Arrowgene.Ddon.GameServer; -using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.Rpc.Command; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Model.Clan; -using Arrowgene.Ddon.Shared.Model.Quest; using Arrowgene.Ddon.Shared.Model.Rpc; using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; using Arrowgene.WebServer; using System; -using System.Linq; using System.Threading.Tasks; using static Arrowgene.Ddon.GameServer.RpcManager; @@ -168,9 +165,7 @@ public override RpcCommandResult Execute(DdonGameServer gameServer) Message = $"AnnouncePacketClan Ch.{_entry.Origin} ClanID {data.ClanId} -> {packet.Id}" }; } - case RpcInternalCommand.EpitaphRoadWeeklyReset: - gameServer.EpitaphRoadManager.PerformWeeklyReset(); - return new RpcCommandResult(this, true); + default: return new RpcCommandResult(this, false); } diff --git a/Arrowgene.Ddon.Rpc.Web/RpcWebServer.cs b/Arrowgene.Ddon.Rpc.Web/RpcWebServer.cs index 954b86fd5..2be935da6 100644 --- a/Arrowgene.Ddon.Rpc.Web/RpcWebServer.cs +++ b/Arrowgene.Ddon.Rpc.Web/RpcWebServer.cs @@ -37,7 +37,7 @@ public void Init() #region Internal RPC InternalMiddleware internalMiddleware = new InternalMiddleware(_gameServer); - Route.Internal.TrackingRoute trackingRoute = new(this); + Route.Internal.CommandRoute trackingRoute = new(this); internalMiddleware.Require(trackingRoute.Route); _webServer.AddRoute(trackingRoute); diff --git a/Arrowgene.Ddon.Server/LoginServerSetting.cs b/Arrowgene.Ddon.Server/LoginServerSetting.cs index 2b57cd280..cbb09b0bb 100644 --- a/Arrowgene.Ddon.Server/LoginServerSetting.cs +++ b/Arrowgene.Ddon.Server/LoginServerSetting.cs @@ -8,6 +8,7 @@ public class LoginServerSetting [DataMember(Order = 1)] public ServerSetting ServerSetting { get; set; } [DataMember(Order = 90)] public bool AccountRequired { get; set; } [DataMember(Order = 105)] public uint NoOperationTimeOutTime { get; set; } + [DataMember(Order = 110)] public bool KickOnMultipleLogin { get; set; } public LoginServerSetting() { @@ -19,13 +20,15 @@ public LoginServerSetting() AccountRequired = false; NoOperationTimeOutTime = 14400; + KickOnMultipleLogin = false; } public LoginServerSetting(LoginServerSetting setting) { ServerSetting = new ServerSetting(setting.ServerSetting); AccountRequired = setting.AccountRequired; - NoOperationTimeOutTime = setting.NoOperationTimeOutTime; + NoOperationTimeOutTime = setting.NoOperationTimeOutTime; + KickOnMultipleLogin = setting.KickOnMultipleLogin; } } } diff --git a/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs b/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs index b4a8a1359..0b792fcc2 100644 --- a/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs +++ b/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs @@ -11,6 +11,7 @@ public enum RpcInternalCommand AnnouncePacketAll, // RpcPacketData AnnouncePacketClan, // RpcPacketData - EpitaphRoadWeeklyReset + EpitaphRoadWeeklyReset, // null + KickInternal, // uint } } diff --git a/Arrowgene.Ddon.Shared/Model/Rpc/RpcUnwrappedObject.cs b/Arrowgene.Ddon.Shared/Model/Rpc/RpcUnwrappedObject.cs new file mode 100644 index 000000000..b3cbafaf9 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Model/Rpc/RpcUnwrappedObject.cs @@ -0,0 +1,39 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Arrowgene.Ddon.Shared.Model.Rpc +{ + public class RpcUnwrappedObject + { + public RpcInternalCommand Command { get; set; } + public ushort Origin { get; set; } + public DateTime Timestamp { get; set; } + + [JsonConverter(typeof(DataJsonConverter))] + public string Data { get; set; } + public T GetData() + { + return JsonSerializer.Deserialize(Data); + } + + // Hack to deserialize nested objects. + internal class DataJsonConverter : JsonConverter + { + public override string Read( + ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using (var jsonDoc = JsonDocument.ParseValue(ref reader)) + { + return jsonDoc.RootElement.GetRawText(); + } + } + + public override void Write( + Utf8JsonWriter writer, string value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Model/Rpc/RpcWrappedObject.cs b/Arrowgene.Ddon.Shared/Model/Rpc/RpcWrappedObject.cs new file mode 100644 index 000000000..3fd1b0d6b --- /dev/null +++ b/Arrowgene.Ddon.Shared/Model/Rpc/RpcWrappedObject.cs @@ -0,0 +1,16 @@ +using System; + +namespace Arrowgene.Ddon.Shared.Model.Rpc +{ + public class RpcWrappedObject + { + public RpcInternalCommand Command { get; set; } + public ushort Origin { get; set; } + public object Data { get; set; } + public DateTime Timestamp { get; set; } + public RpcWrappedObject() + { + Timestamp = DateTime.UtcNow; + } + } +} From a874eabcf0d4d141fe921fa54c72c9dd5bf30d21 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Wed, 18 Dec 2024 16:29:37 -0800 Subject: [PATCH 2/3] Clean up ClientLoginHandler's implementation of the kicking stuff. --- .../Handler/ClientLoginHandler.cs | 62 +++++++++++++------ .../Route/Internal/CommandRoute.cs | 3 +- Arrowgene.Ddon.Server/LoginServerSetting.cs | 8 ++- .../Csv/GameServerListInfoCsv.cs | 4 +- .../Files/Assets/GameServerList.csv | 4 +- .../Model/Rpc/RpcInternalCommand.cs | 8 ++- Arrowgene.Ddon.Shared/Model/ServerInfo.cs | 1 + 7 files changed, 64 insertions(+), 26 deletions(-) diff --git a/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs b/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs index 59ee093e3..09b71cb44 100644 --- a/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs +++ b/Arrowgene.Ddon.LoginServer/Handler/ClientLoginHandler.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Channels; using Arrowgene.Ddon.Database.Model; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Shared.Entity.PacketStructure; @@ -10,6 +5,12 @@ using Arrowgene.Ddon.Shared.Model.Rpc; using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading; namespace Arrowgene.Ddon.LoginServer.Handler { @@ -20,17 +21,15 @@ public class ClientLoginHandler : LoginStructurePacketHandler private readonly LoginServerSetting _setting; private readonly object _tokensInFLightLock; private readonly HashSet _tokensInFlight; + private readonly HttpClient _httpClient = new HttpClient(); + private bool _httpReady = false; public ClientLoginHandler(DdonLoginServer server) : base(server) { _setting = server.Setting; _tokensInFLightLock = new object(); _tokensInFlight = new HashSet(); - - string authToken = server.AssetRepository.ServerList.Find(x => x.Id == server.Id)?.RpcAuthToken ?? - throw new Exception($"Server with ID {server.Id} was not found in the ServerList asset."); - _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Internal", $"{server.Id}:{authToken}"); } public override void Handle(LoginClient client, StructurePacket packet) @@ -110,23 +109,32 @@ public override void Handle(LoginClient client, StructurePacket pac } List connections = Database.SelectConnectionsByAccountId(account.Id); - if (connections.Count > 0) - { - Logger.Error(client, $"Already logged in"); - res.Error = (uint) ErrorCode.ERROR_CODE_AUTH_MULTIPLE_LOGIN; - client.Send(res); - if (_setting.KickOnMultipleLogin) + if (_setting.KickOnMultipleLogin) + { + for (uint tryCount = 0; tryCount < _setting.KickOnMultipleLoginTries; tryCount++) { - foreach (var conn in connections) + if (connections.Any()) + { + connections.ForEach(x => RequestKick(x)); + Thread.Sleep(_setting.KickOnMultipleLoginTimer); + connections = Database.SelectConnectionsByAccountId(account.Id); + } + else { - RequestKick(conn); + break; } } + } + if (connections.Any()) + { + Logger.Error(client, $"Already logged in."); + res.Error = (uint)ErrorCode.ERROR_CODE_AUTH_MULTIPLE_LOGIN; + client.Send(res); return; } - + // Order Important, // account need to be only assigned after // verification that no connection exists, and before @@ -193,6 +201,24 @@ private bool LockToken(string token) private void RequestKick(Connection connection) { + // Timing issues with loading files vs server process startup. + if (!_httpReady) + { + lock(_httpClient) + { + ServerInfo serverInfo = Server.AssetRepository.ServerList.Find(x => x.LoginId == Server.Id); + if (serverInfo is null) + { + Logger.Error($"Login server with ID {Server.Id} was not found in the ServerList asset."); + return; + } + + // The login server auths as though it was the game server. + _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Internal", $"{serverInfo.Id}:{serverInfo.RpcAuthToken}"); + _httpReady = true; + } + } + if (connection.Type == ConnectionType.LoginServer) { // Can't talk to the login server, but there's usually not a stuck connection here. diff --git a/Arrowgene.Ddon.Rpc.Web/Route/Internal/CommandRoute.cs b/Arrowgene.Ddon.Rpc.Web/Route/Internal/CommandRoute.cs index 09f21b237..b73974954 100644 --- a/Arrowgene.Ddon.Rpc.Web/Route/Internal/CommandRoute.cs +++ b/Arrowgene.Ddon.Rpc.Web/Route/Internal/CommandRoute.cs @@ -49,7 +49,7 @@ public override RpcCommandResult Execute(DdonGameServer gameServer) } case RpcInternalCommand.KickInternal: { - uint target = _entry.GetData(); + int target = _entry.GetData(); foreach (var client in gameServer.ClientLookup.GetAll()) { if (client.Account.Id == target) @@ -57,6 +57,7 @@ public override RpcCommandResult Execute(DdonGameServer gameServer) client.Close(); } } + gameServer.Database.DeleteConnection(gameServer.Id, target); return new RpcCommandResult(this, true); } default: diff --git a/Arrowgene.Ddon.Server/LoginServerSetting.cs b/Arrowgene.Ddon.Server/LoginServerSetting.cs index cbb09b0bb..bad40b475 100644 --- a/Arrowgene.Ddon.Server/LoginServerSetting.cs +++ b/Arrowgene.Ddon.Server/LoginServerSetting.cs @@ -9,7 +9,9 @@ public class LoginServerSetting [DataMember(Order = 90)] public bool AccountRequired { get; set; } [DataMember(Order = 105)] public uint NoOperationTimeOutTime { get; set; } [DataMember(Order = 110)] public bool KickOnMultipleLogin { get; set; } - + [DataMember(Order = 111)] public int KickOnMultipleLoginTries { get; set; } + [DataMember(Order = 112)] public int KickOnMultipleLoginTimer { get; set; } + public LoginServerSetting() { ServerSetting = new ServerSetting(); @@ -21,6 +23,8 @@ public LoginServerSetting() AccountRequired = false; NoOperationTimeOutTime = 14400; KickOnMultipleLogin = false; + KickOnMultipleLoginTries = 3; + KickOnMultipleLoginTimer = 5000; } public LoginServerSetting(LoginServerSetting setting) @@ -29,6 +33,8 @@ public LoginServerSetting(LoginServerSetting setting) AccountRequired = setting.AccountRequired; NoOperationTimeOutTime = setting.NoOperationTimeOutTime; KickOnMultipleLogin = setting.KickOnMultipleLogin; + KickOnMultipleLoginTries = setting.KickOnMultipleLoginTries; + KickOnMultipleLoginTimer = setting.KickOnMultipleLoginTimer; } } } diff --git a/Arrowgene.Ddon.Shared/Csv/GameServerListInfoCsv.cs b/Arrowgene.Ddon.Shared/Csv/GameServerListInfoCsv.cs index 14c6cfb6b..48c63295a 100644 --- a/Arrowgene.Ddon.Shared/Csv/GameServerListInfoCsv.cs +++ b/Arrowgene.Ddon.Shared/Csv/GameServerListInfoCsv.cs @@ -5,7 +5,7 @@ namespace Arrowgene.Ddon.Shared.Csv; public class GameServerListInfoCsv : CsvReaderWriter { - protected override int NumExpectedItems => 9; + protected override int NumExpectedItems => 10; protected override ServerInfo CreateInstance(string[] properties) { if (!ushort.TryParse(properties[0], out ushort id)) return null; @@ -17,6 +17,7 @@ protected override ServerInfo CreateInstance(string[] properties) if (!bool.TryParse(properties[6], out bool isHide)) return null; if (!ushort.TryParse(properties[7], out ushort rpcPort)) return null; string authToken = properties[8]; + if (!ushort.TryParse(properties[9], out ushort loginId)) return null; return new ServerInfo() { @@ -30,6 +31,7 @@ protected override ServerInfo CreateInstance(string[] properties) IsHide = isHide, RpcPort = rpcPort, RpcAuthToken = authToken, + LoginId = loginId, }; } } diff --git a/Arrowgene.Ddon.Shared/Files/Assets/GameServerList.csv b/Arrowgene.Ddon.Shared/Files/Assets/GameServerList.csv index 364d35c87..4e4590821 100644 --- a/Arrowgene.Ddon.Shared/Files/Assets/GameServerList.csv +++ b/Arrowgene.Ddon.Shared/Files/Assets/GameServerList.csv @@ -1,2 +1,2 @@ -#Id,Name,Brief,MaxLoginNum,IpAddress,Port,IsHide,RpcPort,RpcAuth -10,Local Host,Brief,1000,127.0.0.1,52000,false,52099,AuthToken \ No newline at end of file +#Id,Name,Brief,MaxLoginNum,IpAddress,Port,IsHide,RpcPort,RpcAuth,LoginId +10,Local Host,Brief,1000,127.0.0.1,52000,false,52099,AuthToken,1 \ No newline at end of file diff --git a/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs b/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs index 0b792fcc2..db43f5a0e 100644 --- a/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs +++ b/Arrowgene.Ddon.Shared/Model/Rpc/RpcInternalCommand.cs @@ -2,16 +2,18 @@ namespace Arrowgene.Ddon.Shared.Model.Rpc { public enum RpcInternalCommand { + //CommandRoute NotifyPlayerList, // List NotifyClanQuestCompletion, //RpcQuestCompletionData + EpitaphRoadWeeklyReset, // null + KickInternal, // int + //InternalChatRoute SendTellMessage, // RpcChatData SendClanMessage, // RpcChatData + //PacketRoute AnnouncePacketAll, // RpcPacketData AnnouncePacketClan, // RpcPacketData - - EpitaphRoadWeeklyReset, // null - KickInternal, // uint } } diff --git a/Arrowgene.Ddon.Shared/Model/ServerInfo.cs b/Arrowgene.Ddon.Shared/Model/ServerInfo.cs index 973374ff3..973fb1d74 100644 --- a/Arrowgene.Ddon.Shared/Model/ServerInfo.cs +++ b/Arrowgene.Ddon.Shared/Model/ServerInfo.cs @@ -21,6 +21,7 @@ public ServerInfo() public bool IsHide { get; set; } public ushort RpcPort { get; set; } public string RpcAuthToken { get; set; } + public ushort LoginId { get; set; } public CDataGameServerListInfo ToCDataGameServerListInfo() { From fa92b014ab3c265a5c4cab5b490ecca1d8afb204 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Thu, 19 Dec 2024 18:07:10 -0800 Subject: [PATCH 3/3] Better default behavior for config files. --- .../Characters/CharacterManager.cs | 2 +- .../Characters/EpitaphRoadManager.cs | 2 +- .../Characters/ExpManager.cs | 7 +- .../Characters/RewardManager.cs | 8 +- .../Characters/WalletManager.cs | 8 +- .../GameServerSetting.cs | 27 ++++-- .../Handler/InstanceEnemyKillHandler.cs | 4 +- .../QuestGetCycleContentsStateListHandler.cs | 5 +- .../Tasks/EpitaphSchedulerTask.cs | 3 +- Arrowgene.Ddon.Server/GameLogicSetting.cs | 91 +++++-------------- Arrowgene.Ddon.Server/LoginServerSetting.cs | 31 +++++-- 11 files changed, 79 insertions(+), 109 deletions(-) diff --git a/Arrowgene.Ddon.GameServer/Characters/CharacterManager.cs b/Arrowgene.Ddon.GameServer/Characters/CharacterManager.cs index cb3e1c3c8..247655845 100644 --- a/Arrowgene.Ddon.GameServer/Characters/CharacterManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/CharacterManager.cs @@ -61,7 +61,7 @@ public Character SelectCharacter(uint characterId, DbConnection? connectionIn = character.EpitaphRoadState.UnlockedContent = _Server.Database.GetEpitaphRoadUnlocks(character.CharacterId, connectionIn); - if (_Server.Setting.GameLogicSetting.EnableEpitaphWeeklyRewards.Value) + if (_Server.Setting.GameLogicSetting.EnableEpitaphWeeklyRewards) { character.EpitaphRoadState.WeeklyRewardsClaimed = _Server.Database.GetEpitaphClaimedWeeklyRewards(character.CharacterId, connectionIn); } diff --git a/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs b/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs index 34c959d00..41bce2482 100644 --- a/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs @@ -1290,7 +1290,7 @@ public List RollGatheringLoot(GameClient client, Charact { results.AddRange(RollWeeklyChestReward(dungeonInfo, reward)); - if (_Server.Setting.GameLogicSetting.EnableEpitaphWeeklyRewards.Value) + if (_Server.Setting.GameLogicSetting.EnableEpitaphWeeklyRewards) { character.EpitaphRoadState.WeeklyRewardsClaimed.Add(reward.EpitaphId); _Server.Database.InsertEpitaphWeeklyReward(character.CharacterId, reward.EpitaphId); diff --git a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs index 45c3bde9f..b85978659 100644 --- a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs @@ -10,7 +10,6 @@ using System; using System.Collections.Generic; using System.Data.Common; -using System.Drawing; using System.Linq; namespace Arrowgene.Ddon.GameServer.Characters @@ -906,13 +905,13 @@ public uint GetScaledPointAmount(RewardSource source, ExpType type, uint amount) switch (type) { case ExpType.ExperiencePoints: - modifier = (source == RewardSource.Enemy) ? _GameSettings.EnemyExpModifier.Value : _GameSettings.QuestExpModifier.Value; + modifier = (source == RewardSource.Enemy) ? _GameSettings.EnemyExpModifier : _GameSettings.QuestExpModifier; break; case ExpType.JobPoints: - modifier = _GameSettings.JpModifier.Value; + modifier = _GameSettings.JpModifier; break; case ExpType.PlayPoints: - modifier = _GameSettings.PpModifier.Value; + modifier = _GameSettings.PpModifier; break; default: modifier = 1.0; diff --git a/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs b/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs index a6a9eea34..b922f3c2c 100644 --- a/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs @@ -1,10 +1,8 @@ -using Arrowgene.Ddon.Server; using Arrowgene.Ddon.GameServer.Quests; +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Shared.Model.Quest; using Arrowgene.Logging; using System.Collections.Generic; -using Arrowgene.Ddon.Shared.Model.Quest; -using Arrowgene.Ddon.Shared.Model; -using Arrowgene.Ddon.Database.Model; using System.Data.Common; namespace Arrowgene.Ddon.GameServer.Characters @@ -24,7 +22,7 @@ public bool AddQuestRewards(GameClient client, Quest quest, DbConnection? connec var rewards = quest.GenerateBoxRewards(); var currentRewards = GetQuestBoxRewards(client, connectionIn); - if (currentRewards.Count >= _Server.Setting.GameLogicSetting.RewardBoxMax.Value) + if (currentRewards.Count >= _Server.Setting.GameLogicSetting.RewardBoxMax) { return false; } diff --git a/Arrowgene.Ddon.GameServer/Characters/WalletManager.cs b/Arrowgene.Ddon.GameServer/Characters/WalletManager.cs index f77e0a996..91e407be5 100644 --- a/Arrowgene.Ddon.GameServer/Characters/WalletManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/WalletManager.cs @@ -107,16 +107,16 @@ public uint GetScaledWalletAmount(WalletType type, uint amount) switch (type) { case WalletType.Gold: - modifier = Server.Setting.GameLogicSetting.GoldModifier.Value; + modifier = Server.Setting.GameLogicSetting.GoldModifier; break; case WalletType.RiftPoints: - modifier = Server.Setting.GameLogicSetting.RiftModifier.Value; + modifier = Server.Setting.GameLogicSetting.RiftModifier; break; case WalletType.BloodOrbs: - modifier = Server.Setting.GameLogicSetting.BoModifier.Value; + modifier = Server.Setting.GameLogicSetting.BoModifier; break; case WalletType.HighOrbs: - modifier = Server.Setting.GameLogicSetting.HoModifier.Value; + modifier = Server.Setting.GameLogicSetting.HoModifier; break; default: modifier = 1.0; diff --git a/Arrowgene.Ddon.GameServer/GameServerSetting.cs b/Arrowgene.Ddon.GameServer/GameServerSetting.cs index 2839e7727..b1fba56c1 100644 --- a/Arrowgene.Ddon.GameServer/GameServerSetting.cs +++ b/Arrowgene.Ddon.GameServer/GameServerSetting.cs @@ -1,4 +1,4 @@ -using System.Runtime.Serialization; +using System.Runtime.Serialization; using Arrowgene.Ddon.Server; namespace Arrowgene.Ddon.GameServer @@ -11,13 +11,7 @@ public class GameServerSetting public GameServerSetting() { - ServerSetting = new ServerSetting(); - ServerSetting.Id = 10; - ServerSetting.Name = "Game"; - ServerSetting.ServerPort = 52000; - ServerSetting.ServerSocketSettings.Identity = "Game"; - - GameLogicSetting = new GameLogicSetting(); + SetDefaultValues(); } public GameServerSetting(GameServerSetting setting) @@ -41,5 +35,22 @@ void OnDeserialized(StreamingContext context) GameLogicSetting ??= new GameLogicSetting(); } + + [OnDeserializing] + void OnDeserializing(StreamingContext context) + { + SetDefaultValues(); + } + + void SetDefaultValues() + { + ServerSetting = new ServerSetting(); + ServerSetting.Id = 10; + ServerSetting.Name = "Game"; + ServerSetting.ServerPort = 52000; + ServerSetting.ServerSocketSettings.Identity = "Game"; + + GameLogicSetting = new GameLogicSetting(); + } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs index cb575a332..e891245d0 100644 --- a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs @@ -209,7 +209,7 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne if (enemyKilled.BloodOrbs > 0) { // Drop BO - uint gainedBo = (uint) (enemyKilled.BloodOrbs * _gameServer.Setting.GameLogicSetting.BoModifier.Value); + uint gainedBo = (uint) (enemyKilled.BloodOrbs * _gameServer.Setting.GameLogicSetting.BoModifier); uint bonusBo = (uint) (gainedBo * _gameServer.GpCourseManager.EnemyBloodOrbBonus()); CDataUpdateWalletPoint boUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.BloodOrbs, gainedBo + bonusBo, bonusBo, connectionIn: connectionIn); updateCharacterItemNtc.UpdateWalletList.Add(boUpdateWalletPoint); @@ -218,7 +218,7 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne if (enemyKilled.HighOrbs > 0) { // Drop HO - uint gainedHo = (uint)(enemyKilled.HighOrbs * _gameServer.Setting.GameLogicSetting.HoModifier.Value); + uint gainedHo = (uint)(enemyKilled.HighOrbs * _gameServer.Setting.GameLogicSetting.HoModifier); CDataUpdateWalletPoint hoUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.HighOrbs, gainedHo, connectionIn: connectionIn); updateCharacterItemNtc.UpdateWalletList.Add(hoUpdateWalletPoint); } diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs index fe946366c..45a3c99a4 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs @@ -1,6 +1,5 @@ using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.GameServer.Dump; -using Arrowgene.Ddon.GameServer.Quests; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Shared.Entity; using Arrowgene.Ddon.Shared.Entity.PacketStructure; @@ -36,8 +35,8 @@ public override S2CQuestGetCycleContentsStateListRes Handle(GameClient client, C ntc.WorldManageQuestOrderList = pcap.WorldManageQuestOrderList; // Recover paths + change vocation ntc.QuestDefine = pcap.QuestDefine; // Recover quest log data to be able to accept quests - ntc.QuestDefine.OrderMaxNum = Server.Setting.GameLogicSetting.QuestOrderMax.Value; - ntc.QuestDefine.RewardBoxMaxNum = Server.Setting.GameLogicSetting.RewardBoxMax.Value; + ntc.QuestDefine.OrderMaxNum = Server.Setting.GameLogicSetting.QuestOrderMax; + ntc.QuestDefine.RewardBoxMaxNum = Server.Setting.GameLogicSetting.RewardBoxMax; // pcap.MainQuestIdList; (this will add back all missing functionality which depends on complete MSQ) var completedMsq = client.Character.CompletedQuests.Values.Where(x => x.QuestType == QuestType.Main); diff --git a/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs b/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs index 204e86585..a47fd3dc1 100644 --- a/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs +++ b/Arrowgene.Ddon.GameServer/Tasks/EpitaphSchedulerTask.cs @@ -1,5 +1,4 @@ using Arrowgene.Ddon.Server; -using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Model.Rpc; using Arrowgene.Ddon.Shared.Model.Scheduler; using Arrowgene.Logging; @@ -18,7 +17,7 @@ public EpitaphSchedulerTask(DayOfWeek day, uint hour, uint minute) : base(TaskTy public override bool IsEnabled(DdonGameServer server) { - return server.Setting.GameLogicSetting.EnableEpitaphWeeklyRewards.Value; + return server.Setting.GameLogicSetting.EnableEpitaphWeeklyRewards; } public override void RunTask(DdonGameServer server) diff --git a/Arrowgene.Ddon.Server/GameLogicSetting.cs b/Arrowgene.Ddon.Server/GameLogicSetting.cs index 5b0ec0f88..3b67942ab 100644 --- a/Arrowgene.Ddon.Server/GameLogicSetting.cs +++ b/Arrowgene.Ddon.Server/GameLogicSetting.cs @@ -2,29 +2,9 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -using YamlDotNet.Serialization; namespace Arrowgene.Ddon.Server { - [DataContract] - public class DefaultDataMember - { - private T _DefaultValue; - private T? _Value; - - [DataMember] - public T Value { - get => _Value ?? _DefaultValue; - set => _Value = Value; - } - - public DefaultDataMember(T defaultValue) - { - _DefaultValue = defaultValue; - } - } - - [DataContract] public class GameLogicSetting { @@ -189,58 +169,58 @@ public class GameLogicSetting /// /// Global modifier for enemy exp calculations to scale up or down. /// - [DataMember(Order = 27)] public double? EnemyExpModifier { get; set; } = 1.0; + [DataMember(Order = 27)] public double EnemyExpModifier { get; set; } /// /// Global modifier for quest exp calculations to scale up or down. /// - [DataMember(Order = 28)] public double? QuestExpModifier { get; set; } = 1.0; + [DataMember(Order = 28)] public double QuestExpModifier { get; set; } /// /// Global modifier for pp calculations to scale up or down. /// - [DataMember(Order = 29)] public double? PpModifier { get; set; } = 1.0; + [DataMember(Order = 29)] public double PpModifier { get; set; } /// /// Global modifier for Gold calculations to scale up or down. /// - [DataMember(Order = 30)] public double? GoldModifier { get; set; } = 1.0; + [DataMember(Order = 30)] public double GoldModifier { get; set; } /// /// Global modifier for Rift calculations to scale up or down. /// - [DataMember(Order = 31)] public double? RiftModifier { get; set; } = 1.0; + [DataMember(Order = 31)] public double RiftModifier { get; set; } /// /// Global modifier for BO calculations to scale up or down. /// - [DataMember(Order = 32)] public double? BoModifier { get; set; } = 1.0; + [DataMember(Order = 32)] public double BoModifier { get; set; } /// /// Global modifier for HO calculations to scale up or down. /// - [DataMember(Order = 33)] public double? HoModifier { get; set; } = 1.0; + [DataMember(Order = 33)] public double HoModifier { get; set; } /// /// Global modifier for JP calculations to scale up or down. /// - [DataMember(Order = 34)] public double? JpModifier { get; set; } = 1.0; + [DataMember(Order = 34)] public double JpModifier { get; set; } /// /// Configures the maximum amount of reward box slots. /// - [DataMember(Order = 35)] public byte? RewardBoxMax { get; set; } = 100; + [DataMember(Order = 35)] public byte RewardBoxMax { get; set; } /// /// Configures the maximum amount of quests that can be ordered at one time. /// - [DataMember(Order = 36)] public byte? QuestOrderMax { get; set; } = 20; + [DataMember(Order = 36)] public byte QuestOrderMax { get; set; } /// /// Configures if epitaph rewards are limited once per weekly reset. /// - [DataMember(Order = 37)] public bool? EnableEpitaphWeeklyRewards { get; set; } = true; + [DataMember(Order = 37)] public bool EnableEpitaphWeeklyRewards { get; set; } /// /// Enables main pawns in party to gain EXP and JP from quests @@ -286,6 +266,11 @@ public class GameLogicSetting [DataMember(Order = 200)] public string UrlCompanionImage { get; set; } public GameLogicSetting() + { + SetDefaultValues(); + } + + void SetDefaultValues() { LaternBurnTimeInSeconds = 1500; AdditionalProductionSpeedFactor = 1.0; @@ -372,6 +357,12 @@ public GameLogicSetting() UrlCompanionImage = $"{urlDomain}/"; } + [OnDeserializing] + void OnDeserializing(StreamingContext context) + { + SetDefaultValues(); + } + public GameLogicSetting(GameLogicSetting setting) { LaternBurnTimeInSeconds = setting.LaternBurnTimeInSeconds; @@ -444,44 +435,6 @@ public GameLogicSetting(GameLogicSetting setting) [OnDeserialized] void OnDeserialized(StreamingContext context) { - // Initialize reference types so tests work properly. - AdjustPartyEnemyExpTiers ??= new(); - AdjustTargetLvEnemyExpTiers ??= new(); - WeatherStatistics ??= new(); - WalletLimits ??= new(); - UrlManual ??= string.Empty; - UrlShopDetail ??= string.Empty; - UrlShopCounterA ??= string.Empty; - UrlShopAttention ??= string.Empty; - UrlShopStoneLimit ??= string.Empty; - UrlShopCounterB ??= string.Empty; - UrlChargeCallback ??= string.Empty; - UrlChargeA ??= string.Empty; - UrlSample9 ??= string.Empty; - UrlSample10 ??= string.Empty; - UrlCampaignBanner ??= string.Empty; - UrlSupportIndex ??= string.Empty; - UrlPhotoupAuthorize ??= string.Empty; - UrlApiA ??= string.Empty; - UrlApiB ??= string.Empty; - UrlIndex ??= string.Empty; - UrlCampaign ??= string.Empty; - UrlChargeB ??= string.Empty; - UrlCompanionImage ??= string.Empty; - - EnableEpitaphWeeklyRewards ??= true; - - EnemyExpModifier ??= 1; - QuestExpModifier ??= 1; - PpModifier ??= 1; - GoldModifier ??= 1; - RiftModifier ??= 1; - BoModifier ??= 1; - HoModifier ??= 1; - JpModifier ??= 1; - RewardBoxMax ??= 100; - QuestOrderMax ??= 20; - if (RookiesRingBonus < 0) { RookiesRingBonus = 1.0; diff --git a/Arrowgene.Ddon.Server/LoginServerSetting.cs b/Arrowgene.Ddon.Server/LoginServerSetting.cs index bad40b475..27fd0e632 100644 --- a/Arrowgene.Ddon.Server/LoginServerSetting.cs +++ b/Arrowgene.Ddon.Server/LoginServerSetting.cs @@ -13,6 +13,27 @@ public class LoginServerSetting [DataMember(Order = 112)] public int KickOnMultipleLoginTimer { get; set; } public LoginServerSetting() + { + SetDefaultValues(); + } + + public LoginServerSetting(LoginServerSetting setting) + { + ServerSetting = new ServerSetting(setting.ServerSetting); + AccountRequired = setting.AccountRequired; + NoOperationTimeOutTime = setting.NoOperationTimeOutTime; + KickOnMultipleLogin = setting.KickOnMultipleLogin; + KickOnMultipleLoginTries = setting.KickOnMultipleLoginTries; + KickOnMultipleLoginTimer = setting.KickOnMultipleLoginTimer; + } + + [OnDeserializing] + void OnDeserializing(StreamingContext context) + { + SetDefaultValues(); + } + + void SetDefaultValues() { ServerSetting = new ServerSetting(); ServerSetting.Id = 1; @@ -26,15 +47,5 @@ public LoginServerSetting() KickOnMultipleLoginTries = 3; KickOnMultipleLoginTimer = 5000; } - - public LoginServerSetting(LoginServerSetting setting) - { - ServerSetting = new ServerSetting(setting.ServerSetting); - AccountRequired = setting.AccountRequired; - NoOperationTimeOutTime = setting.NoOperationTimeOutTime; - KickOnMultipleLogin = setting.KickOnMultipleLogin; - KickOnMultipleLoginTries = setting.KickOnMultipleLoginTries; - KickOnMultipleLoginTimer = setting.KickOnMultipleLoginTimer; - } } }