public static void HandleAccountSetPassword(Session session, params string[] parameters) { string accountName = parameters[0].ToLower(); var account = DatabaseManager.Authentication.GetAccountByName(accountName); if (account == null) { CommandHandlerHelper.WriteOutputInfo(session, "Account " + accountName + " does not exist.", ChatMessageType.Broadcast); return; } if (parameters.Length < 1) { CommandHandlerHelper.WriteOutputInfo(session, "You must specify a password for the account.", ChatMessageType.Broadcast); return; } account.SetPassword(parameters[1]); account.SetSaltForBCrypt(); DatabaseManager.Authentication.UpdateAccount(account); CommandHandlerHelper.WriteOutputInfo(session, $"Account password for {accountName} successfully changed.", ChatMessageType.Broadcast); }
public static void HandleServerPerformance(Session session, params string[] parameters) { if (parameters != null && parameters.Length == 1) { if (parameters[0].ToLower() == "start") { ServerPerformanceMonitor.Start(); CommandHandlerHelper.WriteOutputInfo(session, "Server Performance Monitor started"); return; } if (parameters[0].ToLower() == "stop") { ServerPerformanceMonitor.Stop(); CommandHandlerHelper.WriteOutputInfo(session, "Server Performance Monitor stopped"); return; } if (parameters[0].ToLower() == "reset") { ServerPerformanceMonitor.Reset(); CommandHandlerHelper.WriteOutputInfo(session, "Server Performance Monitor reset"); return; } } if (!ServerPerformanceMonitor.IsRunning) { CommandHandlerHelper.WriteOutputInfo(session, "Server Performance Monitor not running. To start use /serverperformance start"); return; } CommandHandlerHelper.WriteOutputInfo(session, ServerPerformanceMonitor.ToString()); }
public static void HandleCharacterForcedDelete(Session session, params string[] parameters) { var characterName = string.Join(" ", parameters); var foundPlayer = PlayerManager.FindByName(characterName, out var isOnline); if (foundPlayer == null) { CommandHandlerHelper.WriteOutputInfo(session, $"There is no character named {characterName} in the database.", ChatMessageType.Broadcast); return; } if (isOnline && foundPlayer is Player player) { player.Character.DeleteTime = (ulong)Time.GetUnixTime(); player.Character.IsDeleted = true; player.CharacterChangesDetected = true; player.Session.LogOffPlayer(true); PlayerManager.HandlePlayerDelete(player.Character.Id); PlayerManager.ProcessDeletedPlayer(player.Character.Id); } else { var character = DatabaseManager.Shard.GetCharacterByName(foundPlayer.Name); character.DeleteTime = (ulong)Time.GetUnixTime(); character.IsDeleted = true; DatabaseManager.Shard.SaveCharacter(character, new ReaderWriterLockSlim(), null); PlayerManager.HandlePlayerDelete(character.Id); PlayerManager.ProcessDeletedPlayer(character.Id); } CommandHandlerHelper.WriteOutputInfo(session, $"Successfully {(isOnline ? "booted and " : "")}deleted character {foundPlayer.Name} (0x{foundPlayer.Guid}).", ChatMessageType.Broadcast); }
public static void HandleUnBanAccount(Session session, params string[] parameters) { // usage: @unban acct // This command removes the ban from the specified account.The player will then be able to log into the game. // @unban - Unbans the specified player account. var accountName = parameters[0]; var account = DatabaseManager.Authentication.GetAccountByName(accountName); if (account == null) { CommandHandlerHelper.WriteOutputInfo(session, $"Cannot unban \"{accountName}\" because that account cannot be found in database. Check spelling and try again.", ChatMessageType.Broadcast); return; } if (account.BanExpireTime == null) { CommandHandlerHelper.WriteOutputInfo(session, $"Cannon unban\"{accountName}\" because that account is not banned.", ChatMessageType.Broadcast); return; } account.UnBan(); var banText = $"UnBanned account {accountName}."; CommandHandlerHelper.WriteOutputInfo(session, banText, ChatMessageType.Broadcast); PlayerManager.BroadcastToAuditChannel(session?.Player, banText); }
public static void HandleDatabaseShardCacheNPBRT(Session session, params string[] parameters) { if (!(DatabaseManager.Shard.BaseDatabase is ShardDatabaseWithCaching shardDatabaseWithCaching)) { CommandHandlerHelper.WriteOutputInfo(session, "DatabaseManager is not using ShardDatabaseWithCaching"); return; } if (parameters == null || parameters.Length == 0) { CommandHandlerHelper.WriteOutputInfo(session, $"Shard Database, Non-Player Biota Cache - Retention Time {shardDatabaseWithCaching.NonPlayerBiotaRetentionTime.TotalMinutes:N0} m"); return; } if (!int.TryParse(parameters[0], out var value) || value < 0) { CommandHandlerHelper.WriteOutputInfo(session, "Unable to parse argument. Specify retention time in integer minutes."); return; } shardDatabaseWithCaching.NonPlayerBiotaRetentionTime = TimeSpan.FromMinutes(value); CommandHandlerHelper.WriteOutputInfo(session, $"Shard Database, Non-Player Biota Cache - Retention Time {shardDatabaseWithCaching.NonPlayerBiotaRetentionTime.TotalMinutes:N0} m"); }
public static void HandleGCStatus(Session session, params string[] parameters) { var sb = new StringBuilder(); sb.Append($"GC.GetTotalMemory: {(GC.GetTotalMemory(false) >> 20):N0} MB, GC.GetTotalAllocatedBytes: {(GC.GetTotalAllocatedBytes() >> 20):N0} MB{'\n'}"); // https://docs.microsoft.com/en-us/dotnet/api/system.gcmemoryinfo?view=net-5.0 var gcmi = GC.GetGCMemoryInfo(); sb.Append($"GCMI Index: {gcmi.Index:N0}, Generation: {gcmi.Generation}, Compacted: {gcmi.Compacted}, Concurrent: {gcmi.Concurrent}, PauseTimePercentage: {gcmi.PauseTimePercentage}{'\n'}"); for (int i = 0; i < gcmi.GenerationInfo.Length; i++) { sb.Append($"GCMI.GenerationInfo[{i}] FragmentationBeforeBytes: {(gcmi.GenerationInfo[i].FragmentationBeforeBytes >> 20):N0} MB, FragmentationAfterBytes: {(gcmi.GenerationInfo[i].FragmentationAfterBytes >> 20):N0} MB, SizeBeforeBytes: {(gcmi.GenerationInfo[i].SizeBeforeBytes >> 20):N0} MB, SizeAfterBytes: {(gcmi.GenerationInfo[i].SizeAfterBytes >> 20):N0} MB{'\n'}"); } for (int i = 0; i < gcmi.PauseDurations.Length; i++) { sb.Append($"GCMI.PauseDurations[{i}]: {gcmi.PauseDurations[i].TotalMilliseconds:N0} ms{'\n'}"); } sb.Append($"GCMI PinnedObjectsCount: {gcmi.PinnedObjectsCount}, FinalizationPendingCount: {gcmi.FinalizationPendingCount:N0}{'\n'}"); sb.Append($"GCMI FragmentedBytes: {(gcmi.FragmentedBytes >> 20):N0} MB, PromotedBytes: {(gcmi.PromotedBytes >> 20):N0} MB, HeapSizeBytes: {(gcmi.HeapSizeBytes >> 20):N0} MB, TotalCommittedBytes: {(gcmi.TotalCommittedBytes >> 20):N0} MB{'\n'}"); sb.Append($"GCMI MemoryLoadBytes: {(gcmi.MemoryLoadBytes >> 20):N0} MB, HighMemoryLoadThresholdBytes: {(gcmi.HighMemoryLoadThresholdBytes >> 20):N0} MB, TotalAvailableMemoryBytes: {(gcmi.TotalAvailableMemoryBytes >> 20):N0} MB{'\n'}"); CommandHandlerHelper.WriteOutputInfo(session, sb.ToString()); }
public static void HandleSetShutdownInterval(Session session, params string[] parameters) { if (parameters?.Length > 0) { // delay server shutdown for up to x minutes // limit to uint length 65535 string parseInt = parameters[0].Length > 5 ? parameters[0].Substring(0, 5) : parameters[0]; if (uint.TryParse(parseInt, out var newShutdownInterval)) { // newShutdownInterval is represented as a time element if (newShutdownInterval > uint.MaxValue) { newShutdownInterval = uint.MaxValue; } // set the interval ServerManager.SetShutdownInterval(Convert.ToUInt32(newShutdownInterval)); // message the admin CommandHandlerHelper.WriteOutputInfo(session, $"Shutdown Interval (seconds to shutdown server) has been set to {ServerManager.ShutdownInterval}.", ChatMessageType.Broadcast); return; } } CommandHandlerHelper.WriteOutputInfo(session, "Usage: /set-shutdown-interval <00000>", ChatMessageType.Broadcast); }
public static void HandleCharacterTokenization(Session session, params string[] parameters) { string characterName = parameters[0]; AccessLevel accessLevel = AccessLevel.Player; if (parameters.Length > 1) { if (Enum.TryParse(parameters[1], true, out accessLevel)) { if (!Enum.IsDefined(typeof(AccessLevel), accessLevel)) { accessLevel = AccessLevel.Player; } } } DatabaseManager.Shard.SetCharacterAccessLevelByName(characterName.ToLower(), accessLevel, ((uint characterId) => { if (characterId > 0) { string articleAorAN = "a"; if (accessLevel == AccessLevel.Advocate || accessLevel == AccessLevel.Admin || accessLevel == AccessLevel.Envoy) { articleAorAN = "an"; } CommandHandlerHelper.WriteOutputInfo(session, "Character " + characterName + " has been made " + articleAorAN + " " + Enum.GetName(typeof(AccessLevel), accessLevel) + ".", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, "There is no character by the name of " + characterName + " found in the database. Has it been deleted?", ChatMessageType.Broadcast); } })); }
public static void HandleSetShutdownInterval(Session session, params string[] parameters) { if (parameters?.Length > 0) { // delay server shutdown for up to x minutes // limit to uint length 65535 string parseInt = parameters[0].Length > 5 ? parameters[0].Substring(0, 5) : parameters[0]; if (uint.TryParse(parseInt, out var newShutdownInterval)) { // newShutdownInterval is represented as a time element if (newShutdownInterval > uint.MaxValue) { newShutdownInterval = uint.MaxValue; } var adminName = (session == null) ? "CONSOLE" : session.Player.Name; var msg = $"{adminName} has requested the shut down interval be changed from {ServerManager.ShutdownInterval} seconds to {newShutdownInterval} seconds."; //log.Info(msg); PlayerManager.BroadcastToAuditChannel(session?.Player, msg); // set the interval ServerManager.SetShutdownInterval(Convert.ToUInt32(newShutdownInterval)); // message the admin CommandHandlerHelper.WriteOutputInfo(session, $"Shutdown Interval (seconds to shutdown server) has been set to {ServerManager.ShutdownInterval}.", ChatMessageType.Broadcast); return; } } CommandHandlerHelper.WriteOutputInfo(session, "Usage: /set-shutdown-interval <00000>", ChatMessageType.Broadcast); }
public static void HandlePasswd(Session session, params string[] parameters) { if (session == null) { CommandHandlerHelper.WriteOutputInfo(session, "This command is run from ingame client only", ChatMessageType.Broadcast); return; } log.Debug($"{session.Player.Name} is changing their password"); var currentTime = DateTime.UtcNow; if (currentTime - session.LastPassTime < PasswdInterval) { CommandHandlerHelper.WriteOutputInfo(session, $"This command may only be run once every {PasswdInterval.TotalSeconds} seconds.", ChatMessageType.Broadcast); return; } session.LastPassTime = currentTime; if (parameters.Length <= 0) { CommandHandlerHelper.WriteOutputInfo(session, "You must specify the current password for the account.", ChatMessageType.Broadcast); return; } if (parameters.Length < 1) { CommandHandlerHelper.WriteOutputInfo(session, "You must specify a new password for the account.", ChatMessageType.Broadcast); return; } var account = DatabaseManager.Authentication.GetAccountById(session.AccountId); if (account == null) { CommandHandlerHelper.WriteOutputInfo(session, $"Account {session.Account} ({session.AccountId}) wasn't found in the database! How are you in world without a valid account?", ChatMessageType.Broadcast); return; } var oldpassword = parameters[0]; var newpassword = parameters[1]; if (account.PasswordMatches(oldpassword)) { account.SetPassword(newpassword); account.SetSaltForBCrypt(); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to change password: Password provided in first parameter does not match current account password for this account!", ChatMessageType.Broadcast); return; } DatabaseManager.Authentication.UpdateAccount(account); CommandHandlerHelper.WriteOutputInfo(session, "Account password successfully changed.", ChatMessageType.Broadcast); }
public static void HandleAccountCreate(Session session, params string[] parameters) { AccessLevel defaultAccessLevel = (AccessLevel)Common.ConfigManager.Config.Server.Accounts.DefaultAccessLevel; if (!Enum.IsDefined(typeof(AccessLevel), defaultAccessLevel)) { defaultAccessLevel = AccessLevel.Player; } var accessLevel = defaultAccessLevel; if (parameters.Length > 2) { if (Enum.TryParse(parameters[2], true, out accessLevel)) { if (!Enum.IsDefined(typeof(AccessLevel), accessLevel)) { accessLevel = defaultAccessLevel; } } } string articleAorAN = "a"; if (accessLevel == AccessLevel.Advocate || accessLevel == AccessLevel.Admin || accessLevel == AccessLevel.Envoy) { articleAorAN = "an"; } string message = ""; var accountExists = DatabaseManager.Authentication.GetAccountByName(parameters[0]); if (accountExists != null) { message = "Account already exists. Try a new name."; } else { try { var account = DatabaseManager.Authentication.CreateAccount(parameters[0].ToLower(), parameters[1], accessLevel, IPAddress.Parse("127.0.0.1")); if (DatabaseManager.AutoPromoteNextAccountToAdmin && accessLevel == AccessLevel.Admin) { DatabaseManager.AutoPromoteNextAccountToAdmin = false; } message = ("Account successfully created for " + account.AccountName + " (" + account.AccountId + ") with access rights as " + articleAorAN + " " + Enum.GetName(typeof(AccessLevel), accessLevel) + "."); } catch { message = "Account already exists. Try a new name."; } } CommandHandlerHelper.WriteOutputInfo(session, message, ChatMessageType.WorldBroadcast); }
public static void HandleDatabaseQueueInfo(Session session, params string[] parameters) { CommandHandlerHelper.WriteOutputInfo(session, $"Current database queue count: {DatabaseManager.Shard.QueueCount}"); DatabaseManager.Shard.GetCurrentQueueWaitTime(result => { CommandHandlerHelper.WriteOutputInfo(session, $"Current database queue wait time: {result.TotalMilliseconds:N0} ms"); }); }
public static void HandleLandblockPerformance(Session session, params string[] parameters) { var sb = new StringBuilder(); var loadedLandblocks = LandblockManager.GetLoadedLandblocks(); // Filter out landblocks that haven't recorded at least 1000 events var sortedByAverage = loadedLandblocks.Where(r => r.Monitor1h.TotalEvents >= 1000).OrderByDescending(r => r.Monitor1h.AverageEventDuration).Take(10); sb.Append($"Most Busy Landblock - By Average{'\n'}"); sb.Append($"~1h Hits Avg Long Last Tot - Location Players Creatures{'\n'}"); foreach (var entry in sortedByAverage) { int players = 0, creatures = 0; foreach (var worldObject in entry.GetAllWorldObjectsForDiagnostics()) { if (worldObject is Player) { players++; } else if (worldObject is Creature) { creatures++; } } sb.Append($"{entry.Monitor1h.TotalEvents.ToString().PadLeft(7)} {entry.Monitor1h.AverageEventDuration:N4} {entry.Monitor1h.LongestEvent:N3} {entry.Monitor1h.LastEvent:N3} {((int)entry.Monitor1h.TotalSeconds).ToString().PadLeft(4)} - " + $"0x{entry.Id.Raw:X8} {players.ToString().PadLeft(7)} {creatures.ToString().PadLeft(9)}{'\n'}"); } var sortedByLong = loadedLandblocks.Where(r => r.Monitor1h.TotalEvents >= 1000).OrderByDescending(r => r.Monitor1h.LongestEvent).Take(10); sb.Append($"Most Busy Landblock - By Longest{'\n'}"); sb.Append($"~1h Hits Avg Long Last Tot - Location Players Creatures{'\n'}"); foreach (var entry in sortedByLong) { int players = 0, creatures = 0; foreach (var worldObject in entry.GetAllWorldObjectsForDiagnostics()) { if (worldObject is Player) { players++; } else if (worldObject is Creature) { creatures++; } } sb.Append($"{entry.Monitor1h.TotalEvents.ToString().PadLeft(7)} {entry.Monitor1h.AverageEventDuration:N4} {entry.Monitor1h.LongestEvent:N3} {entry.Monitor1h.LastEvent:N3} {((int)entry.Monitor1h.TotalSeconds).ToString().PadLeft(4)} - " + $"0x{entry.Id.Raw:X8} {players.ToString().PadLeft(7)} {creatures.ToString().PadLeft(9)}{'\n'}"); } CommandHandlerHelper.WriteOutputInfo(session, sb.ToString()); }
public static void HandleAccountUpdateAccessLevel(Session session, params string[] parameters) { string accountName = parameters[0].ToLower(); var accountId = DatabaseManager.Authentication.GetAccountIdByName(accountName); if (accountId == 0) { CommandHandlerHelper.WriteOutputInfo(session, "Account " + accountName + " does not exist.", ChatMessageType.Broadcast); return; } AccessLevel accessLevel = AccessLevel.Player; if (parameters.Length > 1) { if (Enum.TryParse(parameters[1], true, out accessLevel)) { if (!Enum.IsDefined(typeof(AccessLevel), accessLevel)) { accessLevel = AccessLevel.Player; } } } string articleAorAN = "a"; if (accessLevel == AccessLevel.Advocate || accessLevel == AccessLevel.Admin || accessLevel == AccessLevel.Envoy) { articleAorAN = "an"; } if (accountId == 0) { CommandHandlerHelper.WriteOutputInfo(session, "Account " + accountName + " does not exist.", ChatMessageType.Broadcast); return; } DatabaseManager.Authentication.UpdateAccountAccessLevel(accountId, accessLevel); if (DatabaseManager.AutoPromoteNextAccountToAdmin && accessLevel == AccessLevel.Admin) { DatabaseManager.AutoPromoteNextAccountToAdmin = false; } CommandHandlerHelper.WriteOutputInfo(session, "Account " + accountName + " updated with access rights set as " + articleAorAN + " " + Enum.GetName(typeof(AccessLevel), accessLevel) + ".", ChatMessageType.Broadcast); }
public static void HandleBanlist(Session session, params string[] parameters) { // @banlist - Lists all banned accounts on this world. var bannedAccounts = DatabaseManager.Authentication.GetListofBannedAccounts(); if (bannedAccounts.Count != 0) { var msg = "The following accounts are banned:\n"; msg += "-------------------\n"; foreach (var account in bannedAccounts) msg += account + "\n"; CommandHandlerHelper.WriteOutputInfo(session, msg, ChatMessageType.Broadcast); } else CommandHandlerHelper.WriteOutputInfo(session, $"There are no accounts currently banned.", ChatMessageType.Broadcast); }
public static void HandleModifyRiptideBoolProperty(Session session, params string[] paramters) { try { var boolVal = bool.Parse(paramters[1]); if (CustomPropertiesManager.ModifyBool(paramters[0], boolVal)) { CommandHandlerHelper.WriteOutputInfo(session, "Bool property successfully updated!"); } else { CommandHandlerHelper.WriteOutputInfo(session, "Unknown bool property was not updated. Type showprops for a list of properties."); } } catch (Exception) { CommandHandlerHelper.WriteOutputInfo(session, "Please input a valid bool", ChatMessageType.Help); } }
public static void HandleModifyRiptideFloatProperty(Session session, params string[] paramters) { try { var doubleVal = double.Parse(paramters[1]); if (CustomPropertiesManager.ModifyDouble(paramters[0], doubleVal)) { CommandHandlerHelper.WriteOutputInfo(session, "Float property successfully updated!"); } else { CommandHandlerHelper.WriteOutputInfo(session, "Unknown float property was not updated. Type rt-showprops for a list of properties."); } } catch (Exception) { CommandHandlerHelper.WriteOutputInfo(session, "Please input a valid float", ChatMessageType.Help); } }
public static void HandlePop(Session session, params string[] parameters) { CommandHandlerHelper.WriteOutputInfo(session, $"Current world population: {PlayerManager.GetOnlineCount():N0}", ChatMessageType.Broadcast); }
public static void HandleHelp(Session session, params string[] parameters) { var open = false; var close = false; var bootPlayers = false; var message = $"World is currently {WorldManager.WorldStatus.ToString()}\nPlease specify state to change\n@world [open | close] <boot>\nIf closing world, using @world close boot will force players to logoff immediately"; if (parameters.Length >= 1) { switch (parameters[0].ToLower()) { case "open": if (WorldManager.WorldStatus != WorldManager.WorldStatusState.Open) { message = "Opening world to players..."; open = true; } else { message = "World is already open."; } break; case "close": if (WorldManager.WorldStatus != WorldManager.WorldStatusState.Closed) { if (parameters.Length > 1) { if (parameters[1].ToLower() == "boot") { bootPlayers = true; } } message = "Closing world"; if (bootPlayers) { message += ", and booting all online players."; } else { message += "..."; } close = true; } else { message = "World is already closed."; } break; } } CommandHandlerHelper.WriteOutputInfo(session, message, ChatMessageType.WorldBroadcast); if (open) { WorldManager.Open(session == null ? null : session.Player); } else if (close) { WorldManager.Close(session == null ? null : session.Player, bootPlayers); } }
public static void HandleFixBiotaEmoteDelay(Session session, params string[] parameters) { if (parameters.Length == 0) { CommandHandlerHelper.WriteOutputInfo(session, "This command is intended to be run while the world is in offline mode, or there are 0 players connected."); CommandHandlerHelper.WriteOutputInfo(session, "To run this fix, type fix-biota-emote-delay fix"); return; } var fix = parameters[0].Equals("fix", StringComparison.OrdinalIgnoreCase); CommandHandlerHelper.WriteOutputInfo(session, "Building weenie emote cache"); var weenieEmoteCache = BuildWeenieEmoteCache(); CommandHandlerHelper.WriteOutputInfo(session, $"Found {weenieEmoteCache.Count:N0} weenie templates w/ emote actions with delay 0"); using (var ctx = new ShardDbContext()) { CommandHandlerHelper.WriteOutputInfo(session, $"Finding biotas for these wcids"); var biotas = ctx.Biota.Where(i => weenieEmoteCache.ContainsKey(i.WeenieClassId)).ToList(); var distinct = biotas.Select(i => i.WeenieClassId).Distinct(); var counts = new Dictionary <uint, uint>(); foreach (var biota in biotas) { if (!counts.ContainsKey(biota.WeenieClassId)) { counts[biota.WeenieClassId] = 1; } else { counts[biota.WeenieClassId]++; } } CommandHandlerHelper.WriteOutputInfo(session, $"Found {biotas.Count} biotas matching {distinct.Count()} distinct wcids"); foreach (var kvp in counts.OrderBy(i => i.Key)) { CommandHandlerHelper.WriteOutputInfo(session, $"{kvp.Key} - {(WeenieClassName)kvp.Key} ({kvp.Value})"); } if (!fix) { CommandHandlerHelper.WriteOutputInfo(session, $"Dry run completed"); return; } var totalUpdated = 0; foreach (var biota in biotas) { bool updated = false; var query = from emote in ctx.BiotaPropertiesEmote join action in ctx.BiotaPropertiesEmoteAction on emote.Id equals action.EmoteId where emote.ObjectId == biota.Id && action.Delay == 1.0f select new { Emote = emote, Action = action }; var emoteActions = query.ToList(); foreach (var emoteAction in emoteActions) { var emote = emoteAction.Emote; var action = emoteAction.Action; // ensure this delay 1 should be delay 0 var hash = CalculateEmoteHash(emote); var weenieEmotes = weenieEmoteCache[biota.WeenieClassId]; if (!weenieEmotes.TryGetValue(hash, out var list)) { //CommandHandlerHelper.WriteOutputInfo(session, $"Skipping emote for {biota.WeenieClassId} not found in hash list"); continue; } if (!list.Contains(action.Order)) { //CommandHandlerHelper.WriteOutputInfo(session, $"Skipping emote for {biota.WeenieClassId} not found in action list"); continue; } // confirmed match, update delay 1 -> 0 action.Delay = 0.0f; updated = true; } if (updated) { CommandHandlerHelper.WriteOutputInfo(session, $"Fixed shard object {biota.Id:X8} of type {biota.WeenieClassId} - {(WeenieClassName)biota.WeenieClassId}"); totalUpdated++; } } ctx.SaveChanges(); CommandHandlerHelper.WriteOutputInfo(session, $"Completed successfully, fixed {totalUpdated} shard items"); } }
public static void HandleBanAccount(Session session, params string[] parameters) { // usage: @ban < acct > < days > < hours > < minutes > // This command bans the specified player account for the specified time.This player will not be able to enter the game with any character until the time expires. // @ban - Bans the specified player account. var accountName = parameters[0]; var banDays = parameters[1]; var banHours = parameters[2]; var banMinutes = parameters[3]; var banReason = string.Empty; if (parameters.Length > 4) { var parametersAfterBanParams = ""; for (var i = 4; i < parameters.Length; i++) { parametersAfterBanParams += parameters[i] + " "; } parametersAfterBanParams = parametersAfterBanParams.Trim(); banReason = parametersAfterBanParams; } var account = DatabaseManager.Authentication.GetAccountByName(accountName); if (account == null) { CommandHandlerHelper.WriteOutputInfo(session, $"Cannot ban \"{accountName}\" because that account cannot be found in database. Check syntax/spelling and try again.", ChatMessageType.Broadcast); return; } if (!double.TryParse(banDays, out var days) || days < 0) { CommandHandlerHelper.WriteOutputInfo(session, $"Days must not be less than 0.", ChatMessageType.Broadcast); return; } if (!double.TryParse(banHours, out var hours) || hours < 0) { CommandHandlerHelper.WriteOutputInfo(session, $"Hours must not be less than 0.", ChatMessageType.Broadcast); return; } if (!double.TryParse(banMinutes, out var minutes) || minutes < 0) { CommandHandlerHelper.WriteOutputInfo(session, $"Minutes must not be less than 0.", ChatMessageType.Broadcast); return; } var bannedOn = DateTime.UtcNow; var banExpires = DateTime.UtcNow.AddDays(days).AddHours(hours).AddMinutes(minutes); var bannedBy = 0u; if (session != null) { bannedBy = session.AccountId; } account.BannedTime = bannedOn; account.BanExpireTime = banExpires; account.BannedByAccountId = bannedBy; if (!string.IsNullOrWhiteSpace(banReason)) { account.BanReason = banReason; } DatabaseManager.Authentication.UpdateAccount(account); // Boot the player if (NetworkManager.Find(accountName) != null) { var bootArgs = new List <string> { "account" }; if (!string.IsNullOrWhiteSpace(banReason)) { bootArgs.Add($"{accountName},"); bootArgs.Add(banReason); } else { bootArgs.Add(accountName); } HandleBoot(session, bootArgs.ToArray()); } var banText = $"Banned account {accountName} for {days} days, {hours} hours and {minutes} minutes.{(!string.IsNullOrWhiteSpace(banReason) ? $" Reason: {banReason}" : "")}"; CommandHandlerHelper.WriteOutputInfo(session, banText, ChatMessageType.Broadcast); PlayerManager.BroadcastToAuditChannel(session?.Player, banText); }
public static void HandleCloak(Session session, params string[] parameters) { // Please specify if you want cloaking on or off.usage: @cloak < on / off / player / creature > // This command sets your current cloaking state. // < on > You will be completely invisible to players. // < off > You will show up as a normal. // < player > You will appear as a player. (No + and a white radar dot.) // < creature > You will appear as a creature. (No + and an orange radar dot.) // @cloak - Sets your cloaking state. // TODO: investigate translucensy/visbility of other cloaked admins. switch (parameters?[0].ToLower()) { case "off": if (session.Player.CloakStatus == CloakStatus.Off) { return; } session.Player.DeCloak(); session.Player.SetProperty(PropertyInt.CloakStatus, (int)CloakStatus.Off); CommandHandlerHelper.WriteOutputInfo(session, $"You are no longer cloaked, can no longer pass through doors and will appear as an admin.", ChatMessageType.Broadcast); break; case "on": if (session.Player.CloakStatus == CloakStatus.On) { return; } session.Player.HandleCloak(); session.Player.SetProperty(PropertyInt.CloakStatus, (int)CloakStatus.On); CommandHandlerHelper.WriteOutputInfo(session, $"You are now cloaked.\nYou are now ethereal and can pass through doors.", ChatMessageType.Broadcast); break; case "player": if (session.AccessLevel > AccessLevel.Envoy) { if (session.Player.CloakStatus == CloakStatus.Player) { return; } session.Player.SetProperty(PropertyInt.CloakStatus, (int)CloakStatus.Player); session.Player.DeCloak(); CommandHandlerHelper.WriteOutputInfo(session, $"You will now appear as a player.", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"You do not have permission to do that state", ChatMessageType.Broadcast); } break; case "creature": if (session.AccessLevel > AccessLevel.Envoy) { if (session.Player.CloakStatus == CloakStatus.Creature) { return; } session.Player.SetProperty(PropertyInt.CloakStatus, (int)CloakStatus.Creature); session.Player.Attackable = true; session.Player.DeCloak(); CommandHandlerHelper.WriteOutputInfo(session, $"You will now appear as a creature.\nUse @pk free to be allowed to attack all living things.", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"You do not have permission to do that state", ChatMessageType.Broadcast); } break; default: session.Network.EnqueueSend(new GameMessageSystemChat("Please specify if you want cloaking on or off.", ChatMessageType.Broadcast)); break; } }
public static void HandleFetchRiptideFloatProperty(Session session, params string[] paramters) { var floatVal = CustomPropertiesManager.GetDouble(paramters[0]); CommandHandlerHelper.WriteOutputInfo(session, $"{paramters[0]} - {floatVal.Description ?? "No Description"}: {floatVal.Item}"); }
public static void HandleCloak(Session session, params string[] parameters) { // Please specify if you want cloaking on or off.usage: @cloak < on / off / player / creature > // This command sets your current cloaking state. // < on > You will be completely invisible to players. // < off > You will show up as a normal. // < player > You will appear as a player. (No + and a white radar dot.) // < creature > You will appear as a creature. (No + and an orange radar dot.) // @cloak - Sets your cloaking state. // TODO: investigate translucensy/visbility of other cloaked admins. switch (parameters?[0].ToLower()) { case "off": session.Player.Cloaked = false; session.Player.Ethereal = false; // session.Player.IgnoreCollisions = false; session.Player.NoDraw = false; // session.Player.ReportCollisions = true; session.Player.EnqueueBroadcastPhysicsState(); session.Player.Translucency = null; session.Player.Visibility = false; if (session.Player.CloakStatus > CloakStatus.On) { CommandHandlerHelper.WriteOutputInfo(session, $"You will now appear as an admin. You must relog for this to take effect", ChatMessageType.Broadcast); } session.Player.SetProperty(ACE.Entity.Enum.Properties.PropertyInt.CloakStatus, (int)CloakStatus.Off); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageRemoveObject(session.Player)); //session.Player.CurrentLandblock?.RemoveWorldObject(session.Player.Guid, false); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageCreateObject(session.Player)); //session.Player.CurrentLandblock?.AddWorldObject(session.Player); break; case "on": session.Player.Cloaked = true; session.Player.Ethereal = true; // session.Player.IgnoreCollisions = true; session.Player.NoDraw = true; // session.Player.ReportCollisions = false; session.Player.EnqueueBroadcastPhysicsState(); session.Player.Visibility = true; session.Player.Translucency = 0.5f; if (session.Player.CloakStatus > CloakStatus.On) { CommandHandlerHelper.WriteOutputInfo(session, $"You will now appear as an admin. You must relog for this to take effect", ChatMessageType.Broadcast); } session.Player.SetProperty(ACE.Entity.Enum.Properties.PropertyInt.CloakStatus, (int)CloakStatus.On); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageRemoveObject(session.Player)); //session.Player.CurrentLandblock?.RemoveWorldObject(session.Player.Guid, false); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageCreateObject(session.Player)); //session.Network.EnqueueSend(new GameMessageCreateObject(session.Player)); //session.Player.CurrentLandblock?.AddWorldObject(session.Player); break; case "player": if (session.AccessLevel > AccessLevel.Envoy) { session.Player.Cloaked = false; session.Player.Ethereal = false; // session.Player.IgnoreCollisions = false; session.Player.NoDraw = false; // session.Player.ReportCollisions = true; session.Player.EnqueueBroadcastPhysicsState(); session.Player.Translucency = null; session.Player.Visibility = false; session.Player.SetProperty(ACE.Entity.Enum.Properties.PropertyInt.CloakStatus, (int)CloakStatus.Player); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageRemoveObject(session.Player)); //session.Player.CurrentLandblock?.RemoveWorldObject(session.Player.Guid, false); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageCreateObject(session.Player)); //session.Player.CurrentLandblock?.AddWorldObject(session.Player); CommandHandlerHelper.WriteOutputInfo(session, $"You will now appear as a player. You must relog for this to take effect", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"You do not have permission to do that state", ChatMessageType.Broadcast); } break; case "creature": if (session.AccessLevel > AccessLevel.Envoy) { session.Player.Cloaked = false; session.Player.Ethereal = false; // session.Player.IgnoreCollisions = false; session.Player.NoDraw = false; // session.Player.ReportCollisions = true; session.Player.EnqueueBroadcastPhysicsState(); session.Player.Translucency = null; session.Player.Visibility = false; session.Player.SetProperty(ACE.Entity.Enum.Properties.PropertyInt.CloakStatus, (int)CloakStatus.Creature); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageRemoveObject(session.Player)); //session.Player.CurrentLandblock?.RemoveWorldObject(session.Player.Guid, false); //session.Player.CurrentLandblock?.EnqueueBroadcast(session.Player.Location, new GameMessageCreateObject(session.Player)); //session.Player.CurrentLandblock?.AddWorldObject(session.Player); CommandHandlerHelper.WriteOutputInfo(session, $"You will now appear as a creature. You must relog for this to take effect\nIn order to appear correctly, you must also set '@attackable on'\nUse @pk free to be allowed to attack all living things.", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"You do not have permission to do that state", ChatMessageType.Broadcast); } break; default: session.Network.EnqueueSend(new GameMessageSystemChat("Please specify if you want cloaking on or off.", ChatMessageType.Broadcast)); break; } }
public static void HandleRiptideShowProperties(Session session, params string[] paramters) { CommandHandlerHelper.WriteOutputInfo(session, CustomPropertiesManager.ListProperties()); }
public static void HandleLandblockPerformance(Session session, params string[] parameters) { var sb = new StringBuilder(); var loadedLandblocks = LandblockManager.GetLoadedLandblocks(); // Filter out landblocks that haven't recorded a certain amount of events var sortedBy5mAverage = loadedLandblocks.Where(r => r.Monitor5m.EventHistory.TotalEvents >= 10).OrderByDescending(r => r.Monitor5m.EventHistory.AverageEventDuration).Take(10).ToList(); var sortedBy1hrAverage = loadedLandblocks.Where(r => r.Monitor1h.EventHistory.TotalEvents >= 1000).OrderByDescending(r => r.Monitor1h.EventHistory.AverageEventDuration).Take(10).ToList(); var combinedByAverage = sortedBy5mAverage.Concat(sortedBy1hrAverage).Distinct().OrderByDescending(r => Math.Max(r.Monitor5m.EventHistory.AverageEventDuration, r.Monitor1h.EventHistory.AverageEventDuration)).Take(10); sb.Append($"Most Busy Landblock - By Average{'\n'}"); sb.Append($"~5m Hits Avg Long Last - ~1h Hits Avg Long Last - Location Players Creatures{'\n'}"); foreach (var entry in combinedByAverage) { int players = 0, creatures = 0; foreach (var worldObject in entry.GetAllWorldObjectsForDiagnostics()) { if (worldObject is Player) { players++; } else if (worldObject is Creature) { creatures++; } } sb.Append($"{entry.Monitor5m.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor5m.EventHistory.AverageEventDuration:N4} {entry.Monitor5m.EventHistory.LongestEvent:N3} {entry.Monitor5m.EventHistory.LastEvent:N3} - " + $"{entry.Monitor1h.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor1h.EventHistory.AverageEventDuration:N4} {entry.Monitor1h.EventHistory.LongestEvent:N3} {entry.Monitor1h.EventHistory.LastEvent:N3} - " + $"0x{entry.Id.Raw:X8} {players.ToString().PadLeft(7)} {creatures.ToString().PadLeft(9)}{'\n'}"); } var sortedBy5mLong = loadedLandblocks.OrderByDescending(r => r.Monitor5m.EventHistory.LongestEvent).Take(10); var sortedBy1hrLong = loadedLandblocks.OrderByDescending(r => r.Monitor1h.EventHistory.LongestEvent).Take(10); var combinedByLong = sortedBy5mLong.Concat(sortedBy1hrLong).Distinct().OrderByDescending(r => Math.Max(r.Monitor5m.EventHistory.LongestEvent, r.Monitor1h.EventHistory.LongestEvent)).Take(10); sb.Append($"Most Busy Landblock - By Longest{'\n'}"); sb.Append($"~5m Hits Avg Long Last - ~1h Hits Avg Long Last - Location Players Creatures{'\n'}"); foreach (var entry in combinedByLong) { int players = 0, creatures = 0; foreach (var worldObject in entry.GetAllWorldObjectsForDiagnostics()) { if (worldObject is Player) { players++; } else if (worldObject is Creature) { creatures++; } } sb.Append($"{entry.Monitor5m.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor5m.EventHistory.AverageEventDuration:N4} {entry.Monitor5m.EventHistory.LongestEvent:N3} {entry.Monitor5m.EventHistory.LastEvent:N3} - " + $"{entry.Monitor1h.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor1h.EventHistory.AverageEventDuration:N4} {entry.Monitor1h.EventHistory.LongestEvent:N3} {entry.Monitor1h.EventHistory.LastEvent:N3} - " + $"0x{entry.Id.Raw:X8} {players.ToString().PadLeft(7)} {creatures.ToString().PadLeft(9)}{'\n'}"); } CommandHandlerHelper.WriteOutputInfo(session, sb.ToString()); }
public static void HandleBoot(Session session, params string[] parameters) { // usage: @boot { account,char, iid} who // This command boots the specified character out of the game.You can specify who to boot by account, character name, or player instance id. 'who' is the account / character / instance id to actually boot. // @boot - Boots the character out of the game. var whomToBoot = parameters[1]; string specifiedReason = null; if (parameters.Length > 1) { var parametersAfterBootType = ""; for (var i = 1; i < parameters.Length; i++) { parametersAfterBootType += parameters[i] + " "; } parametersAfterBootType = parametersAfterBootType.Trim(); var completeBootNamePlusCommaSeperatedReason = parametersAfterBootType.Split(","); whomToBoot = completeBootNamePlusCommaSeperatedReason[0].Trim(); if (completeBootNamePlusCommaSeperatedReason.Length > 1) { specifiedReason = parametersAfterBootType.Replace($"{whomToBoot},", "").Trim(); } } string whatToBoot = null; Session sessionToBoot = null; switch (parameters[0].ToLower()) { case "char": whatToBoot = "character"; sessionToBoot = PlayerManager.GetOnlinePlayer(whomToBoot)?.Session; break; case "account": whatToBoot = "account"; sessionToBoot = NetworkManager.Find(whomToBoot); break; case "iid": whatToBoot = "instance id"; if (!whomToBoot.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { CommandHandlerHelper.WriteOutputInfo(session, $"That is not a valid Instance ID (IID). IIDs must be between 0x{ObjectGuid.PlayerMin:X8} and 0x{ObjectGuid.PlayerMax:X8}", ChatMessageType.Broadcast); return; } if (uint.TryParse(whomToBoot.Substring(2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out var iid)) { sessionToBoot = PlayerManager.GetOnlinePlayer(iid)?.Session; } else { CommandHandlerHelper.WriteOutputInfo(session, $"That is not a valid Instance ID (IID). IIDs must be between 0x{ObjectGuid.PlayerMin:X8} and 0x{ObjectGuid.PlayerMax:X8}", ChatMessageType.Broadcast); return; } break; default: CommandHandlerHelper.WriteOutputInfo(session, "You must specify what you are booting with char, account, or iid as the first parameter.", ChatMessageType.Broadcast); return; } if (sessionToBoot == null) { CommandHandlerHelper.WriteOutputInfo(session, $"Cannot boot \"{whomToBoot}\" because that {whatToBoot} is not currently online or cannot be found. Check syntax/spelling and try again.", ChatMessageType.Broadcast); return; } // Boot the player var bootText = $"Booting {whatToBoot} {whomToBoot}.{(specifiedReason != null ? $" Reason: {specifiedReason}" : "")}"; CommandHandlerHelper.WriteOutputInfo(session, bootText, ChatMessageType.Broadcast); sessionToBoot.Terminate(SessionTerminationReason.AccountBooted, new GameMessageBootAccount($"{(specifiedReason != null ? $" - {specifiedReason}" : null)}"), null, specifiedReason); //CommandHandlerHelper.WriteOutputInfo(session, $"...Result: Success!", ChatMessageType.Broadcast); PlayerManager.BroadcastToAuditChannel(session?.Player, bootText); }
public static void HandleServerStatus(Session session, params string[] parameters) { // This is formatted very similarly to GDL. var sb = new StringBuilder(); var proc = Process.GetCurrentProcess(); sb.Append($"Server Status:{'\n'}"); sb.Append($"Host Info: {Environment.OSVersion}, vCPU: {Environment.ProcessorCount}{'\n'}"); var runTime = DateTime.Now - proc.StartTime; sb.Append($"Server Runtime: {(int)runTime.TotalHours}h {runTime.Minutes}m {runTime.Seconds}s{'\n'}"); sb.Append($"Total CPU Time: {(int)proc.TotalProcessorTime.TotalHours}h {proc.TotalProcessorTime.Minutes}m {proc.TotalProcessorTime.Seconds}s, Threads: {proc.Threads.Count}{'\n'}"); // todo, add actual system memory used/avail sb.Append($"{(proc.PrivateMemorySize64 >> 20):N0} MB used{'\n'}"); // sb.Append($"{(proc.PrivateMemorySize64 >> 20)} MB used, xxxx / yyyy MB physical mem free.{'\n'}"); sb.Append($"{NetworkManager.GetSessionCount():N0} connections, {NetworkManager.GetUniqueSessionEndpointCount():N0} unique connections, {PlayerManager.GetAllOnline().Count:N0} players online{'\n'}"); sb.Append($"Total Accounts Created: {DatabaseManager.Authentication.GetAccountCount():N0}, Total Characters Created: {(PlayerManager.GetAllOffline().Count + PlayerManager.GetAllOnline().Count):N0}{'\n'}"); // 330 active objects, 1931 total objects(16777216 buckets.) // todo, expand this var loadedLandblocks = LandblockManager.GetLoadedLandblocks(); int dormantLandblocks = 0, activeDungeonLandblocks = 0, dormantDungeonLandblocks = 0; int players = 0, creatures = 0, missiles = 0, other = 0, total = 0; foreach (var landblock in loadedLandblocks) { if (landblock.IsDormant) { dormantLandblocks++; } if (landblock.IsDungeon) { if (landblock.IsDormant) { dormantDungeonLandblocks++; } else { activeDungeonLandblocks++; } } foreach (var worldObject in landblock.GetAllWorldObjectsForDiagnostics()) { if (worldObject is Player) { players++; } else if (worldObject is Creature) { creatures++; } else if (worldObject.Missile ?? false) { missiles++; } else { other++; } total++; } } sb.Append($"Landblocks: {(loadedLandblocks.Count - dormantLandblocks):N0} active ({activeDungeonLandblocks:N0} dungeons), {dormantLandblocks:N0} dormant ({dormantDungeonLandblocks:N0} dungeons) - Players: {players:N0}, Creatures: {creatures:N0}, Missiles: {missiles:N0}, Other: {other:N0}, Total: {total:N0}.{'\n'}"); // 11 total blocks loaded. 11 active. 0 pending dormancy. 0 dormant. 314 unloaded. // 11 total blocks loaded. 11 active. 0 pending dormancy. 0 dormant. 314 unloaded. if (ServerPerformanceMonitor.IsRunning) { sb.Append($"Server Performance Monitor - UpdateGameWorld ~5m {ServerPerformanceMonitor.GetEventHistory5m(ServerPerformanceMonitor.MonitorType.UpdateGameWorld_Entire).AverageEventDuration:N3}, ~1h {ServerPerformanceMonitor.GetEventHistory1h(ServerPerformanceMonitor.MonitorType.UpdateGameWorld_Entire).AverageEventDuration:N3} s{'\n'}"); } else { sb.Append($"Server Performance Monitor - Not running. To start use /serverperformance start{'\n'}"); } sb.Append($"Physics Cache Counts - BSPCache: {BSPCache.Count:N0}, GfxObjCache: {GfxObjCache.Count:N0}, PolygonCache: {PolygonCache.Count:N0}, VertexCache: {VertexCache.Count:N0}{'\n'}"); sb.Append($"Total Server Objects: {ServerObjectManager.ServerObjects.Count:N0}{'\n'}"); sb.Append($"World DB Cache Counts - Weenies: {DatabaseManager.World.GetWeenieCacheCount():N0}, LandblockInstances: {DatabaseManager.World.GetLandblockInstancesCacheCount():N0}, PointsOfInterest: {DatabaseManager.World.GetPointsOfInterestCacheCount():N0}, Cookbooks: {DatabaseManager.World.GetCookbookCacheCount():N0}, Spells: {DatabaseManager.World.GetSpellCacheCount():N0}, Encounters: {DatabaseManager.World.GetEncounterCacheCount():N0}, Events: {DatabaseManager.World.GetEventsCacheCount():N0}{'\n'}"); sb.Append($"Shard DB Counts - Biotas: {DatabaseManager.Shard.GetBiotaCount():N0}{'\n'}"); sb.Append(GuidManager.GetDynamicGuidDebugInfo() + '\n'); sb.Append($"Portal.dat has {DatManager.PortalDat.FileCache.Count:N0} files cached of {DatManager.PortalDat.AllFiles.Count:N0} total{'\n'}"); sb.Append($"Cell.dat has {DatManager.CellDat.FileCache.Count:N0} files cached of {DatManager.CellDat.AllFiles.Count:N0} total{'\n'}"); CommandHandlerHelper.WriteOutputInfo(session, $"{sb}"); }
public static void HandleCharacterForcedDelete(Session session, params string[] parameters) { var characterName = string.Join(" ", parameters); var foundPlayer = PlayerManager.FindByName(characterName, out var isOnline); if (foundPlayer == null) { CommandHandlerHelper.WriteOutputInfo(session, $"There is no character named {characterName} in the database.", ChatMessageType.Broadcast); return; } if (isOnline && foundPlayer is Player player) { player.Character.DeleteTime = (ulong)Time.GetUnixTime(); player.Character.IsDeleted = true; player.CharacterChangesDetected = true; player.Session.LogOffPlayer(true); PlayerManager.HandlePlayerDelete(player.Character.Id); var success = PlayerManager.ProcessDeletedPlayer(player.Character.Id); if (success) { CommandHandlerHelper.WriteOutputInfo(session, $"Successfully {(isOnline ? "booted and " : "")}deleted character {foundPlayer.Name} (0x{foundPlayer.Guid}).", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to PlayerManager failure.", ChatMessageType.Broadcast); } } else { var existingCharId = foundPlayer.Guid.Full; //DatabaseManager.Shard.BaseDatabase.GetCharacterStubByName(foundPlayer.Name).Id; DatabaseManager.Shard.GetCharacter(existingCharId, character => { if (character != null) { character.DeleteTime = (ulong)Time.GetUnixTime(); character.IsDeleted = true; DatabaseManager.Shard.SaveCharacter(character, new ReaderWriterLockSlim(), result => { if (result) { var deleteOfflineChain = new ActionChain(); deleteOfflineChain.AddAction(WorldManager.ActionQueue, () => PlayerManager.HandlePlayerDelete(character.Id)); deleteOfflineChain.AddDelayForOneTick(); deleteOfflineChain.AddAction(WorldManager.ActionQueue, () => { var success = PlayerManager.ProcessDeletedPlayer(character.Id); if (success) { CommandHandlerHelper.WriteOutputInfo(session, $"Successfully {(isOnline ? "booted and " : "")}deleted character {foundPlayer.Name} (0x{foundPlayer.Guid}).", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to PlayerManager failure.", ChatMessageType.Broadcast); } }); deleteOfflineChain.EnqueueChain(); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to shard database SaveCharacter failure.", ChatMessageType.Broadcast); } }); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to shard database GetCharacter failure.", ChatMessageType.Broadcast); } }); } }