public void Add(DockingItem item)
        {
            lock(m_instance)
                m_dockingItems.Add(item);

            Save();
        }
        public void Remove(DockingItem item)
        {
            lock (m_instance)
                m_dockingItems.Remove(item);

            Save();
        }
        public void Add(DockingItem item)
        {
            lock (m_instance)
                m_dockingItems.Add(item);

            Save();
        }
		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, 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)
				{
					// Check ownership
					foreach (IMyCubeBlock entityBlock in beaconList)
					{
						IMyTerminalBlock terminal = (IMyTerminalBlock)entityBlock;
						if (!terminal.HasPlayerAccess(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;
					}

					// 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, string.Format("Docking zone '{0}' already contains the maximum capacity of ships.", pylonName));
						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, string.Format("The ship '{0}' is too large for it's carrier.  The ship's bounding box must fit inside the docking zone bounding box!", dockingEntity.DisplayName));
							return true;
						}

						if (targetBounding.Contains(ref dockingBounding) != ContainmentType.Contains)
						{
							Communication.SendPrivateInformation(userId, string.Format("The ship '{0}' is not fully inside the docking zone '{1}'.  Make sure the ship is fully contained inside the docking zone", dockingEntity.DisplayName, pylonName));
							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, string.Format("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={0}kg CM={1}kg)", dockingMass, parentMass));
							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 != null && x.FatBlock.BlockDefinition.TypeId == typeof(MyObjectBuilder_Cockpit));
						foreach (IMySlimBlock slim_cbe in blocks)
						{
							MyObjectBuilder_Cockpit c = (MyObjectBuilder_Cockpit)slim_cbe.FatBlock.GetObjectBuilderCubeBlock();
							if (c.Pilot != null)
							{
								Communication.SendPrivateInformation(userId, string.Format("Ship in docking zone '{0}' has a pilot!  Please exit the ship before trying to dock.  (Sometimes this can lag a bit.  Wait 10 seconds and try again)", pylonName));
								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", string.Format( "docked_{0}_{1}_{2}.sbc", ownerId, parent.EntityId, dockingEntity.EntityId ) ) );
						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, string.Format("Failed to load entity for export: {0}", 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 );
						//BaseObjectManager.SaveContentFile<MyObjectBuilder_CubeGrid, MyObjectBuilder_CubeGridSerializer>(gridBuilder, info);
						BaseEntityNetworkManager.BroadcastRemoveEntity(dockingEntity);
						//dockingEntity.Close();

						Communication.SendPrivateInformation(userId, string.Format("Docked ship '{0}' in docking zone '{1}'.", dockItem.DockedName, 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, string.Format("No ships 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}'.  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;
			}
		}
        public void Remove(DockingItem item)
        {
            lock (m_instance)
                m_dockingItems.Remove(item);

            Save();
        }