public override bool HandleCommand(ulong userId, string[] words) { lock (Docking.Instance) { foreach (DockingItem dockingItem in Docking.Instance.DockingItems) { string dockedShipFileName = Essentials.PluginPath + String.Format("\\Docking\\docked_{0}_{1}_{2}.sbc", dockingItem.PlayerId, dockingItem.TargetEntityId, dockingItem.DockedEntityId); DockingItem dockedShip = dockingItem; IMyEntity entity = MyAPIGateway.Entities.GetEntity(x => x.EntityId == dockedShip.TargetEntityId && x is IMyCubeGrid); if (entity == null) { //Communication.SendPrivateInformation(userId, string.Format("Unable to undock ship due to error.")); Log.Info(string.Format("Unable to find parent '{0}' for '{1}' - '{2}'", dockingItem.TargetEntityId, dockingItem.DockedEntityId, dockingItem.DockedName)); //continue; } if (!File.Exists(dockedShipFileName)) { Log.Info(string.Format("Unable to find ship file: {0}", dockedShipFileName)); continue; } // FileInfo fileInfo = new FileInfo(dockedShipFileName); MyObjectBuilder_CubeGrid cubeGrid; MyObjectBuilderSerializer.DeserializeXML(dockedShipFileName, out cubeGrid); if (entity != null) { // Rotate our ship relative to our saved rotation and the new carrier rotation cubeGrid.PositionAndOrientation = new MyPositionAndOrientation(Matrix.CreateFromQuaternion(Quaternion.CreateFromRotationMatrix(entity.Physics.GetWorldMatrix().GetOrientation()) * dockingItem.SaveQuat).GetOrientation()); // Move our ship relative to the new carrier position and orientation Quaternion newQuat = Quaternion.CreateFromRotationMatrix(entity.Physics.GetWorldMatrix().GetOrientation()); Vector3D rotatedPos = Vector3D.Transform(dockingItem.SavePos, newQuat); //cubeGrid.Position = rotatedPos + parent.GetPosition(); cubeGrid.PositionAndOrientation = new MyPositionAndOrientation(MathUtility.RandomPositionFromPoint(entity.GetPosition(), 250f), cubeGrid.PositionAndOrientation.Value.Forward, cubeGrid.PositionAndOrientation.Value.Up); } else { cubeGrid.PositionAndOrientation = new MyPositionAndOrientation(MathUtility.RandomPositionFromPoint(cubeGrid.PositionAndOrientation.Value.Position, 500f), 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); } File.Delete(dockedShipFileName); Communication.SendPrivateInformation(userId, string.Format("The ship '{0}' has been undocked from docking zone", dockingItem.DockedName)); } Docking.Instance.DockingItems.Clear(); Docking.Instance.Save(); } 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); } 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[] 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; } }