public static void LogBroadcastChat(Channel channel, WorldObject sender, string message) { switch (channel) { case Channel.Abuse: if (!PropertyManager.GetBool("chat_log_abuse").Item) { return; } break; case Channel.Admin: if (!PropertyManager.GetBool("chat_log_admin").Item) { return; } break; case Channel.AllBroadcast: // using this to sub in for a WorldBroadcast channel which isn't technically a channel if (!PropertyManager.GetBool("chat_log_global").Item) { return; } break; case Channel.Audit: if (!PropertyManager.GetBool("chat_log_audit").Item) { return; } break; case Channel.Advocate1: case Channel.Advocate2: case Channel.Advocate3: if (!PropertyManager.GetBool("chat_log_advocate").Item) { return; } break; case Channel.Debug: if (!PropertyManager.GetBool("chat_log_debug").Item) { return; } break; case Channel.Fellow: case Channel.FellowBroadcast: if (!PropertyManager.GetBool("chat_log_fellow").Item) { return; } break; case Channel.Help: if (!PropertyManager.GetBool("chat_log_help").Item) { return; } break; case Channel.Olthoi: if (!PropertyManager.GetBool("chat_log_olthoi").Item) { return; } break; case Channel.QA1: case Channel.QA2: if (!PropertyManager.GetBool("chat_log_qa").Item) { return; } break; case Channel.Sentinel: if (!PropertyManager.GetBool("chat_log_sentinel").Item) { return; } break; case Channel.SocietyCelHanBroadcast: case Channel.SocietyEldWebBroadcast: case Channel.SocietyRadBloBroadcast: if (!PropertyManager.GetBool("chat_log_society").Item) { return; } break; case Channel.AllegianceBroadcast: case Channel.CoVassals: case Channel.Monarch: case Channel.Patron: case Channel.Vassals: if (!PropertyManager.GetBool("chat_log_allegiance").Item) { return; } break; case Channel.AlArqas: case Channel.Holtburg: case Channel.Lytelthorpe: case Channel.Nanto: case Channel.Rithwic: case Channel.Samsur: case Channel.Shoushi: case Channel.Yanshi: case Channel.Yaraq: if (!PropertyManager.GetBool("chat_log_townchans").Item) { return; } break; default: return; } if (channel != Channel.AllBroadcast) { log.Info($"[CHAT][{channel.ToString().ToUpper()}] {(sender != null ? sender.Name : "[SYSTEM]")} says on the {channel} channel, \"{message}\""); } else { log.Info($"[CHAT][GLOBAL] {(sender != null ? sender.Name : "[SYSTEM]")} issued a world broadcast, \"{message}\""); } }
public static void UpdatePKStatusForAllPlayers(string worldType, bool enabled) { switch (worldType) { case "pk_server": if (enabled) { foreach (var player in GetAllOnline()) { player.SetPlayerKillerStatus(PlayerKillerStatus.PK, true); } foreach (var player in GetAllOffline()) { player.SetProperty(PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK); player.SetProperty(PropertyFloat.MinimumTimeSincePk, 0); } var msg = $"This world has been changed to a Player Killer world. All players will become Player Killers in {PropertyManager.GetDouble("pk_respite_timer").Item} seconds."; BroadcastToAll(new GameMessageSystemChat(msg, ChatMessageType.WorldBroadcast)); LogBroadcastChat(Channel.AllBroadcast, null, msg); } else { foreach (var player in GetAllOnline()) { player.SetPlayerKillerStatus(PlayerKillerStatus.NPK, true); } foreach (var player in GetAllOffline()) { player.SetProperty(PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK); player.SetProperty(PropertyFloat.MinimumTimeSincePk, 0); } var msg = "This world has been changed to a Non Player Killer world. All players are now Non-Player Killers."; BroadcastToAll(new GameMessageSystemChat(msg, ChatMessageType.WorldBroadcast)); LogBroadcastChat(Channel.AllBroadcast, null, msg); } break; case "pkl_server": if (PropertyManager.GetBool("pk_server").Item) { return; } if (enabled) { foreach (var player in GetAllOnline()) { player.SetPlayerKillerStatus(PlayerKillerStatus.PKLite, true); } foreach (var player in GetAllOffline()) { player.SetProperty(PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK); player.SetProperty(PropertyFloat.MinimumTimeSincePk, 0); } var msg = $"This world has been changed to a Player Killer Lite world. All players will become Player Killer Lites in {PropertyManager.GetDouble("pk_respite_timer").Item} seconds."; BroadcastToAll(new GameMessageSystemChat(msg, ChatMessageType.WorldBroadcast)); LogBroadcastChat(Channel.AllBroadcast, null, msg); } else { foreach (var player in GetAllOnline()) { player.SetPlayerKillerStatus(PlayerKillerStatus.NPK, true); } foreach (var player in GetAllOffline()) { player.SetProperty(PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK); player.SetProperty(PropertyFloat.MinimumTimeSincePk, 0); } var msg = "This world has been changed to a Non Player Killer world. All players are now Non-Player Killers."; BroadcastToAll(new GameMessageSystemChat(msg, ChatMessageType.WorldBroadcast)); LogBroadcastChat(Channel.AllBroadcast, null, msg); } break; } }
private static void DoPlayerEnterWorld(Session session, Character character, Biota playerBiota, PossessedBiotas possessedBiotas) { Player player; Player.HandleNoLogLandblock(playerBiota); var stripAdminProperties = false; var addAdminProperties = false; var addSentinelProperties = false; if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (session.AccessLevel <= AccessLevel.Advocate) // check for elevated characters { if (playerBiota.WeenieType == (int)WeenieType.Admin || playerBiota.WeenieType == (int)WeenieType.Sentinel) // Downgrade weenie { character.IsPlussed = false; playerBiota.WeenieType = (int)WeenieType.Creature; stripAdminProperties = true; } } else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy) { if (playerBiota.WeenieType == (int)WeenieType.Creature || playerBiota.WeenieType == (int)WeenieType.Admin) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = (int)WeenieType.Sentinel; addSentinelProperties = true; } } else // Developers and Admins { if (playerBiota.WeenieType == (int)WeenieType.Creature || playerBiota.WeenieType == (int)WeenieType.Sentinel) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = (int)WeenieType.Admin; addAdminProperties = true; } } } if (playerBiota.WeenieType == (int)WeenieType.Admin) { player = new Admin(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else if (playerBiota.WeenieType == (int)WeenieType.Sentinel) { player = new Sentinel(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else { player = new Player(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } session.SetPlayer(player); if (stripAdminProperties) // continue stripping properties { player.CloakStatus = null; player.Attackable = true; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, true); player.AdvocateLevel = null; player.ChannelsActive = null; player.ChannelsAllowed = null; player.Invincible = null; player.Cloaked = null; player.ChangesDetected = true; player.CharacterChangesDetected = true; } if (addSentinelProperties || addAdminProperties) // continue restoring properties to default { WorldObject weenie; if (addAdminProperties) { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("admin"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)) as Admin; } else { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("sentinel"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)) as Sentinel; } if (weenie != null) { player.CloakStatus = CloakStatus.Off; player.Attackable = weenie.Attackable; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, false); player.AdvocateLevel = weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.AdvocateLevel); player.ChannelsActive = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsActive); player.ChannelsAllowed = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsAllowed); player.Invincible = false; player.Cloaked = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } } // If the client is missing a location, we start them off in the starter dungeon if (session.Player.Location == null) { if (session.Player.Instantiation != null) { session.Player.Location = new Position(session.Player.Instantiation); } else { session.Player.Location = new Position(2349072813, 12.3199f, -28.482f, 0.0049999995f, 0.0f, 0.0f, -0.9408059f, -0.3389459f); } } session.Player.PlayerEnterWorld(); LandblockManager.AddObject(session.Player, true); var popup_header = PropertyManager.GetString("popup_header").Item; var popup_motd = PropertyManager.GetString("popup_motd").Item; var popup_welcome = PropertyManager.GetString("popup_welcome").Item; if (character.TotalLogins <= 1) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd, popup_welcome))); } else if (!string.IsNullOrEmpty(popup_motd)) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd))); } var info = "Welcome to Asheron's Call\n powered by ACEmulator\n\nFor more information on commands supported by this server, type @acehelp\n"; session.Network.EnqueueSend(new GameMessageSystemChat(info, ChatMessageType.Broadcast)); var server_motd = PropertyManager.GetString("server_motd").Item; if (!string.IsNullOrEmpty(server_motd)) { session.Network.EnqueueSend(new GameMessageSystemChat($"{server_motd}\n", ChatMessageType.Broadcast)); } }
/// <summary> /// Returns the time remaining until the player can solve this quest again /// </summary> public TimeSpan GetNextSolveTime(string questFormat) { var questName = GetQuestName(questFormat); var quest = DatabaseManager.World.GetCachedQuest(questName); if (quest == null) { return(TimeSpan.MaxValue); // world quest not found - cannot solve it } var playerQuest = GetQuest(questName); if (playerQuest == null) { return(TimeSpan.MinValue); // player hasn't completed this quest yet - can solve immediately } if (quest.MaxSolves > -1 && playerQuest.NumTimesCompleted >= quest.MaxSolves) { return(TimeSpan.MaxValue); // cannot solve this quest again - max solves reached / exceeded } var currentTime = (uint)Time.GetUnixTime(); uint nextSolveTime; if (CanScaleQuestMinDelta(quest)) { nextSolveTime = playerQuest.LastTimeCompleted + (uint)(quest.MinDelta * PropertyManager.GetDouble("quest_mindelta_rate", 1).Item); } else { nextSolveTime = playerQuest.LastTimeCompleted + quest.MinDelta; } if (currentTime >= nextSolveTime) { return(TimeSpan.MinValue); // can solve again now - next solve time expired } // return the time remaining on the player's quest timer return(TimeSpan.FromSeconds(nextSolveTime - currentTime)); }
private static void DoPlayerEnterWorld(Session session, Character character, Biota playerBiota, PossessedBiotas possessedBiotas) { Player player; Player.HandleNoLogLandblock(playerBiota); var stripAdminProperties = false; var addAdminProperties = false; var addSentinelProperties = false; if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (session.AccessLevel <= AccessLevel.Advocate) // check for elevated characters { if (playerBiota.WeenieType == WeenieType.Admin || playerBiota.WeenieType == WeenieType.Sentinel) // Downgrade weenie { character.IsPlussed = false; playerBiota.WeenieType = WeenieType.Creature; stripAdminProperties = true; } } else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy) { if (playerBiota.WeenieType == WeenieType.Creature || playerBiota.WeenieType == WeenieType.Admin) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = WeenieType.Sentinel; addSentinelProperties = true; } } else // Developers and Admins { if (playerBiota.WeenieType == WeenieType.Creature || playerBiota.WeenieType == WeenieType.Sentinel) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = WeenieType.Admin; addAdminProperties = true; } } } if (playerBiota.WeenieType == WeenieType.Admin) { player = new Admin(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else if (playerBiota.WeenieType == WeenieType.Sentinel) { player = new Sentinel(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else { player = new Player(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } session.SetPlayer(player); if (stripAdminProperties) // continue stripping properties { player.CloakStatus = CloakStatus.Undef; player.Attackable = true; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, true); player.AdvocateLevel = null; player.ChannelsActive = null; player.ChannelsAllowed = null; player.Invincible = false; player.Cloaked = null; player.IgnoreHouseBarriers = false; player.IgnorePortalRestrictions = false; player.SafeSpellComponents = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } if (addSentinelProperties || addAdminProperties) // continue restoring properties to default { WorldObject weenie; if (addAdminProperties) { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("admin"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } else { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("sentinel"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } if (weenie != null) { player.CloakStatus = CloakStatus.Off; player.Attackable = weenie.Attackable; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, false); player.AdvocateLevel = weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.AdvocateLevel); player.ChannelsActive = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsActive); player.ChannelsAllowed = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsAllowed); player.Invincible = false; player.Cloaked = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } } // If the client is missing a location, we start them off in the starter town they chose if (session.Player.Location == null) { if (session.Player.Instantiation != null) { session.Player.Location = new Position(session.Player.Instantiation); } else { session.Player.Location = new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); // ultimate fallback } } session.Player.PlayerEnterWorld(); var success = LandblockManager.AddObject(session.Player, true); if (!success) { // send to lifestone, or fallback location var fixLoc = session.Player.Sanctuary ?? new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); log.Error($"WorldManager.DoPlayerEnterWorld: failed to spawn {session.Player.Name}, relocating to {fixLoc.ToLOCString()}"); session.Player.Location = new Position(fixLoc); LandblockManager.AddObject(session.Player, true); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(5.0f); actionChain.AddAction(session.Player, () => { if (session != null && session.Player != null) { session.Player.Teleport(fixLoc); } }); actionChain.EnqueueChain(); } var popup_header = PropertyManager.GetString("popup_header").Item; var popup_motd = PropertyManager.GetString("popup_motd").Item; var popup_welcome = PropertyManager.GetString("popup_welcome").Item; if (character.TotalLogins <= 1) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd, popup_welcome))); } else if (!string.IsNullOrEmpty(popup_motd)) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd))); } var info = "Welcome to Asheron's Call\n powered by ACEmulator\n\nFor more information on commands supported by this server, type @acehelp\n"; session.Network.EnqueueSend(new GameMessageSystemChat(info, ChatMessageType.Broadcast)); var server_motd = PropertyManager.GetString("server_motd").Item; if (!string.IsNullOrEmpty(server_motd)) { session.Network.EnqueueSend(new GameMessageSystemChat($"{server_motd}\n", ChatMessageType.Broadcast)); } }
/// <summary> /// Threaded task created when performing a server shutdown /// </summary> private static void ShutdownServer() { var shutdownTime = DateTime.UtcNow.AddSeconds(ShutdownInterval); ShutdownTime = shutdownTime; var lastNoticeTime = DateTime.UtcNow; // wait for shutdown interval to expire while (shutdownTime != DateTime.MinValue && shutdownTime >= DateTime.UtcNow) { // this allows the server shutdown to be canceled if (!ShutdownInitiated) { // reset shutdown details string shutdownText = $"The server has canceled the shutdown procedure @ {DateTime.UtcNow} UTC"; log.Info(shutdownText); // special text foreach (var player in PlayerManager.GetAllOnline()) { player.Session.WorldBroadcast(shutdownText); } // break function return; } lastNoticeTime = NotifyPlayersOfPendingShutdown(lastNoticeTime, shutdownTime.AddSeconds(1)); Thread.Sleep(10); } ShutdownInProgress = true; PropertyManager.ResyncVariables(); PropertyManager.StopUpdating(); WorldManager.EnqueueAction(new ActionEventDelegate(() => { log.Debug("Logging off all players..."); // logout each player foreach (var player in PlayerManager.GetAllOnline()) { player.Session.LogOffPlayer(true); } })); // Wait for all players to log out var logUpdateTS = DateTime.MinValue; int playerCount; while ((playerCount = PlayerManager.GetOnlineCount()) > 0) { logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for {playerCount} player{(playerCount > 1 ? "s" : "")} to log off..."); Thread.Sleep(10); } WorldManager.EnqueueAction(new ActionEventDelegate(() => { log.Debug("Disconnecting all sessions..."); // disconnect each session NetworkManager.DisconnectAllSessionsForShutdown(); })); // Wait for all sessions to drop out logUpdateTS = DateTime.MinValue; int sessionCount; while ((sessionCount = NetworkManager.GetSessionCount()) > 0) { logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for {sessionCount} session{(sessionCount > 1 ? "s" : "")} to disconnect..."); Thread.Sleep(10); } log.Debug("Adding all landblocks to destruction queue..."); // Queue unloading of all the landblocks // The actual unloading will happen in WorldManager.UpdateGameWorld LandblockManager.AddAllActiveLandblocksToDestructionQueue(); // Wait for all landblocks to unload logUpdateTS = DateTime.MinValue; int landblockCount; while ((landblockCount = LandblockManager.GetLoadedLandblocks().Count) > 0) { logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for {landblockCount} loaded landblock{(landblockCount > 1 ? "s" : "")} to unload..."); Thread.Sleep(10); } log.Debug("Stopping world..."); // Disabled thread update loop WorldManager.StopWorld(); // Wait for world to end logUpdateTS = DateTime.MinValue; while (WorldManager.WorldActive) { logUpdateTS = LogStatusUpdate(logUpdateTS, "Waiting for world to stop..."); Thread.Sleep(10); } log.Info("Saving OfflinePlayers that have unsaved changes..."); PlayerManager.SaveOfflinePlayersWithChanges(); // Wait for the database queue to empty logUpdateTS = DateTime.MinValue; int shardQueueCount; while ((shardQueueCount = DatabaseManager.Shard.QueueCount) > 0) { logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for database queue ({shardQueueCount}) to empty..."); Thread.Sleep(10); } // Write exit to console/log log.Info($"Exiting at {DateTime.UtcNow}"); // System exit Environment.Exit(Environment.ExitCode); }
/// <summary> /// Handles the eviction process for a player house /// </summary> public static void HandleEviction(House house, uint playerGuid, bool multihouse = false) { // clear out slumlord inventory var slumlord = house.SlumLord; slumlord.ClearInventory(true); var player = PlayerManager.FindByGuid(playerGuid, out bool isOnline); if (!PropertyManager.GetBool("house_rent_enabled", true).Item&& !multihouse) { // rent disabled, push forward var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var nextRentTime = house.GetRentDue(purchaseTime); player.HouseRentTimestamp = (int)nextRentTime; log.Info($"HouseManager.HandleRentPaid({player.Name}): house rent disabled via config"); // re-add item to queue AddRentQueue(player, house); return; } // handle eviction house.HouseOwner = null; house.MonarchId = null; house.HouseOwnerName = null; house.ClearPermissions(); house.SaveBiotaToDatabase(); // relink house.UpdateLinks(); // player slumlord 'off' animation var off = new Motion(MotionStance.Invalid, MotionCommand.Off); slumlord.CurrentMotionState = off; slumlord.EnqueueBroadcastMotion(off); // reset slumlord name var weenie = DatabaseManager.World.GetCachedWeenie(slumlord.WeenieClassId); var wo = WorldObjectFactory.CreateWorldObject(weenie, ObjectGuid.Invalid); slumlord.Name = wo.Name; slumlord.EnqueueBroadcast(new GameMessagePublicUpdatePropertyString(slumlord, PropertyString.Name, wo.Name)); slumlord.SaveBiotaToDatabase(); // if evicting a multihouse owner's previous house, // no update for player properties if (player.HouseInstance == house.Guid.Full) { player.HouseId = null; player.HouseInstance = null; //player.HousePurchaseTimestamp = null; player.HouseRentTimestamp = null; } else { log.Warn($"HouseManager.HandleRentEviction({house.Guid}, {player.Name}, {multihouse}): house guids don't match {player.HouseInstance}"); } house.ClearRestrictions(); log.Info($"HouseManager.HandleRentEviction({player.Name})"); if (multihouse) { RemoveRentQueue(house.Guid.Full); player.SaveBiotaToDatabase(); return; } if (!isOnline) { // inform player of eviction when they log in var offlinePlayer = PlayerManager.GetOfflinePlayer(playerGuid); if (offlinePlayer == null) { log.Warn($"{player.Name}.HandleEviction(): couldn't find offline player"); return; } offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true); offlinePlayer.SaveBiotaToDatabase(); return; } var onlinePlayer = PlayerManager.GetOnlinePlayer(playerGuid); onlinePlayer.House = null; // send text message onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You abandon your house!", ChatMessageType.Broadcast)); onlinePlayer.RemoveDeed(); onlinePlayer.SaveBiotaToDatabase(); // clear house panel for online player var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); // wait for slumlord inventory biotas above to save actionChain.AddAction(onlinePlayer, onlinePlayer.HandleActionQueryHouse); actionChain.EnqueueChain(); }
/// <summary> /// Queries the status of multi-house owners on the server /// </summary> private static void QueryMultiHouse() { var slumlordBiotas = DatabaseManager.Shard.GetBiotasByType(WeenieType.SlumLord); var playerHouses = new Dictionary <IPlayer, List <Biota> >(); var accountHouses = new Dictionary <string, List <Biota> >(); foreach (var slumlord in slumlordBiotas) { var biotaOwner = slumlord.BiotaPropertiesIID.FirstOrDefault(i => i.Type == (ushort)PropertyInstanceId.HouseOwner); if (biotaOwner == null) { // this is fine. this is just a house that was purchased, and then later abandoned //Console.WriteLine($"HouseManager.QueryMultiHouse(): couldn't find owner for house {slumlord.Id:X8}"); continue; } var owner = PlayerManager.FindByGuid(biotaOwner.Value); if (owner == null) { Console.WriteLine($"HouseManager.QueryMultiHouse(): couldn't find owner {biotaOwner.Value:X8}"); continue; } if (!playerHouses.TryGetValue(owner, out var houses)) { houses = new List <Biota>(); playerHouses.Add(owner, houses); } houses.Add(slumlord); var accountName = owner.Account != null ? owner.Account.AccountName : "NULL"; if (!accountHouses.TryGetValue(accountName, out var aHouses)) { aHouses = new List <Biota>(); accountHouses.Add(accountName, aHouses); } aHouses.Add(slumlord); } if (PropertyManager.GetBool("house_per_char").Item) { var results = playerHouses.Where(i => i.Value.Count() > 1).OrderByDescending(i => i.Value.Count()); if (results.Count() > 0) { Console.WriteLine("Multi-house owners:"); } foreach (var playerHouse in results) { Console.WriteLine($"{playerHouse.Key.Name}: {playerHouse.Value.Count}"); for (var i = 0; i < playerHouse.Value.Count; i++) { Console.WriteLine($"{i + 1}. {GetCoords(playerHouse.Value[i])}"); } } } else { var results = accountHouses.Where(i => i.Value.Count() > 1).OrderByDescending(i => i.Value.Count()); if (results.Count() > 0) { Console.WriteLine("Multi-house owners:"); } foreach (var accountHouse in results) { Console.WriteLine($"{accountHouse.Key}: {accountHouse.Value.Count}"); for (var i = 0; i < accountHouse.Value.Count; i++) { Console.WriteLine($"{i + 1}. {GetCoords(accountHouse.Value[i])}"); } } } }
/// <summary> /// Handles the eviction process for a player house /// </summary> public static void HandleEviction(House house, uint playerGuid, bool multihouse = false, bool force = false) { // clear out slumlord inventory var slumlord = house.SlumLord; slumlord.ClearInventory(true); var player = PlayerManager.FindByGuid(playerGuid, out bool isOnline); if (!PropertyManager.GetBool("house_rent_enabled", true).Item&& !multihouse && !force) { // rent disabled, push forward var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var nextRentTime = house.GetRentDue(purchaseTime); player.HouseRentTimestamp = (int)nextRentTime; log.Debug($"[HOUSE] HouseManager.HandleRentPaid({player.Name}): house rent disabled via config"); // re-add item to queue AddRentQueue(player, house); return; } // handle eviction house.HouseOwner = null; house.MonarchId = null; house.HouseOwnerName = null; house.ClearPermissions(); house.SaveBiotaToDatabase(); // relink house.UpdateLinks(); if (house.HasDungeon) { var dungeonHouse = house.GetDungeonHouse(); if (dungeonHouse != null) { dungeonHouse.UpdateLinks(); } } // player slumlord 'off' animation slumlord.Off(); // reset slumlord name slumlord.SetAndBroadcastName(); slumlord.SaveBiotaToDatabase(); // if evicting a multihouse owner's previous house, // no update for player properties if (player.HouseInstance == house.Guid.Full) { player.HouseId = null; player.HouseInstance = null; //player.HousePurchaseTimestamp = null; player.HouseRentTimestamp = null; } else { log.Warn($"HouseManager.HandleRentEviction({house.Guid}, {player.Name}, {multihouse}): house guids don't match {player.HouseInstance}"); } house.ClearRestrictions(); log.Debug($"[HOUSE] HouseManager.HandleRentEviction({player.Name})"); if (multihouse) { RemoveRentQueue(house.Guid.Full); player.SaveBiotaToDatabase(); return; } if (!isOnline) { // inform player of eviction when they log in var offlinePlayer = PlayerManager.GetOfflinePlayer(playerGuid); if (offlinePlayer == null) { log.Warn($"{player.Name}.HandleEviction(): couldn't find offline player"); return; } offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true); offlinePlayer.SaveBiotaToDatabase(); return; } var onlinePlayer = PlayerManager.GetOnlinePlayer(playerGuid); onlinePlayer.House = null; // send text message onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You've been evicted from your house!", ChatMessageType.Broadcast)); onlinePlayer.RemoveDeed(); onlinePlayer.SaveBiotaToDatabase(); // clear house panel for online player var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); // wait for slumlord inventory biotas above to save actionChain.AddAction(onlinePlayer, onlinePlayer.HandleActionQueryHouse); actionChain.EnqueueChain(); }
public static async void HandleEviction(PlayerHouse playerHouse) { // todo: copied from Player_House.HandleActionAbandonHouse, move to House.Abandon() // todo: get online copy of house var house = playerHouse.House; // clear out slumlord inventory // todo: get online copy of house var slumlord = house.SlumLord; slumlord.ClearInventory(true); var player = PlayerManager.FindByGuid(playerHouse.PlayerGuid, out bool isOnline); if (!PropertyManager.GetBool("house_rent_enabled", true).Item) { // rent disabled, push forward var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var nextRentTime = house.GetRentDue(purchaseTime); player.HouseRentTimestamp = (int)nextRentTime; log.Info($"HouseManager.HandleRentPaid({playerHouse.PlayerName}): house rent disabled via config"); BuildRentQueue(); return; } house.HouseOwner = null; house.MonarchId = null; house.HouseOwnerName = null; house.ClearPermissions(); house.SaveBiotaToDatabase(); // relink house.UpdateLinks(); // player slumlord 'off' animation var off = new Motion(MotionStance.Invalid, MotionCommand.Off); slumlord.CurrentMotionState = off; slumlord.EnqueueBroadcastMotion(off); // reset slumlord name var weenie = DatabaseManager.World.GetCachedWeenie(slumlord.WeenieClassId); var wo = WorldObjectFactory.CreateWorldObject(weenie, ObjectGuid.Invalid); slumlord.Name = wo.Name; slumlord.EnqueueBroadcast(new GameMessagePublicUpdatePropertyString(slumlord, PropertyString.Name, wo.Name)); slumlord.SaveBiotaToDatabase(); player.HouseId = null; player.HouseInstance = null; //player.HousePurchaseTimestamp = null; player.HouseRentTimestamp = null; house.ClearRestrictions(); log.Info($"HouseManager.HandleRentEviction({playerHouse.PlayerName})"); BuildRentQueue(); if (!isOnline) { // inform player of eviction when they log in var offlinePlayer = PlayerManager.GetOfflinePlayer(playerHouse.PlayerGuid); if (offlinePlayer == null) { log.Warn($"{playerHouse.PlayerName}.HandleEviction(): couldn't find offline player"); return; } offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true); offlinePlayer.SaveBiotaToDatabase(); return; } var onlinePlayer = PlayerManager.GetOnlinePlayer(playerHouse.PlayerGuid); onlinePlayer.House = null; // send text message onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You abandon your house!", ChatMessageType.Broadcast)); onlinePlayer.RemoveDeed(); await Task.Delay(3000); // wait for slumlord inventory biotas above to save onlinePlayer.HandleActionQueryHouse(); }
/// <summary> /// Threaded task created when performing a server shutdown /// </summary> private static void ShutdownServer() { var shutdownTime = DateTime.UtcNow.AddSeconds(ShutdownInterval); ShutdownTime = shutdownTime; var lastNoticeTime = DateTime.UtcNow; // wait for shutdown interval to expire while (shutdownTime != DateTime.MinValue && shutdownTime >= DateTime.UtcNow) { // this allows the server shutdown to be canceled if (!ShutdownInitiated) { // reset shutdown details string shutdownText = $"The server has canceled the shutdown procedure @ {DateTime.UtcNow} UTC"; log.Info(shutdownText); // special text foreach (var player in PlayerManager.GetAllOnline()) { player.Session.WorldBroadcast(shutdownText); } // break function return; } lastNoticeTime = NotifyPlayersOfPendingShutdown(lastNoticeTime, shutdownTime.AddSeconds(1)); Thread.Sleep(10); } PropertyManager.ResyncVariables(); PropertyManager.StopUpdating(); log.Debug("Logging off all players..."); // logout each player foreach (var player in PlayerManager.GetAllOnline()) { player.Session.LogOffPlayer(); } log.Info("Waiting for all players to log off..."); // wait 10 seconds for log-off while (PlayerManager.GetAllOnline().Count > 0) { Thread.Sleep(10); } log.Debug("Adding all landblocks to destruction queue..."); // Queue unloading of all the landblocks // The actual unloading will happen in WorldManager.UpdateGameWorld LandblockManager.AddAllActiveLandblocksToDestructionQueue(); log.Info("Waiting for all active landblocks to unload..."); while (LandblockManager.GetLoadedLandblocks().Count > 0) { Thread.Sleep(10); } log.Debug("Stopping world..."); // Disabled thread update loop WorldManager.StopWorld(); log.Info("Waiting for world to stop..."); // Wait for world to end while (WorldManager.WorldActive) { Thread.Sleep(10); } log.Info("Saving OfflinePlayers that have unsaved changes..."); PlayerManager.SaveOfflinePlayersWithChanges(); log.Info("Waiting for database queue to empty..."); // Wait for the database queue to empty while (DatabaseManager.Shard.QueueCount > 0) { Thread.Sleep(10); } // Write exit to console/log log.Info($"Exiting at {DateTime.UtcNow}"); // System exit Environment.Exit(Environment.ExitCode); }