public static async void HandlePlayerDelete(uint playerGuid) { var player = PlayerManager.FindByGuid(playerGuid); if (player == null) { Console.WriteLine($"HouseManager.HandlePlayerDelete({playerGuid:X8}): couldn't find player guid"); return; } if (player.HouseInstance == null) { return; } var playerHouse = FindPlayerHouse(playerGuid); if (playerHouse == null) { return; } // truncated ProcessRent / HandleEviction // load the most up-to-date house info from db playerHouse.House = House.GetHouse(playerHouse.House.Guid.Full); // todo: slumlord inventory callback await Task.Delay(3000); HandleEviction(playerHouse); }
/// <summary> /// Called on character delete, evicts from house /// </summary> public static void HandlePlayerDelete(uint playerGuid) { var player = PlayerManager.FindByGuid(playerGuid); if (player == null) { Console.WriteLine($"HouseManager.HandlePlayerDelete({playerGuid:X8}): couldn't find player guid"); return; } if (player.HouseInstance == null) { return; } var playerHouse = FindPlayerHouse(playerGuid); if (playerHouse == null) { return; } // load the most up-to-date copy of house data GetHouse(playerHouse.House.Guid.Full, (house) => { playerHouse.House = house; HandleEviction(playerHouse); RemoveRentQueue(house.Guid.Full); }); }
/// <summary> /// Called when a player joins/exits an Allegiance /// </summary> public static void Rebuild(Allegiance allegiance) { if (allegiance == null) { return; } RemoveCache(allegiance); // rebuild allegiance allegiance = GetAllegiance(allegiance.Monarch.Player); // relink players foreach (var member in allegiance.Members.Keys) { var player = PlayerManager.FindByGuid(member); if (player == null) { continue; } LoadPlayer(player); } // update dynamic properties allegiance.UpdateProperties(); }
/// <summary> /// Called when adding or removing a character squelch /// </summary> public void HandleActionModifyCharacterSquelch(bool squelch, uint playerGuid, string playerName, ChatMessageType messageType) { //Console.WriteLine($"{Player.Name}.HandleActionModifyCharacterSquelch({squelch}, {playerGuid:X8}, {playerName}, {messageType})"); if (messageType != ChatMessageType.AllChannels && !IsLegalChannel(messageType)) { Player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{messageType} is not a legal squelch channel", ChatMessageType.Broadcast)); return; } IPlayer player; if (playerGuid != 0) { player = PlayerManager.FindByGuid(new ObjectGuid(playerGuid)); if (player == null) { Player.Session.Network.EnqueueSend(new GameMessageSystemChat("Couldn't find player to squelch.", ChatMessageType.Broadcast)); return; } } else { if (string.IsNullOrWhiteSpace(playerName)) { return; } player = PlayerManager.FindByName(playerName); if (player == null) { Player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{playerName} not found.", ChatMessageType.Broadcast)); return; } } if (player.Guid == Player.Guid) { Player.Session.Network.EnqueueSend(new GameMessageSystemChat("You can't squelch yourself!", ChatMessageType.Broadcast)); return; } if (squelch) { SquelchCharacter(player, messageType); } else { UnsquelchCharacter(player, messageType); } UpdateSquelchDB(); SendSquelchDB(); }
/// <summary> /// Returns the monarch for a player /// </summary> public static IPlayer GetMonarch(IPlayer player) { if (player.Monarch == null) { return(player); } var monarch = PlayerManager.FindByGuid(player.Monarch.Value); return(monarch ?? player); }
/// <summary> /// Returns TRUE if a player currently meets all of the requirements for owning their house (allegiance rank) /// </summary> private static bool HasRequirements(PlayerHouse playerHouse) { if (!PropertyManager.GetBool("house_purchase_requirements").Item) { return(true); } var slumlord = playerHouse.House.SlumLord; if (slumlord.AllegianceMinLevel == null) { return(true); } var allegianceMinLevel = PropertyManager.GetLong("mansion_min_rank", -1).Item; if (allegianceMinLevel == -1) { allegianceMinLevel = slumlord.AllegianceMinLevel.Value; } var player = PlayerManager.FindByGuid(playerHouse.PlayerGuid); if (player == null) { log.Info($"{playerHouse.PlayerName}.HasRequirements() - couldn't find player"); return(false); } // ensure allegiance is loaded var allegiance = AllegianceManager.GetAllegiance(player); AllegianceNode allegianceNode = null; if (allegiance != null) { allegiance.Members.TryGetValue(player.Guid, out allegianceNode); } var rank = allegianceNode != null ? allegianceNode.Rank : 0; if (allegiance == null || rank < allegianceMinLevel) { log.Info($"{playerHouse.PlayerName}.HasRequirements() - allegiance rank {rank} < {allegianceMinLevel}"); return(false); } return(true); }
/// <summary> /// Handles a successful rent payment /// Updates the next rent due timestamp, and clears the slumlord inventory /// </summary> private static void HandleRentPaid(PlayerHouse playerHouse) { var player = PlayerManager.FindByGuid(playerHouse.PlayerGuid); if (player == null) { log.Warn($"[HOUSE] HouseManager.HandleRentPaid({playerHouse.PlayerName}): couldn't find player"); return; } var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var rentTime = (uint)(player.HouseRentTimestamp ?? 0); var nextRentTime = playerHouse.House.GetRentDue(purchaseTime); if (nextRentTime <= rentTime) { log.Warn($"[HOUSE] HouseManager.HandleRentPaid({playerHouse.PlayerName}): nextRentTime {nextRentTime} <= rentTime {rentTime}"); return; } player.HouseRentTimestamp = (int)nextRentTime; player.SaveBiotaToDatabase(); // clear out slumlord inventory var slumlord = playerHouse.House.SlumLord; slumlord.ClearInventory(); slumlord.SaveBiotaToDatabase(); log.Debug($"[HOUSE] HouseManager.HandleRentPaid({playerHouse.PlayerName}): rent payment successful!"); // re-add item to queue AddRentQueue(player, playerHouse.House); var onlinePlayer = PlayerManager.GetOnlinePlayer(playerHouse.PlayerGuid); if (onlinePlayer != null) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); // wait for slumlord inventory biotas above to save actionChain.AddAction(onlinePlayer, onlinePlayer.HandleActionQueryHouse); actionChain.EnqueueChain(); } }
/// <summary> /// Adds a player-owned house to the rent queue /// </summary> private static void AddRentQueue(Biota slumlord) { 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.AddRentQueue(): couldn't find owner for house {slumlord.Id:X8}"); return; } var owner = PlayerManager.FindByGuid(biotaOwner.Value); if (owner == null) { log.Error($"[HOUSE] HouseManager.AddRentQueue(): couldn't find owner {biotaOwner.Value:X8}"); return; } var houseId = slumlord.BiotaPropertiesDID.FirstOrDefault(i => i.Type == (ushort)PropertyDataId.HouseId); if (houseId == null) { log.Error($"[HOUSE] HouseManager.AddRentQueue(): couldn't find house id for {slumlord.Id:X8}"); return; } if (!HouseIdToGuid.TryGetValue(houseId.Value, out var houseGuids)) { log.Error($"[HOUSE] HouseManager.AddRentQueue(): couldn't find house instance for {slumlord.Id:X8}"); return; } var houseInstance = GetHouseGuid(slumlord.Id, houseGuids); if (houseInstance == 0) { log.Error($"[HOUSE] HouseManager.AddRentQueue(): couldn't find house guid for {slumlord.Id:X8}"); return; } if (RentQueueContainsHouse(houseInstance)) { log.Error($"[HOUSE] HouseManager.AddRentQueue(): rent queue already contains house {houseInstance}"); return; } AddRentQueue(owner, houseInstance); }
public static async void HandleRentPaid(PlayerHouse playerHouse) { var player = PlayerManager.FindByGuid(playerHouse.PlayerGuid); if (player == null) { log.Warn($"HouseManager.HandleRentPaid({playerHouse.PlayerName}): couldn't find player"); return; } var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var rentTime = (uint)(player.HouseRentTimestamp ?? 0); var nextRentTime = playerHouse.House.GetRentDue(purchaseTime); if (nextRentTime <= rentTime) { log.Warn($"HouseManager.HandleRentPaid({playerHouse.PlayerName}): nextRentTime {nextRentTime} <= rentTime {rentTime}"); return; } player.HouseRentTimestamp = (int)nextRentTime; // clear out slumlord inventory var slumlord = playerHouse.House.SlumLord; slumlord.ClearInventory(true); log.Info($"HouseManager.HandleRentPaid({playerHouse.PlayerName}): rent payment successful!"); BuildRentQueue(); var onlinePlayer = PlayerManager.GetOnlinePlayer(playerHouse.PlayerGuid); if (onlinePlayer != null) { await Task.Delay(3000); // wait for slumlord inventory biotas above to save onlinePlayer.HandleActionQueryHouse(); } }
/// <summary> /// Called when a player joins/exits an Allegiance /// </summary> public static void Rebuild(Allegiance allegiance) { if (allegiance == null) { return; } RemoveCache(allegiance); // rebuild allegiance var refresh = GetAllegiance(allegiance.Monarch.Player); // relink players foreach (var member in allegiance.Members.Keys) { var player = PlayerManager.FindByGuid(member); if (player == null) { continue; } LoadPlayer(player); } }
public static bool HasRequirements(PlayerHouse playerHouse) { if (!PropertyManager.GetBool("house_purchase_requirements").Item) { return(true); } var slumlord = playerHouse.House.SlumLord; if (slumlord.AllegianceMinLevel == null) { return(true); } var allegianceMinLevel = PropertyManager.GetLong("mansion_min_rank", -1).Item; if (allegianceMinLevel == -1) { allegianceMinLevel = slumlord.AllegianceMinLevel.Value; } var player = PlayerManager.FindByGuid(playerHouse.PlayerGuid); if (player == null) { log.Info($"{playerHouse.PlayerName}.HasRequirements() - couldn't find player"); return(false); } if (player.Allegiance == null || player.AllegianceNode.Rank < allegianceMinLevel) { log.Info($"{playerHouse.PlayerName}.HasRequirements() - allegiance rank {player.AllegianceNode.Rank} < {allegianceMinLevel}"); return(false); } return(true); }
/// <summary> /// Queries the status of multi-house owners on the server /// </summary> private static void QueryMultiHouse() { var slumlordBiotas = DatabaseManager.Shard.BaseDatabase.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])}"); } } } }
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(); }
public static void HandlePlayerDelete(uint playerGuid) { var player = PlayerManager.FindByGuid(playerGuid); if (player == null) { Console.WriteLine($"AllegianceManager.HandlePlayerDelete({playerGuid:X8}): couldn't find player guid"); return; } var allegiance = GetAllegiance(player); if (allegiance == null) { return; } allegiance.Members.TryGetValue(player.Guid, out var allegianceNode); var players = new List <IPlayer>() { player }; if (player.PatronId != null) { var patron = PlayerManager.FindByGuid(player.PatronId.Value); if (patron != null) { players.Add(patron); } } player.PatronId = null; player.MonarchId = null; // vassals now become monarchs... foreach (var vassalNode in allegianceNode.Vassals.Values) { var vassal = PlayerManager.FindByGuid(vassalNode.PlayerGuid); if (vassal == null) { continue; } vassal.PatronId = null; vassal.MonarchId = null; // walk the allegiance tree from this node, update monarch ids vassalNode.Walk((node) => { node.Player.MonarchId = vassalNode.PlayerGuid.Full; node.Player.SaveBiotaToDatabase(); }, false); players.Add(vassal); } RemoveCache(allegiance); // rebuild for those directly involved foreach (var p in players) { Rebuild(GetAllegiance(p)); } foreach (var p in players) { LoadPlayer(p); } foreach (var p in players) { HandleNoAllegiance(p); } // save immediately? foreach (var p in players) { p.SaveBiotaToDatabase(); } }
public static void HandlePlayerDelete(uint playerGuid) { var player = PlayerManager.FindByGuid(playerGuid); if (player == null) { Console.WriteLine($"AllegianceManager.HandlePlayerDelete({playerGuid:X8}): couldn't find player guid"); return; } var allegiance = GetAllegiance(player); if (allegiance == null) { return; } allegiance.Members.TryGetValue(player.Guid, out var allegianceNode); var players = new List <IPlayer>() { player }; if (player.PatronId != null) { var patron = PlayerManager.FindByGuid(player.PatronId.Value); players.Add(patron); } player.PatronId = null; player.MonarchId = null; // vassals now become monarchs... foreach (var vassal in allegianceNode.Vassals) { var vassalPlayer = PlayerManager.FindByGuid(vassal.PlayerGuid, out bool isOnline); vassalPlayer.PatronId = null; vassalPlayer.MonarchId = null; players.Add(vassal.Player); } RemoveCache(allegiance); // rebuild for those directly involved foreach (var p in players) { Rebuild(GetAllegiance(p)); } foreach (var p in players) { LoadPlayer(p); } foreach (var p in players) { HandleNoAllegiance(p); } // save immediately? foreach (var p in players) { var offline = PlayerManager.GetOfflinePlayer(p.Guid); if (offline != null) { offline.SaveBiotaToDatabase(); } else { var online = PlayerManager.GetOnlinePlayer(p.Guid); if (online != null) { online.SaveBiotaToDatabase(); } } } }
/// <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(); 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($"[HOUSE] 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($"[HOUSE] {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("Your house has reverted due to non-payment of the maintenance costs. All items stored in the house have been lost.", 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> /// 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(); }