示例#1
0
        private void PopulateZoneCache()
        {
            HashSet <IMyEntity> entities = new HashSet <IMyEntity>();

            Wrapper.GameAction(() =>
            {
                MyAPIGateway.Entities.GetEntities(entities, x => x is IMyCubeGrid);
            });

            foreach (IMyEntity entity in entities)
            {
                IMyCubeGrid grid = (IMyCubeGrid)entity;
                if (DockingZone.DoesGridContainZone(grid))
                {
                    m_zoneCache.Add(grid);
                }
            }
        }
示例#2
0
        public override bool HandleCommand(ulong userId, string[] words)
        {
            if (!PluginSettings.Instance.DockingEnabled)
            {
                return(false);
            }

            if (words.Length < 1)
            {
                Communication.SendPrivateInformation(userId, GetHelp());
                return(true);
            }

            if (m_undocking)
            {
                Communication.SendPrivateInformation(userId, string.Format("Server is busy, try again"));
                return(true);
            }

            m_undocking = true;
            try
            {
                String pylonName = String.Join(" ", words);
                if (PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).Count < 1)
                {
                    Communication.SendPrivateInformation(userId, string.Format("Unable to find player Id: {0}", userId));
                    return(true);
                }

                long playerId = PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).First( );

                Dictionary <String, List <IMyCubeBlock> > testList;
                List <IMyCubeBlock> beaconList;
                DockingZone.FindByName(pylonName, out testList, out beaconList, playerId);

                if (beaconList.Count == 4)
                {
                    foreach (IMyCubeBlock entity in beaconList)
                    {
                        if (!Entity.CheckOwnership(entity, playerId))
                        {
                            Communication.SendPrivateInformation(userId, string.Format("You do not have permission to use '{0}'.  You must either own all the beacons or they must be shared with faction.", pylonName));
                            return(true);
                        }
                    }

                    IMyCubeBlock e      = beaconList.First( );
                    IMyCubeGrid  parent = (IMyCubeGrid)e.Parent;

                    long[]             beaconListIds = beaconList.Select(p => p.EntityId).ToArray( );
                    long               ownerId       = beaconList.First( ).OwnerId;
                    List <DockingItem> dockingItems  = Docking.Instance.Find(d => d.PlayerId == ownerId && d.TargetEntityId == parent.EntityId && d.DockingBeaconIds.Intersect(beaconListIds).Count( ) == 4);
                    if (dockingItems.Count < 1)
                    {
                        Communication.SendPrivateInformation(userId, string.Format("You have no ships docked in docking zone '{0}'.", pylonName));
                        return(true);
                    }

                    DockingItem dockingItem = dockingItems.First( );

                    // Figure out center of docking area, and other distance information
                    double   maxDistance = 99;
                    Vector3D vPos        = new Vector3D(0, 0, 0);
                    foreach (IMyCubeBlock b in beaconList)
                    {
                        Vector3D beaconPos = Entity.GetBlockEntityPosition(b);
                        vPos += beaconPos;
                    }

                    vPos = vPos / 4;
                    foreach (IMyCubeBlock b in beaconList)
                    {
                        Vector3D beaconPos = Entity.GetBlockEntityPosition(b);
                        maxDistance = Math.Min(maxDistance, Vector3D.Distance(vPos, beaconPos));
                    }

                    List <IMySlimBlock> blocks = new List <IMySlimBlock>( );
                    parent.GetBlocks(blocks);
                    foreach (IMySlimBlock slim_cbe in blocks)
                    {
                        if (slim_cbe is IMyCubeBlock)
                        {
                            IMyCubeBlock cbe = slim_cbe.FatBlock;
                            if (cbe.GetObjectBuilderCubeBlock( ) is MyObjectBuilder_Cockpit)
                            {
                                MyObjectBuilder_Cockpit c = (MyObjectBuilder_Cockpit)cbe.GetObjectBuilderCubeBlock( );
                                if (c.Pilot != null)
                                {
                                    Communication.SendPrivateInformation(userId,
                                                                         string.Format(
                                                                             "Carrier ship has a pilot.  The carrier should be unpiloted and fully stopped before undocking.  (Sometimes this can lag a bit.  Wait 10 seconds and try again)",
                                                                             pylonName));
                                    return(true);
                                }
                            }
                        }
                    }

                    String dockedShipFileName = Essentials.PluginPath + String.Format("\\Docking\\docked_{0}_{1}_{2}.sbc", ownerId, dockingItem.TargetEntityId, dockingItem.DockedEntityId);

                    // Load Entity From File and add to game
                    FileInfo fileInfo = new FileInfo(dockedShipFileName);
                    //CubeGridEntity cubeGrid = new CubeGridEntity(fileInfo);

                    MyObjectBuilder_CubeGrid cubeGrid = BaseObjectManager.ReadSpaceEngineersFile <MyObjectBuilder_CubeGrid, MyObjectBuilder_CubeGridSerializer>(dockedShipFileName);

                    // Rotate our ship relative to our saved rotation and the new carrier rotation
                    cubeGrid.PositionAndOrientation =
                        new MyPositionAndOrientation(Matrix.CreateFromQuaternion(Quaternion.CreateFromRotationMatrix(parent.Physics.GetWorldMatrix( ).GetOrientation( )) * dockingItem.SaveQuat).GetOrientation( ));
                    // Move our ship relative to the new carrier position and orientation
                    Quaternion newQuat    = Quaternion.CreateFromRotationMatrix(parent.Physics.GetWorldMatrix( ).GetOrientation( ));
                    Vector3D   rotatedPos = Vector3D.Transform(dockingItem.SavePos, newQuat);
                    //cubeGrid.Position = rotatedPos + parent.GetPosition();
                    cubeGrid.PositionAndOrientation = new MyPositionAndOrientation(rotatedPos + parent.GetPosition( ), cubeGrid.PositionAndOrientation.Value.Forward, cubeGrid.PositionAndOrientation.Value.Up);

                    // Add object to world
                    cubeGrid.EntityId        = BaseEntity.GenerateEntityId( );
                    cubeGrid.LinearVelocity  = Vector3.Zero;
                    cubeGrid.AngularVelocity = Vector3.Zero;

                    bool undock = false;
                    Wrapper.GameAction(() =>
                    {
                        try
                        {
                            MyAPIGateway.Entities.CreateFromObjectBuilderAndAdd(cubeGrid);
                            List <MyObjectBuilder_EntityBase> addList = new List <MyObjectBuilder_EntityBase>( );
                            addList.Add(cubeGrid);
                            MyAPIGateway.Multiplayer.SendEntitiesCreated(addList);
                            undock = true;
                        }
                        catch (Exception Ex)
                        {
                            Log.Info(string.Format("Error undocking ship: {0}", Ex.ToString( )));
                            Communication.SendPrivateInformation(userId, string.Format("Unable to undock ship due to error."));
                        }
                    });

                    if (!undock)
                    {
                        return(true);
                    }

                    //SectorObjectManager.Instance.AddEntity(cubeGrid);

                    // Remove the docking file
                    File.Delete(dockedShipFileName);
                    Docking.Instance.Remove(dockingItem);

                    Communication.SendPrivateInformation(userId, string.Format("The ship '{0}' has been undocked from docking zone '{1}'", dockingItem.DockedName, pylonName));

                    /*
                     * // Queue for cooldown
                     * DockingCooldownItem cItem = new DockingCooldownItem();
                     * cItem.Name = pylonName;
                     * cItem.startTime = DateTime.Now;
                     *
                     * lock (m_cooldownList)
                     *      m_cooldownList.Add(cItem);
                     *
                     * IMyEntity gridEntity = MyAPIGateway.Entities.GetEntityById(dockingItem.DockedEntityId);
                     * IMyCubeGrid cubeGrid = (IMyCubeGrid)gridEntity;
                     *
                     * Quaternion q = Quaternion.CreateFromRotationMatrix(parent.WorldMatrix.GetOrientation()) * dockingItem.SaveQuat;
                     * Quaternion newQuat = Quaternion.CreateFromRotationMatrix(parent.WorldMatrix.GetOrientation());
                     * Vector3 parentPosition = parent.GetPosition();
                     * Vector3 rotatedPos = Vector3.Transform(dockingItem.savePos, newQuat);
                     * Vector3 position = rotatedPos + parentPosition;
                     * Matrix positionMatrix = Matrix.CreateFromQuaternion(q);
                     *
                     * cubeGrid.ChangeGridOwnership(playerId, MyOwnershipShareModeEnum.None);
                     * gridEntity.SetPosition(dockingItem.savePos);
                     *
                     * gridEntity.WorldMatrix = positionMatrix;
                     * gridEntity.SetPosition(position);
                     *
                     * // We need to update again, as this doesn't seem to sync properly?  I set world matrix, and setposition, and it doesn't go where it should, and I
                     * // have to bump into it for it to show up, it's mega weird.
                     *
                     * if (PluginDocking.Settings.DockingItems == null)
                     *      throw new Exception("DockingItems is null");
                     *
                     * // Remove from docked items
                     * PluginDocking.Settings.DockingItems.Remove(dockingItem);
                     *
                     * // Notify user
                     * Communication.SendPrivateInformation(userId, string.Format("The ship '{0}' has been undocked from docking zone '{1}'", gridEntity.DisplayName, pylonName));
                     */
                    // Queue for cooldown

                    /*
                     * DockingCooldownItem cItem = new DockingCooldownItem();
                     * cItem.name = pylonName;
                     * cItem.startTime = DateTime.Now;
                     * PluginDocking.CooldownList.Add(cItem);
                     */
                }
                else if (beaconList.Count > 4)                   // Too many beacons, must be 4
                {
                    Communication.SendPrivateInformation(userId,
                                                         string.Format("Too many beacons with the name or another zone with the name '{0}'.  Place only 4 beacons to create a zone or try a different zone name.",
                                                                       pylonName));
                }
                else                 // Can't find docking zone
                {
                    Communication.SendPrivateInformation(userId,
                                                         string.Format("Can not locate docking zone '{0}'.  There must be 4 beacons with the name '{0}' to create a docking zone.  Beacons must be fully built!",
                                                                       pylonName));
                }
            }
            catch (NullReferenceException ex)
            {
                Log.Error(ex);
            }
            finally
            {
                m_undocking = false;
            }

            return(true);
        }
        public override bool HandleCommand(ulong userId, string command)
        {
            string[] words = command.Split(' ');
            if (!PluginSettings.Instance.DockingEnabled)
            {
                return(false);
            }

            if (words.Length < 1)
            {
                Communication.SendPrivateInformation(userId, GetHelp());
                return(true);
            }

            String pylonName = String.Join(" ", words);

            if (PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).Count < 1)
            {
                Communication.SendPrivateInformation(userId, string.Format("Unable to find player Id: {0}", userId));
                return(true);
            }

            long playerId = PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).First();

            Dictionary <String, List <IMyCubeBlock> > testList;
            List <IMyCubeBlock> beaconList;

            DockingZone.FindByName(pylonName, out testList, out beaconList, playerId);

            if (beaconList.Count == 4)
            {
                foreach (IMyCubeBlock entity in beaconList)
                {
                    if (!Entity.CheckOwnership(entity, playerId))
                    {
                        Communication.SendPrivateInformation(userId, string.Format("You do not have permission to use '{0}'.  You must either own all the beacons or they must be shared with faction.", pylonName));
                        return(true);
                    }
                }

                IMyCubeBlock e      = beaconList.First();
                IMyCubeGrid  parent = (IMyCubeGrid)e.Parent;

                long[]             beaconListIds = beaconList.Select(p => p.EntityId).ToArray();
                long               ownerId       = beaconList.First().OwnerId;
                List <DockingItem> dockingItems  = Docking.Instance.Find(d => d.PlayerId == ownerId && d.TargetEntityId == parent.EntityId && d.DockingBeaconIds.Intersect(beaconListIds).Count() == 4);
                if (dockingItems.Count > 0)
                {
                    Communication.SendPrivateInformation(userId, string.Format("You have {1} ship(s) docked in docking zone '{0}':", pylonName, dockingItems.Count));
                    foreach (DockingItem item in dockingItems)
                    {
                        Communication.SendPrivateInformation(userId, string.Format("Docked ship: {0} ({1})", item.DockedName, item.DockedEntityId));
                    }

                    return(true);
                }
                else
                {
                    Communication.SendPrivateInformation(userId, string.Format("No ships are docked in docking zone '{0}'.", pylonName));
                }
            }
            else if (beaconList.Count > 4)
            {
                Communication.SendPrivateInformation(userId, string.Format("Too many beacons with the name or another zone with the name '{0}'.  Place only 4 beacons to create a zone or try a different zone name.", pylonName));
            }
            else
            {
                Communication.SendPrivateInformation(userId, string.Format("Can not locate docking zone '{0}'", pylonName));
            }

            return(true);
        }
        public override bool HandleCommand(ulong userId, string[] words)
        {
            if (!PluginSettings.Instance.DockingEnabled)
            {
                return(false);
            }

            if (words.Length < 1)
            {
                Communication.SendPrivateInformation(userId, GetHelp());
                return(true);
            }

            // Grab shared beacon name and playerId
            String pylonName = String.Join(" ", words);

            if (PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).Count < 1)
            {
                Communication.SendPrivateInformation(userId, "Failed to retrieve your playerId.  Command failed.");
                return(true);
            }

            long playerId = PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).First();

            // Find shared beacon area
            Dictionary <String, List <IMyCubeBlock> > testList;
            List <IMyCubeBlock> beaconList;

            DockingZone.FindByName(pylonName, out testList, out beaconList, playerId);
            if (beaconList.Count == 4)
            {
                // Check ownership
                foreach (IMyCubeBlock entityBlock in beaconList)
                {
                    if (!Entity.CheckOwnership(entityBlock, playerId))
                    {
                        Communication.SendPrivateInformation(userId, string.Format("You do not have permission to use '{0}'.  You must either own all the beacons or they must be shared with faction.", pylonName));
                        return(true);
                    }
                }

                // Check for bounding box intsection of other docking zones
                int intersectElement = 0;
                if (Entity.CheckForIntersection(testList, beaconList, out intersectElement))
                {
                    Communication.SendPrivateInformation(userId, string.Format("The docking zone '{0}' intersects with docking zone '{1}'.  Make sure you place your docking zones so they don't overlap.", pylonName, testList.ElementAt(intersectElement).Key));
                    return(true);
                }

                Communication.SendPrivateInformation(userId, string.Format("The docking zone '{0}' appears to be a valid docking zone.", pylonName));
            }
            else if (beaconList.Count > 4)
            {
                Communication.SendPrivateInformation(userId, string.Format("Too many beacons with the name or another zone with the name '{0}'.  Place only 4 beacons to create a zone or try a different zone name.", pylonName));
            }
            else
            {
                Communication.SendPrivateInformation(userId, string.Format("Can not locate docking zone '{0}'.  There must be 4 beacons with the name '{0}' to create a docking zone.  Beacons must be fully built!", pylonName));
            }

            return(true);
        }
        private void CheckPlayerInDockingZone(IMyPlayer player)
        {
            if (player.Controller == null || player.Controller.ControlledEntity == null || player.Controller.ControlledEntity.Entity == null)
            {
                return;
            }

            IMyEntity entity   = player.Controller.ControlledEntity.Entity;
            long      playerId = player.PlayerID;
            IMyEntity parent   = entity.GetTopMostParent();

            // Not a ship?  let's not process
            if (!(parent is IMyCubeGrid))
            {
                if (m_playersInside.Contains(playerId))
                {
                    m_playersInside.Remove(playerId);
                    ulong steamId = PlayerMap.Instance.GetSteamIdFromPlayerId(playerId);
                    Communication.Notification(steamId, MyFontEnum.DarkBlue, 7, "You have exited a ship in a docking zone");
                }

                return;
            }

            // Get All ships with 500m
            BoundingSphereD  sphere         = new BoundingSphereD(parent.GetPosition(), 500);
            List <IMyEntity> nearByEntities = null;

            // Live dangerously (no wrapper for speed!)
            try
            {
                nearByEntities = MyAPIGateway.Entities.GetEntitiesInSphere(ref sphere);
            }
            catch (Exception ex)
            {
                Logging.WriteLineAndConsole(string.Format("CheckPlayerInDockingZone(): {0}", ex.ToString()));
                return;
            }

            if (nearByEntities == null)
            {
                return;
            }

            List <IMyEntity> nearByGrids = nearByEntities.FindAll(x => x is IMyCubeGrid);

            // See if player's ship is inside a docking zone
            foreach (IMyEntity nearByEntity in nearByGrids)
            {
                // Sanity Check
                if (!(nearByEntity is IMyCubeGrid))
                {
                    return;
                }

                IMyCubeGrid cubeGrid = (IMyCubeGrid)nearByEntity;
                // Does this grid contain a docking zone?
                if (m_zoneCache.Contains(cubeGrid))
                {
                    Dictionary <String, List <IMyCubeBlock> > zoneList = DockingZone.GetZonesInGrid(cubeGrid);
                    if (zoneList == null)
                    {
                        continue;
                    }

                    // Get zones
                    foreach (KeyValuePair <String, List <IMyCubeBlock> > p in zoneList)
                    {
                        // Check if we're inside
                        if (DockingZone.IsGridInside((IMyCubeGrid)parent, p.Value))
                        {
                            if (!m_playersInside.Contains(playerId))
                            {
                                m_playersInside.Add(playerId);
                                ulong steamId = PlayerMap.Instance.GetSteamIdFromPlayerId(playerId);
                                Communication.Notification(steamId, MyFontEnum.Green, 7, string.Format("You are inside a valid docking zone: {0}", p.Key));
                            }

                            return;
                        }
                    }
                }
            }

            // We've left
            if (m_playersInside.Contains(playerId))
            {
                m_playersInside.Remove(playerId);
                ulong steamId = PlayerMap.Instance.GetSteamIdFromPlayerId(playerId);
                Communication.Notification(steamId, MyFontEnum.Red, 7, "You have left a docking zone");
            }
        }
示例#6
0
        public override bool HandleCommand(ulong userId, string[] words)
        {
            if (!PluginSettings.Instance.DockingEnabled)
            {
                return(false);
            }

            if (words.Length < 1)
            {
                Communication.SendPrivateInformation(userId, GetHelp());
                return(true);
            }

            if (m_docking)
            {
                Communication.SendPrivateInformation(userId, "Server is busy");
                return(true);
            }

            m_docking = true;
            try
            {
                string pylonName = string.Join(" ", words);

                /*
                 * int timeLeft;
                 * if (Entity.CheckCoolDown(pylonName, out timeLeft))
                 * {
                 *      Communication.Message(String.Format("The docking zone '{0}' is on cooldown.  Please wait a {1} seconds before trying to dock/undock again.", pylonName, Math.Max(0, timeLeft)));
                 *      return;
                 * }
                 */

                if (PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).Count < 1)
                {
                    Communication.SendPrivateInformation(userId, $"Unable to find player Id: {userId}");
                    return(true);
                }

                long playerId = PlayerMap.Instance.GetPlayerIdsFromSteamId(userId).First();

                Dictionary <string, List <IMyCubeBlock> > testList;
                List <IMyCubeBlock> beaconList;
                DockingZone.FindByName(pylonName, out testList, out beaconList, playerId);
                if (beaconList.Count == 4)
                {
                    // Check ownership
                    foreach (IMyCubeBlock entityBlock in beaconList)
                    {
                        IMyTerminalBlock terminal = (IMyTerminalBlock)entityBlock;
                        if (!terminal.HasPlayerAccess(playerId))
                        {
                            Communication.SendPrivateInformation(userId, $"You do not have permission to use '{pylonName}'.  You must either own all the beacons or they must be shared with faction.");
                            return(true);
                        }
                    }

                    // Check for bounding box intsection of other docking zones
                    int intersectElement = 0;
                    if (Entity.CheckForIntersection(testList, beaconList, out intersectElement))
                    {
                        Communication.SendPrivateInformation(userId, $"The docking zone '{pylonName}' intersects with docking zone '{testList.ElementAt( intersectElement ).Key}'.  Make sure you place your docking zones so they don't overlap.");
                        return(true);
                    }

                    // Check if ship already docked in this zone
                    IMyCubeBlock       e             = beaconList[0];
                    IMyCubeGrid        parent        = (IMyCubeGrid)e.Parent;
                    long[]             beaconListIds = beaconList.Select(b => b.EntityId).ToArray();
                    long               ownerId       = beaconList.First().OwnerId;
                    List <DockingItem> checkItems    = Docking.Instance.Find(d => d.PlayerId == ownerId && d.TargetEntityId == parent.EntityId && d.DockingBeaconIds.Intersect(beaconListIds).Count() == 4);
                    if (checkItems.Count >= PluginSettings.Instance.DockingShipsPerZone)
                    {
                        Communication.SendPrivateInformation(userId, $"Docking zone '{pylonName}' already contains the maximum capacity of ships.");
                        return(true);
                    }

                    // Figure out center of docking area, and other distance information
                    double   maxDistance = 99;
                    Vector3D vPos        = new Vector3D(0, 0, 0);

                    foreach (IMyCubeBlock b in beaconList)
                    {
                        Vector3D beaconPos = Entity.GetBlockEntityPosition(b);
                        vPos += beaconPos;
                    }

                    vPos = vPos / 4;
                    foreach (IMyCubeBlock b in beaconList)
                    {
                        Vector3D beaconPos = Entity.GetBlockEntityPosition(b);
                        maxDistance = Math.Min(maxDistance, Vector3D.Distance(vPos, beaconPos));
                    }

                    // Find ship in docking area
                    IMyCubeGrid         dockingEntity = null;
                    HashSet <IMyEntity> cubeGrids     = new HashSet <IMyEntity>();
                    Wrapper.GameAction(() =>
                    {
                        MyAPIGateway.Entities.GetEntities(cubeGrids, f => f is IMyCubeGrid);
                    });
                    foreach (IMyCubeGrid gridCheck in cubeGrids)
                    {
                        if (gridCheck.IsStatic || gridCheck == parent)
                        {
                            continue;
                        }

                        double distance = Vector3D.Distance(gridCheck.GetPosition(), vPos);
                        if (distance < maxDistance)
                        {
                            dockingEntity = gridCheck;
                            break;
                        }
                    }

                    // Figure out if the ship fits in docking area, and then save ship
                    if (dockingEntity != null)
                    {
                        // Get bounding box of both the docking zone and docking ship
                        OrientedBoundingBoxD targetBounding  = Entity.GetBoundingBox(beaconList);
                        OrientedBoundingBoxD dockingBounding = Entity.GetBoundingBox(dockingEntity);

                        // Make sure the docking zone contains the docking ship.  If they intersect or are disjointed, then fail
                        if (!Entity.GreaterThan(dockingBounding.HalfExtent * 2, targetBounding.HalfExtent * 2))
                        {
                            Communication.SendPrivateInformation(userId, $"The ship '{dockingEntity.DisplayName}' is too large for it's carrier.  The ship's bounding box must fit inside the docking zone bounding box!");
                            return(true);
                        }

                        if (targetBounding.Contains(ref dockingBounding) != ContainmentType.Contains)
                        {
                            Communication.SendPrivateInformation(userId, $"The ship '{dockingEntity.DisplayName}' is not fully inside the docking zone '{pylonName}'.  Make sure the ship is fully contained inside the docking zone");
                            return(true);
                        }

                        // Calculate the mass and ensure the docking ship is less than half the mass of the dock
                        float parentMass  = Entity.CalculateMass(parent);
                        float dockingMass = Entity.CalculateMass(dockingEntity);
                        if (dockingMass > parentMass)
                        {
                            Communication.SendPrivateInformation(userId, $"The ship you're trying to dock is too heavy for it's carrier.  The ship mass must be less than half the large ship / stations mass! (DM={dockingMass}kg CM={parentMass}kg)");
                            return(true);
                        }

                        // Check to see if the ship is piloted, if it is, error out.
                        // TODO: Check to see if we can get a real time copy of this entity?
                        List <IMySlimBlock> blocks = new List <IMySlimBlock>();
                        dockingEntity.GetBlocks(blocks, x => x.FatBlock is MyCockpit);
                        foreach (IMySlimBlock slim in blocks)
                        {
                            //MyObjectBuilder_Cockpit c = (MyObjectBuilder_Cockpit)slim.FatBlock.GetObjectBuilderCubeBlock();
                            var c = (MyShipController)slim.FatBlock;
                            if (c.Pilot != null)
                            {
                                Communication.SendPrivateInformation(userId, $"Ship in docking zone '{pylonName}' has a pilot!  Please exit the ship before trying to dock.  (Sometimes this can lag a bit.  Wait 10 seconds and try again)");
                                return(true);
                            }
                        }

                        // Save position and rotation information.  Some fun stuff here.
                        // Get our dock rotation as a quaternion
                        Quaternion saveQuat = Quaternion.CreateFromRotationMatrix(parent.WorldMatrix.GetOrientation());
                        // Transform docked ship's local position by inverse of the the parent (unwinds parent) and save it for when we undock
                        Vector3D savePos = Vector3D.Transform(dockingEntity.GetPosition() - parent.GetPosition(), Quaternion.Inverse(saveQuat));
                        // Get local rotation of dock ship, and save it for when we undock
                        saveQuat = Quaternion.Inverse(saveQuat) * Quaternion.CreateFromRotationMatrix(dockingEntity.WorldMatrix.GetOrientation());

                        // Save ship to file and remove
                        FileInfo info = new FileInfo(Path.Combine(Essentials.PluginPath, "Docking", $"docked_{ownerId}_{parent.EntityId}_{dockingEntity.EntityId}.sbc"));
                        if (!Directory.Exists(info.DirectoryName))
                        {
                            Directory.CreateDirectory(info.DirectoryName);
                        }
                        //CubeGridEntity dockingGrid = new CubeGridEntity((MyObjectBuilder_CubeGrid)dockingEntity.GetObjectBuilder(), dockingEntity);
                        MyObjectBuilder_CubeGrid gridBuilder = CubeGrids.SafeGetObjectBuilder(dockingEntity);
                        if (gridBuilder == null)
                        {
                            Communication.SendPrivateInformation(userId, $"Failed to load entity for export: {dockingEntity.DisplayName}");
                            return(true);
                        }

                        // Save item
                        DockingItem dockItem = new DockingItem
                        {
                            DockedEntityId   = dockingEntity.EntityId,
                            TargetEntityId   = parent.EntityId,
                            PlayerId         = ownerId,
                            DockingBeaconIds = beaconList.Select(s => s.EntityId).ToArray( ),
                            DockedName       = dockingEntity.DisplayName,
                            SavePos          = savePos,
                            SaveQuat         = saveQuat
                        };
                        Docking.Instance.Add(dockItem);

                        // Serialize and save ship to file
                        MyObjectBuilderSerializer.SerializeXML(info.FullName, false, gridBuilder);
                        Wrapper.BeginGameAction(() => dockingEntity.Close( ));

                        Communication.SendPrivateInformation(userId, $"Docked ship '{dockItem.DockedName}' in docking zone '{pylonName}'.");
                        Log.Info("Docked ship \"{0}\" in docking zone \"{1}\". Saved to {2}", dockItem.DockedName, pylonName, info.FullName);

                        /*
                         * // Add a cool down
                         * DockingCooldownItem cItem = new DockingCooldownItem();
                         * cItem.name = pylonName;
                         * cItem.startTime = DateTime.Now;
                         * PluginDocking.CooldownList.Add(cItem);
                         */
                    }
                    else
                    {
                        Communication.SendPrivateInformation(userId, $"No ships in docking zone '{pylonName}'.");
                    }
                }
                else if (beaconList.Count > 4)
                {
                    Communication.SendPrivateInformation(userId, $"Too many beacons with the name or another zone with the name '{pylonName}'.  Place only 4 beacons to create a zone or try a different zone name.");
                }
                else
                {
                    Communication.SendPrivateInformation(userId, string.Format("Can not locate docking zone '{0}'.  There must be 4 beacons with the name '{0}' to create a docking zone.  Beacons must be fully built!", pylonName));
                }

                return(true);
            }
            catch (SecurityException ex)
            {
                Log.Error("Can't access docked ship file.", ex);
                return(false);
            }
            catch (UnauthorizedAccessException ex)
            {
                Log.Error("Can't access docked ship file.", ex);
                return(false);
            }
            catch (DirectoryNotFoundException ex)
            {
                Log.Error("Directory does not exist", ex);
                return(false);
            }
            catch (IOException ex)
            {
                Log.Error(ex);
                return(false);
            }
            finally
            {
                m_docking = false;
            }
        }