Пример #1
0
        public void ActivateLinks(List <LandblockInstance> sourceObjects, List <Biota> biotas)
        {
            if (LinkedInstances.Count == 0)
            {
                return;
            }

            if (IsGenerator)
            {
                AddGeneratorLinks();
                return;
            }

            foreach (var link in LinkedInstances)
            {
                WorldObject wo    = null;
                var         biota = biotas.FirstOrDefault(b => b.Id == link.Guid);
                if (biota == null)
                {
                    wo = WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie(link.WeenieClassId), new ObjectGuid(link.Guid));
                }
                else
                {
                    wo = WorldObjectFactory.CreateWorldObject(biota);
                    //Console.WriteLine("Loaded child biota " + wo.Name);
                }

                if (wo == null)
                {
                    continue;
                }

                wo.Location = new Position(link.ObjCellId, link.OriginX, link.OriginY, link.OriginZ, link.AnglesX, link.AnglesY, link.AnglesZ, link.AnglesW);
                SetLinkProperties(wo);
                CurrentLandblock?.AddWorldObject(wo);

                wo.ParentLink = this;
                ChildLinks.Add(wo);

                // process nested links recursively
                foreach (var subLink in link.LandblockInstanceLink)
                {
                    var linkInstance = sourceObjects.FirstOrDefault(x => x.Guid == subLink.ChildGuid);

                    if (linkInstance != null)
                    {
                        wo.LinkedInstances.Add(linkInstance);
                    }
                }

                if (wo.LinkedInstances.Count > 0)
                {
                    wo.ActivateLinks(sourceObjects, biotas);
                }
            }
        }
Пример #2
0
        /// <summary>
        /// The only time this should be used is to populate EquippedObjects from the ctor.
        /// </summary>
        protected void AddBiotasToEquippedObjects(IEnumerable <Biota> wieldedItems)
        {
            foreach (var biota in wieldedItems)
            {
                var worldObject = WorldObjectFactory.CreateWorldObject(biota);
                EquippedObjects[worldObject.Guid] = worldObject;

                EncumbranceVal += worldObject.Burden;
            }

            EquippedObjectsLoaded = true;
        }
Пример #3
0
        /// <summary>
        /// The only time this should be used is to populate EquippedObjects from the ctor.
        /// </summary>
        protected void AddBiotasToEquippedObjects(IEnumerable <ACE.Database.Models.Shard.Biota> wieldedItems)
        {
            foreach (var biota in wieldedItems)
            {
                var worldObject = WorldObjectFactory.CreateWorldObject(biota);
                EquippedObjects[worldObject.Guid] = worldObject;

                EncumbranceVal += (worldObject.EncumbranceVal ?? 0);
            }

            EquippedObjectsLoaded = true;

            SetChildren();
        }
Пример #4
0
        public GameEventApproachVendor(Session session, WorldObject vendor, List <WorldObject> items)
            : base(GameEventType.ApproachVendor, GameMessageGroup.Group09, session)
        {
            Writer.Write(vendor.Guid.Full); // merchant id

            // bit mask ? categories / mask may need figured out more.
            Writer.Write((uint)vendor.MerchandiseItemTypes);
            Writer.Write((uint)vendor.MerchandiseMinValue);
            Writer.Write((uint)vendor.MerchandiseMaxValue);
            Writer.Write((uint)(vendor.DealMagicalItems ?? false ? 1 : 0)); // magic
            Writer.Write((float)vendor.BuyPrice);                           // buy_price
            Writer.Write((float)vendor.SellPrice);                          // sell_price
            Writer.Write(vendor.AlternateCurrencyDID ?? 0u);                // trade id .. wcid of currency vendor uses
            if (vendor.AlternateCurrencyDID > 0)
            {
                var    currency    = WorldObjectFactory.CreateWorldObject(Database.DatabaseManager.World.GetAceObjectByWeenie((uint)vendor.AlternateCurrencyDID));
                string fixedPlural = currency.NamePlural;
                if (fixedPlural == null)
                {
                    fixedPlural = currency.Name;
                    if (fixedPlural.EndsWith("ch") || fixedPlural.EndsWith("s") || fixedPlural.EndsWith("sh") || fixedPlural.EndsWith("x") || fixedPlural.EndsWith("z"))
                    {
                        fixedPlural += "es";
                    }
                    else
                    {
                        fixedPlural += "s";
                    }
                }
                Writer.Write((uint)0);              // trade number .. current amount of that currency player has on hand, need a function to return # of items of specific wcid found in inventory
                Writer.WriteString16L(fixedPlural); // the name of that currency
            }
            else
            {
                Writer.Write((uint)0);       // trade number .. current amount of that currency player has on hand, need a function to return # of items of specific wcid found in inventory
                Writer.WriteString16L("");   // the name of that currency
            }
            Writer.Write((uint)items.Count); // number of items

            foreach (WorldObject obj in items)
            {
                // Serialize Stream.
                Writer.Write(0xFFFFFFFF); // pretty sure this is either -1 (0xFFFFFFFF) or specific amount of item.. limited quanity
                obj.SerializeGameDataOnly(Writer);
            }

            Writer.Align();
        }
Пример #5
0
        /// <summary>
        /// The only time this should be used is to populate Inventory from the ctor.
        /// </summary>
        protected void SortBiotasIntoInventory(IEnumerable <Biota> biotas)
        {
            var worldObjects = new List <WorldObject>();

            foreach (var biota in biotas)
            {
                worldObjects.Add(WorldObjectFactory.CreateWorldObject(biota));
            }

            SortWorldObjectsIntoInventory(worldObjects);

            if (worldObjects.Count > 0)
            {
                log.Error("Inventory detected without a container to put it in to.");
            }
        }
Пример #6
0
        /// <summary>
        /// The only time this should be used is to populate Inventory from the ctor.
        /// </summary>
        protected void SortBiotasIntoInventory(IEnumerable <ACE.Database.Models.Shard.Biota> biotas)
        {
            var worldObjects = new List <WorldObject>();

            foreach (var biota in biotas)
            {
                worldObjects.Add(WorldObjectFactory.CreateWorldObject(biota));
            }

            SortWorldObjectsIntoInventory(worldObjects);

            if (worldObjects.Count > 0)
            {
                Console.WriteLine("Inventory detected without a container to put it in to.");
            }
        }
Пример #7
0
        /// <summary>
        /// On initial load, we will create all of the wielded items as world objects and add to dictionary for management.
        /// </summary>
        /// <param name="aceObject"></param>
        public Container(AceObject aceObject)
            : base(aceObject)
        {
            CoinValue = 0;
            log.Debug($"{aceObject.Name} CoinValue initialized to {CoinValue}");

            Burden = 0;
            log.Debug($"{aceObject.Name} Burden initialized to {Burden}");

            Burden += Weenie.EncumbranceVal ?? 0;
            log.Debug($"{aceObject.Name}'s weenie id is {Weenie.WeenieClassId} and its base burden is {Weenie.EncumbranceVal}, added to burden, Burden = {Burden}");

            Value  = 0;
            Value += Weenie.Value ?? 0;

            WieldedObjects = new Dictionary <ObjectGuid, WorldObject>();
            foreach (var wieldedItem in WieldedItems)
            {
                ObjectGuid woGuid = new ObjectGuid(wieldedItem.Value.AceObjectId);
                WieldedObjects.Add(woGuid, WorldObjectFactory.CreateWorldObject(wieldedItem.Value));

                Burden += wieldedItem.Value.EncumbranceVal;
                log.Debug($"{aceObject.Name} is wielding {wieldedItem.Value.Name}, adding {wieldedItem.Value.EncumbranceVal}, current Burden = {Burden}");

                Value += wieldedItem.Value.Value;
            }

            InventoryObjects = new Dictionary <ObjectGuid, WorldObject>();
            foreach (var inventoryItem in Inventory)
            {
                ObjectGuid  woGuid = new ObjectGuid(inventoryItem.Value.AceObjectId);
                WorldObject wo     = WorldObjectFactory.CreateWorldObject(inventoryItem.Value);
                InventoryObjects.Add(woGuid, wo);

                Burden += wo.Burden ?? 0;
                log.Debug($"{aceObject.Name} is has {wo.Name} in inventory, adding {wo.Burden}, current Burden = {Burden}");

                Value += wo.Value ?? 0;

                if (wo.WeenieType == WeenieType.Coin)
                {
                    CoinValue += wo.Value ?? 0;
                    log.Debug($"{aceObject.Name} is has {wo.Name} in inventory, of WeenieType.Coin, adding {wo.Value}, current CoinValue = {CoinValue}");
                }
            }
        }
Пример #8
0
        public virtual void ActivateLinks(List <LandblockInstance> sourceObjects)
        {
            if (LinkedInstances.Count == 0)
            {
                return;
            }

            if (IsGenerator)
            {
                AddGeneratorLinks();
                return;
            }

            foreach (var link in LinkedInstances)
            {
                var wo = WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie(link.WeenieClassId), new ObjectGuid(link.Guid));
                if (wo == null)
                {
                    continue;
                }

                wo.Location         = new Position(link.ObjCellId, link.OriginX, link.OriginY, link.OriginZ, link.AnglesX, link.AnglesY, link.AnglesZ, link.AnglesW);
                wo.ActivationTarget = Guid.Full;
                CurrentLandblock?.AddWorldObject(wo);

                // process nested links recursively
                foreach (var subLink in link.LandblockInstanceLink)
                {
                    var linkInstance = sourceObjects.FirstOrDefault(x => x.Guid == subLink.ChildGuid);

                    if (linkInstance != null)
                    {
                        wo.LinkedInstances.Add(linkInstance);
                    }
                }

                if (wo.LinkedInstances.Count > 0)
                {
                    wo.ActivateLinks(sourceObjects);
                }
            }
        }
Пример #9
0
        /// <summary>
        /// On initial load, we will create all of the wielded items as world objects and add to dictionary for management.
        /// </summary>
        /// <param name="aceObject"></param>
        public Container(AceObject aceObject)
            : base(aceObject)
        {
            CoinValue = 0;

            WieldedObjects = new Dictionary <ObjectGuid, WorldObject>();
            foreach (var wieldedItem in WieldedItems)
            {
                ObjectGuid woGuid = new ObjectGuid(wieldedItem.Value.AceObjectId);
                WieldedObjects.Add(woGuid, WorldObjectFactory.CreateWorldObject(wieldedItem.Value));
            }

            InventoryObjects = new Dictionary <ObjectGuid, WorldObject>();
            foreach (var inventoryItem in Inventory)
            {
                ObjectGuid woGuid = new ObjectGuid(inventoryItem.Value.AceObjectId);
                InventoryObjects.Add(woGuid, WorldObjectFactory.CreateWorldObject(inventoryItem.Value));
                if (InventoryObjects[woGuid].WeenieType == WeenieType.Container)
                {
                    InventoryObjects[woGuid].InventoryObjects = new Dictionary <ObjectGuid, WorldObject>();
                    foreach (var item in Inventory[woGuid].Inventory)
                    {
                        ObjectGuid cwoGuid = new ObjectGuid(item.Value.AceObjectId);
                        InventoryObjects[woGuid].InventoryObjects.Add(cwoGuid, WorldObjectFactory.CreateWorldObject(item.Value));

                        if (InventoryObjects[woGuid].WeenieType == WeenieType.Coin)
                        {
                            CoinValue += item.Value.Value ?? 0;
                        }
                    }
                }

                if (InventoryObjects[woGuid].WeenieType == WeenieType.Coin)
                {
                    CoinValue += inventoryItem.Value.Value ?? 0;
                }
            }
        }
Пример #10
0
        public void HandleActionAbandonHouse()
        {
            Console.WriteLine($"\n{Name}.HandleActionAbandonHouse()");

            var house = GetHouse();

            if (house != null)
            {
                house.HouseOwner = null;
                house.SaveBiotaToDatabase();

                // relink
                house.UpdateLinks();

                // player slumlord 'off' animation
                var slumlord = house.SlumLord;
                slumlord.EnqueueBroadcastMotion(new Motion(MotionStance.Invalid, MotionCommand.Off));

                // reset slumlord name
                var weenie = DatabaseManager.World.GetCachedWeenie(slumlord.WeenieClassId);
                var wo     = WorldObjectFactory.CreateWorldObject(weenie, new ObjectGuid(0));
                slumlord.Name = wo.Name;

                slumlord.EnqueueBroadcast(new GameMessagePublicUpdatePropertyString(slumlord, PropertyString.Name, wo.Name));
            }

            HouseId                = null;
            HouseInstance          = null;
            HousePurchaseTimestamp = null;

            House = null;

            // send text message
            Session.Network.EnqueueSend(new GameMessageSystemChat("You abandon your house!", ChatMessageType.Broadcast));

            HandleActionQueryHouse();
        }
Пример #11
0
        /// <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();
        }
Пример #12
0
        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();
        }
Пример #13
0
        public static void HandleCreateInst(Session session, params string[] parameters)
        {
            var loc = new Position(session.Player.Location);

            var param = parameters[0];

            Weenie weenie = null;

            if (uint.TryParse(param, out var wcid))
            {
                weenie = DatabaseManager.World.GetCachedWeenie(wcid);   // wcid
            }
            else
            {
                weenie = DatabaseManager.World.GetCachedWeenie(param);  // classname
            }
            if (weenie == null)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find weenie {param}", ChatMessageType.Broadcast));
                return;
            }

            var landblock      = session.Player.CurrentLandblock.Id.Landblock;
            var nextStaticGuid = GetNextStaticGuid(landblock);

            var wo = WorldObjectFactory.CreateWorldObject(weenie, new ObjectGuid(nextStaticGuid));

            if (wo == null)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"Failed to create new object for {weenie.ClassId} - {weenie.ClassName}", ChatMessageType.Broadcast));
                return;
            }

            if (!wo.Stuck)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"{weenie.ClassId} - {weenie.ClassName} is missing PropertyBool.Stuck, cannot spawn as landblock instance", ChatMessageType.Broadcast));
                return;
            }

            // spawn as ethereal temporarily, to spawn directly on player position
            wo.Ethereal = true;
            wo.Location = new Position(loc);

            // even on flat ground, objects can sometimes fail to spawn at the player's current Z
            // Position.Z has some weird thresholds when moving around, but i guess the same logic doesn't apply when trying to spawn in...
            wo.Location.PositionZ += 0.05f;

            session.Network.EnqueueSend(new GameMessageSystemChat($"Creating new landblock instance @ {loc.ToLOCString()}\n{wo.WeenieClassId} - {wo.Name} ({nextStaticGuid:X8})", ChatMessageType.Broadcast));

            wo.EnterWorld();

            LastStaticGuid[landblock] = wo.Guid.Full;

            // serialize to .sql file
            var contentFolder = VerifyContentFolder(session, false);

            var sep    = Path.DirectorySeparatorChar;
            var folder = new DirectoryInfo($"{contentFolder.FullName}{sep}sql{sep}6 LandblockExtendedData{sep}");

            if (!folder.Exists)
            {
                folder.Create();
            }

            var pos      = wo.PhysicsObj.Position;
            var origin   = pos.Frame.Origin;
            var rotation = pos.Frame.Orientation;

            var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

            var insert = $"INSERT INTO `landblock_instance` (`guid`, `weenie_Class_Id`, `obj_Cell_Id`, `origin_X`, `origin_Y`, `origin_Z`, `angles_W`, `angles_X`, `angles_Y`, `angles_Z`, `is_Link_Child`, `last_Modified`)\n" +
                         $"VALUES(0x{wo.Guid.Full:X8}, {wo.WeenieClassId}, 0x{wo.PhysicsObj.Position.ObjCellID:X8}, {origin.X}, {origin.Y}, {origin.Z}, {rotation.W}, {rotation.X}, {rotation.Y}, {rotation.Z}, False, '{timestamp}'); /* {wo.Name} */\n" +
                         $"/* @teleloc {wo.Location.ToLOCString()} */";

            var sql_filename = $"{landblock:X4}.sql";

            using (var file = File.Open($"{folder.FullName}{sep}{sql_filename}", FileMode.OpenOrCreate))
            {
                file.Seek(0, SeekOrigin.End);
                using (var stream = new StreamWriter(file))
                {
                    if (file.Position > 0)
                    {
                        stream.WriteLine();
                    }

                    stream.WriteLine(insert);
                }
            }
        }
Пример #14
0
        public static void HandleCreateInst(Session session, params string[] parameters)
        {
            var loc = new Position(session.Player.Location);

            var param = parameters[0];

            Weenie weenie = null;

            uint?parentGuid = null;

            var landblock = session.Player.CurrentLandblock.Id.Landblock;

            var firstStaticGuid = 0x70000000 | (uint)landblock << 12;

            if (parameters.Length > 1)
            {
                var allParams = string.Join(" ", parameters);

                var match = Regex.Match(allParams, @"-p ([\S]+) -c ([\S]+)", RegexOptions.IgnoreCase);

                if (match.Success)
                {
                    var parentGuidStr = match.Groups[1].Value;
                    param = match.Groups[2].Value;

                    if (parentGuidStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
                    {
                        parentGuidStr = parentGuidStr.Substring(2);
                    }

                    if (!uint.TryParse(parentGuidStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var _parentGuid))
                    {
                        session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't parse parent guid {match.Groups[1].Value}", ChatMessageType.Broadcast));
                        return;
                    }

                    parentGuid = _parentGuid;

                    if (parentGuid <= 0xFFF)
                    {
                        parentGuid = firstStaticGuid | parentGuid;
                    }
                }

                else if (parameters[1].StartsWith("-c", StringComparison.OrdinalIgnoreCase))
                {
                    // get parent from last appraised object
                    var parent = CommandHandlerHelper.GetLastAppraisedObject(session);

                    if (parent == null)
                    {
                        session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find parent object", ChatMessageType.Broadcast));
                        return;
                    }

                    parentGuid = parent.Guid.Full;
                }
            }

            if (uint.TryParse(param, out var wcid))
            {
                weenie = DatabaseManager.World.GetCachedWeenie(wcid);   // wcid
            }
            else
            {
                weenie = DatabaseManager.World.GetCachedWeenie(param);  // classname
            }
            if (weenie == null)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find weenie {param}", ChatMessageType.Broadcast));
                return;
            }

            // clear any cached instances for this landblock
            DatabaseManager.World.ClearCachedInstancesByLandblock(landblock);

            var instances = DatabaseManager.World.GetCachedInstancesByLandblock(landblock);

            // for link mode, ensure parent guid instance exists
            WorldObject       parentObj      = null;
            LandblockInstance parentInstance = null;

            if (parentGuid != null)
            {
                parentInstance = instances.FirstOrDefault(i => i.Guid == parentGuid);

                if (parentInstance == null)
                {
                    session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find landblock instance for parent guid 0x{parentGuid:X8}", ChatMessageType.Broadcast));
                    return;
                }

                parentObj = session.Player.CurrentLandblock.GetObject(parentGuid.Value);

                if (parentObj == null)
                {
                    session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find parent object 0x{parentGuid:X8}", ChatMessageType.Broadcast));
                    return;
                }
            }

            var nextStaticGuid = GetNextStaticGuid(landblock, instances);

            var maxStaticGuid = firstStaticGuid | 0xFFF;

            // manually specify a start guid?
            if (parameters.Length == 2)
            {
                if (uint.TryParse(parameters[1].Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var startGuid))
                {
                    if (startGuid <= 0xFFF)
                    {
                        startGuid = firstStaticGuid | startGuid;
                    }

                    if (startGuid < firstStaticGuid || startGuid > maxStaticGuid)
                    {
                        session.Network.EnqueueSend(new GameMessageSystemChat($"Landblock instance guid {startGuid:X8} must be between {firstStaticGuid:X8} and {maxStaticGuid:X8}", ChatMessageType.Broadcast));
                        return;
                    }

                    var existing = instances.FirstOrDefault(i => i.Guid == startGuid);

                    if (existing != null)
                    {
                        session.Network.EnqueueSend(new GameMessageSystemChat($"Landblock instance guid {startGuid:X8} already exists", ChatMessageType.Broadcast));
                        return;
                    }
                    nextStaticGuid = startGuid;
                }
            }


            if (nextStaticGuid >= maxStaticGuid)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"Landblock {landblock:X4} has reached the maximum # of static guids", ChatMessageType.Broadcast));
                return;
            }

            // create and spawn object
            var wo = WorldObjectFactory.CreateWorldObject(weenie, new ObjectGuid(nextStaticGuid));

            if (wo == null)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"Failed to create new object for {weenie.ClassId} - {weenie.ClassName}", ChatMessageType.Broadcast));
                return;
            }

            if (!wo.Stuck)
            {
                session.Network.EnqueueSend(new GameMessageSystemChat($"{weenie.ClassId} - {weenie.ClassName} is missing PropertyBool.Stuck, cannot spawn as landblock instance", ChatMessageType.Broadcast));
                return;
            }

            // spawn as ethereal temporarily, to spawn directly on player position
            wo.Ethereal = true;
            wo.Location = new Position(loc);

            // even on flat ground, objects can sometimes fail to spawn at the player's current Z
            // Position.Z has some weird thresholds when moving around, but i guess the same logic doesn't apply when trying to spawn in...
            wo.Location.PositionZ += 0.05f;

            var isLinkChild = parentInstance != null;

            session.Network.EnqueueSend(new GameMessageSystemChat($"Creating new landblock instance {(isLinkChild ? "child object " : "")}@ {loc.ToLOCString()}\n{wo.WeenieClassId} - {wo.Name} ({nextStaticGuid:X8})", ChatMessageType.Broadcast));

            if (!wo.EnterWorld())
            {
                session.Network.EnqueueSend(new GameMessageSystemChat("Failed to spawn new object at this location", ChatMessageType.Broadcast));
                return;
            }

            // create new landblock instance
            var instance = CreateLandblockInstance(wo, isLinkChild);

            instances.Add(instance);

            if (isLinkChild)
            {
                var link = new LandblockInstanceLink();

                link.ParentGuid   = parentGuid.Value;
                link.ChildGuid    = wo.Guid.Full;
                link.LastModified = DateTime.Now;

                parentInstance.LandblockInstanceLink.Add(link);

                parentObj.LinkedInstances.Add(instance);

                // ActivateLinks?
                parentObj.SetLinkProperties(wo);
                parentObj.ChildLinks.Add(wo);
                wo.ParentLink = parentObj;
            }

            SyncInstances(session, landblock, instances);
        }