diff --git a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs index 9665058..67a356e 100644 --- a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs +++ b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Helpers.cs @@ -170,7 +170,7 @@ public static void Update() public class TaskScheduler { - private const int MAX_TASKS_PER_FRAME = 50; //at 10 it takes 5.5 minutes on a 33k reactor grid. we can speed that up + private const int MAX_TASKS_PER_FRAME = 10; //at 10 it takes 5.5 minutes on a 33k reactor grid. we can speed that up // I sped it up -Muzz private readonly object _lock = new object(); private readonly Queue _taskQueue = new Queue(); @@ -216,13 +216,13 @@ public bool TryGetEntity(out TEntity entity) public class PerformanceMonitor { - private const int REPORT_INTERVAL = 1000; + private const int REPORT_INTERVAL = 600; private int _tickCount; private long _syncTimeTotal; private long _asyncTimeTotal; private int _tasksProcessedTotal; - private long _blocksCulledTotal; // New! - private long _blocksUnculledTotal; // New! + private long _blocksCulledTotal; + private long _blocksUnculledTotal; public void RecordSyncOperation(long milliseconds) => Interlocked.Add(ref _syncTimeTotal, milliseconds); public void RecordAsyncOperation(long milliseconds) => Interlocked.Add(ref _asyncTimeTotal, milliseconds); @@ -242,14 +242,13 @@ public void Update() private void GenerateReport() { - // Using StringBuilder instead of string concatenation reduces memory allocations StringBuilder report = new StringBuilder(); report.AppendLine($"Performance Report (Last {REPORT_INTERVAL} ticks):") .AppendLine($"Avg Sync Time: {(double)_syncTimeTotal / REPORT_INTERVAL:F2}ms") .AppendLine($"Avg Async Time: {(double)_asyncTimeTotal / REPORT_INTERVAL:F2}ms") .AppendLine($"Tasks/Second: {(double)_tasksProcessedTotal / (REPORT_INTERVAL / 60.0):F2}") - .AppendLine($"Blocks Culled: {Interlocked.Read(ref _blocksCulledTotal):F2}") - .AppendLine($"Blocks Unculled: {Interlocked.Read(ref _blocksUnculledTotal):F2}"); + .AppendLine($"Blocks Culled: {_blocksCulledTotal:F2}") + .AppendLine($"Blocks Unculled: {_blocksUnculledTotal:F2}"); ThreadSafeLog.EnqueueMessage(report.ToString()); } diff --git a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs index 9c4544d..715b38b 100644 --- a/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs +++ b/BlockCulling/Data/Scripts/BlockCulling/BlockCulling_Session.cs @@ -12,6 +12,7 @@ using VRage.ModAPI; using VRage.Utils; using VRageMath; +using VRageMath.Spatial; namespace Scripts.BlockCulling { @@ -97,28 +98,23 @@ protected override void UnloadData() public override void UpdateBeforeSimulation() { - - if (modConfig == null || !modConfig.ModEnabled) return; + if (modConfig == null || !modConfig.ModEnabled) + return; try { var stopwatch = Stopwatch.StartNew(); _taskScheduler?.ProcessTasks(); - if (ThreadSafeLog.EnableDebugLogging) - { - ThreadSafeLog.ProcessLogQueue(); - } + ThreadSafeLog.ProcessLogQueue(); stopwatch.Stop(); _performanceMonitor?.RecordSyncOperation(stopwatch.ElapsedMilliseconds); _performanceMonitor?.Update(); - if (ThreadSafeLog.EnableDebugLogging) + + _reportCounter++; + if (_reportCounter >= REPORT_INTERVAL) { - _reportCounter++; - if (_reportCounter >= REPORT_INTERVAL) - { - GenerateCulledBlocksReport(); - _reportCounter = 0; - } + GenerateCulledBlocksReport(); + _reportCounter = 0; } } catch (Exception e) @@ -185,18 +181,26 @@ public override void UpdateAfterSimulation() private void ProcessQueuedBlockCulls() { int maxProcessPerTick = MaxBlocksCulledPerTick; - int cullCount = Math.Min(_queuedBlockCulls.Count, maxProcessPerTick / 2); + int cullCount = Math.Min(_queuedBlockCulls.Count, maxProcessPerTick); + for (int i = 0; i < cullCount; i++) { _queuedBlockCulls[i].Visible = false; } _queuedBlockCulls.RemoveRange(0, cullCount); + int uncullCount = Math.Min(_queuedBlockUnculls.Count, maxProcessPerTick - cullCount); for (int i = 0; i < uncullCount; i++) { _queuedBlockUnculls[i].Visible = true; } _queuedBlockUnculls.RemoveRange(0, uncullCount); + + if (ThreadSafeLog.EnableDebugLogging) + { + _performanceMonitor.RecordBlocksCulled(cullCount); + _performanceMonitor.RecordBlocksUnculled(uncullCount); + } } private void UpdateGridCulling() @@ -218,6 +222,7 @@ private void UpdateGridCulling() foreach (var block in _culledBlocks[grid]) { if (!block.Visible) _queuedBlockUnculls.Add(block); + } } } @@ -245,7 +250,8 @@ private void GenerateCulledBlocksReport() } } report.AppendFormat("TOTAL: {0} blocks across {1} grids", totalCulledBlocks, totalGrids); - ThreadSafeLog.EnqueueMessageDebug(report.ToString()); + _performanceMonitor.RecordBlocksCulled(totalCulledBlocks); + ThreadSafeLog.EnqueueMessageDebug(report.ToString()); } private void OnEntityAdd(IMyEntity entity) @@ -256,7 +262,7 @@ private void OnEntityAdd(IMyEntity entity) HashSet gridBlocks = _culledBlocks.ContainsKey(grid) ? _culledBlocks[grid] : (_culledBlocks[grid] = new HashSet()); - + if (gridBlocks.Count > 0) { ThreadSafeLog.EnqueueMessageDebug($"Grid {grid.EntityId} already exists. Re-initializing..."); @@ -310,21 +316,31 @@ private void SetTransparencyAsync(SafeSlimBlockRef slimBlockRef, bool recursive try { IMySlimBlock slimBlock; - if (!slimBlockRef.TryGetSlimBlock(out slimBlock)) return; + if (!slimBlockRef.TryGetSlimBlock(out slimBlock)) + return; + IMyCubeBlock block = slimBlock.FatBlock; - if (!recursive && block == null) return; + if (!recursive && block == null) + return; + var blockSlimNeighbors = new HashSet(); bool shouldCullBlock = BlockEligibleForCulling(slimBlock, ref blockSlimNeighbors); + MainThreadDispatcher.Enqueue(() => { if (block?.CubeGrid != null) { - if (!_unCulledGrids.Contains(block.CubeGrid)) block.Visible = !shouldCullBlock; - if (shouldCullBlock) _culledBlocks[block.CubeGrid].Add(block); - else _culledBlocks[block.CubeGrid].Remove(block); + if (!_unCulledGrids.Contains(block.CubeGrid)) + block.Visible = !shouldCullBlock; + + if (shouldCullBlock) + _culledBlocks[block.CubeGrid].Add(block); + else + _culledBlocks[block.CubeGrid].Remove(block); } }); - if (recursive) + + if (recursive && !shouldCullBlock) { foreach (var slimBlockN in blockSlimNeighbors) { @@ -336,31 +352,55 @@ private void SetTransparencyAsync(SafeSlimBlockRef slimBlockRef, bool recursive { ThreadSafeLog.EnqueueMessage($"Exception in SetTransparencyAsync: {ex}"); } + stopwatch.Stop(); - _performanceMonitor.RecordAsyncOperation(stopwatch.ElapsedMilliseconds); + if (ThreadSafeLog.EnableDebugLogging) + { + _performanceMonitor.RecordAsyncOperation(stopwatch.ElapsedMilliseconds); + _performanceMonitor.RecordTasksProcessed(1); + } }); } private bool BlockEligibleForCulling(IMySlimBlock slimBlock, ref HashSet blockSlimNeighbors) { - if (blockSlimNeighbors == null) blockSlimNeighbors = new HashSet(); + if (blockSlimNeighbors == null) + blockSlimNeighbors = new HashSet(); + foreach (var blockPos in GetSurfacePositions(slimBlock)) { IMySlimBlock neighbor = slimBlock.CubeGrid.GetCubeBlock(blockPos); - if (neighbor != null) blockSlimNeighbors.Add(neighbor); + if (neighbor != null) + blockSlimNeighbors.Add(neighbor); } + IMyCubeBlock block = slimBlock?.FatBlock; - if (block == null) return false; + if (block == null) + return false; + List slimNeighborsContributor = new List(); - int blockFaceCount = GetBlockFaceCount(block); // Cache the result + int blockFaceCount = GetBlockFaceCount(block); + foreach (var slimNeighbor in blockSlimNeighbors) { int surroundingBlockCount = 0; foreach (Vector3I surfacePosition in GetSurfacePositions(slimNeighbor)) - if (slimNeighbor.CubeGrid.CubeExists(surfacePosition)) surroundingBlockCount++; - if (surroundingBlockCount != GetBlockFaceCount(slimNeighbor) && !ConnectsWithFullMountPoint(slimBlock, slimNeighbor)) return false; - if (slimNeighbor.FatBlock == null || !(slimNeighbor.FatBlock is IMyLightingBlock || slimNeighbor.BlockDefinition.Id.SubtypeName.Contains("Window"))) slimNeighborsContributor.Add(slimNeighbor); + if (slimNeighbor.CubeGrid.CubeExists(surfacePosition)) + surroundingBlockCount++; + + if (surroundingBlockCount != GetBlockFaceCount(slimNeighbor) && !ConnectsWithFullMountPoint(slimBlock, slimNeighbor)) + return false; + + if (slimNeighbor.FatBlock == null || + !(slimNeighbor.FatBlock is IMyDoor || + slimNeighbor.FatBlock is IMyAirtightHangarDoor || + slimNeighbor.FatBlock is IMyAirtightSlideDoor || + slimNeighbor.FatBlock is IMyAdvancedDoor || + slimNeighbor.FatBlock is IMyLightingBlock || + slimNeighbor.BlockDefinition.Id.SubtypeName.Contains("Window"))) + slimNeighborsContributor.Add(slimNeighbor); } + bool result = slimNeighborsContributor.Count == blockFaceCount; return result; } @@ -391,26 +431,40 @@ private int GetBlockFaceCount(IMyCubeBlock block) } private Vector3I[] _surfacePositions = new Vector3I[6]; - private Vector3I[] GetSurfacePositions(IMySlimBlock block) { Vector3I blockSize = Vector3I.Abs(block.Max - block.Min) + Vector3I.One; int faceCount = 2 * (blockSize.X * blockSize.Y + blockSize.Y * blockSize.Z + blockSize.Z * blockSize.X); - if (_surfacePositions.Length != faceCount) _surfacePositions = new Vector3I[faceCount]; + + if (_surfacePositions.Length != faceCount) + _surfacePositions = new Vector3I[faceCount]; + int idx = 0; - for (int x = -1; x <= blockSize.X; x++) + for (int dim = 0; dim < 3; dim++) { - for (int y = -1; y <= blockSize.Y; y++) + for (int i = 0; i < 2; i++) { - for (int z = -1; z <= blockSize.Z; z++) + Vector3I facePos = block.Min; + int faceDir = i == 0 ? -1 : blockSize[dim]; + facePos[dim] += faceDir; + + Vector3I faceSize = blockSize; + faceSize[dim] = 1; + + for (int x = 0; x < faceSize.X; x++) { - bool xLimit = (x == -1 || x == blockSize.X); - bool yLimit = (y == -1 || y == blockSize.Y); - bool zLimit = (z == -1 || z == blockSize.Z); - if ((!xLimit && yLimit ^ zLimit) || (xLimit && !(yLimit || zLimit))) _surfacePositions[idx++] = block.Min + new Vector3I(x, y, z); + for (int y = 0; y < faceSize.Y; y++) + { + for (int z = 0; z < faceSize.Z; z++) + { + Vector3I surfacePos = facePos + new Vector3I(x, y, z); + _surfacePositions[idx++] = surfacePos; + } + } } } } + return _surfacePositions; } } diff --git a/ShipyardsRevival/Data/Scripts/ShipyardMod/ShipyardItem.cs b/ShipyardsRevival/Data/Scripts/ShipyardMod/ShipyardItem.cs index 14c9fce..7f3a4c8 100644 --- a/ShipyardsRevival/Data/Scripts/ShipyardMod/ShipyardItem.cs +++ b/ShipyardsRevival/Data/Scripts/ShipyardMod/ShipyardItem.cs @@ -219,13 +219,20 @@ public void UpdatePowerUse(float addedPower = 0) if (YardType == ShipyardType.Disabled || YardType == ShipyardType.Invalid) { Utilities.Invoke(() => - { - foreach (IMyCubeBlock tool in Tools) - { - tool.GameLogic.GetAs().SetPowerUse(5 + addedPower); - Communication.SendToolPower(tool.EntityId, 5 + addedPower); - } - }); + { + if (Tools != null) + { + foreach (IMyCubeBlock tool in Tools) + { + var shipyardCorner = tool?.GameLogic?.GetAs(); + if (shipyardCorner != null) + { + shipyardCorner.SetPowerUse(5 + addedPower); + Communication.SendToolPower(tool.EntityId, 5 + addedPower); + } + } + } + }); } else { @@ -240,36 +247,44 @@ public void UpdatePowerUse(float addedPower = 0) } Utilities.Invoke(() => - { - foreach (IMyCubeBlock tool in Tools) - { - float power = 5; - //Logging.Instance.WriteDebug(String.Format("Tool[{0}] Base power usage [{1:F1} MW]", tool.DisplayNameText, power)); - int i = 0; - foreach (BlockTarget blockTarget in BlocksToProcess[tool.EntityId]) - { - if (blockTarget == null) - continue; - - float laserPower = 30 + 300 * multiplier * (float)blockTarget.ToolDist[tool.EntityId] / 200000; - //Logging.Instance.WriteDebug(String.Format("Tool[{0}] laser[{1}] distance[{2:F1}m] multiplier[{3:F1}x] additional power req [{4:F1} MW]", tool.DisplayNameText, i, Math.Sqrt(blockTarget.ToolDist[tool.EntityId]), multiplier, laserPower)); - power += laserPower; - i++; - } - - if (!StaticYard) - power *= 2; - - power += addedPower; - - //Logging.Instance.WriteDebug(String.Format("Tool[{0}] Total computed power [{1:F1} MW]", tool.DisplayNameText, power)); - var log = tool.GameLogic.GetAs(); - if (log == null) - continue; - tool.GameLogic.GetAs().SetPowerUse(power); - Communication.SendToolPower(tool.EntityId, power); - } - }); + { + if (Tools != null) + { + foreach (IMyCubeBlock tool in Tools) + { + float power = 5; + //Logging.Instance.WriteDebug(String.Format("Tool[{0}] Base power usage [{1:F1} MW]", tool.DisplayNameText, power)); + + if (BlocksToProcess != null && BlocksToProcess.ContainsKey(tool.EntityId)) + { + int i = 0; + foreach (BlockTarget blockTarget in BlocksToProcess[tool.EntityId]) + { + if (blockTarget == null) + continue; + + float laserPower = 30 + 300 * multiplier * (float)blockTarget.ToolDist[tool.EntityId] / 200000; + //Logging.Instance.WriteDebug(String.Format("Tool[{0}] laser[{1}] distance[{2:F1}m] multiplier[{3:F1}x] additional power req [{4:F1} MW]", tool.DisplayNameText, i, Math.Sqrt(blockTarget.ToolDist[tool.EntityId]), multiplier, laserPower)); + power += laserPower; + i++; + } + } + + if (!StaticYard) + power *= 2; + + power += addedPower; + + //Logging.Instance.WriteDebug(String.Format("Tool[{0}] Total computed power [{1:F1} MW]", tool.DisplayNameText, power)); + var shipyardCorner = tool?.GameLogic?.GetAs(); + if (shipyardCorner != null) + { + shipyardCorner.SetPowerUse(power); + Communication.SendToolPower(tool.EntityId, power); + } + } + } + }); } } diff --git a/ShipyardsRevival/Data/Scripts/concatrecursivelyandminify.bat b/ShipyardsRevival/Data/Scripts/concatrecursivelyandminify.bat new file mode 100644 index 0000000..9bbffa3 --- /dev/null +++ b/ShipyardsRevival/Data/Scripts/concatrecursivelyandminify.bat @@ -0,0 +1,60 @@ +@echo off +setlocal enabledelayedexpansion +chcp 65001 +REM ^^ this is to change the encoding to UTF-8, apparently + +echo Starting operation... + +set TEMP_OUTPUT=temp_concatenated.txt +set FINAL_OUTPUT=minified_output.txt +set /a COUNT=0 +set /a ERRORS=0 + +if exist "%TEMP_OUTPUT%" ( + echo Existing temporary file found. Deleting... + del "%TEMP_OUTPUT%" +) + +if exist "%FINAL_OUTPUT%" ( + echo Existing output file found. Deleting... + del "%FINAL_OUTPUT%" +) + +for /r %%i in (*.cs) do ( + echo Processing: "%%i" + type "%%i" >> "%TEMP_OUTPUT%" + if errorlevel 1 ( + echo Error processing "%%i". + set /a ERRORS+=1 + ) else ( + set /a COUNT+=1 + ) +) + +echo Concatenation completed. +echo Total files processed: %COUNT% +if %ERRORS% gtr 0 ( + echo There were %ERRORS% errors during the concatenation. +) else ( + echo No errors encountered during concatenation. +) + +echo Minifying concatenated file... +csmin < "%TEMP_OUTPUT%" > "%FINAL_OUTPUT%" +if errorlevel 1 ( + echo Error occurred during minification. + set /a ERRORS+=1 +) else ( + echo Minification completed successfully. +) + +echo Cleaning up temporary file... +del "%TEMP_OUTPUT%" + +echo Operation completed. +if %ERRORS% gtr 0 ( + echo There were %ERRORS% errors during the entire operation. +) else ( + echo No errors encountered during the entire operation. +) +pause \ No newline at end of file diff --git a/ShipyardsRevival/concat_recursive.bat b/ShipyardsRevival/concat_recursive.bat deleted file mode 100644 index b0c8f60..0000000 --- a/ShipyardsRevival/concat_recursive.bat +++ /dev/null @@ -1,33 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -echo Starting operation... - -set OUTPUT=newfile.txt -set /a COUNT=0 -set /a ERRORS=0 - -if exist "%OUTPUT%" ( - echo Existing output file found. Deleting... - del "%OUTPUT%" -) - -for /r %%i in (*.cs) do ( - echo Processing: "%%i" - type "%%i" >> "%OUTPUT%" - if errorlevel 1 ( - echo Error processing "%%i". - set /a ERRORS+=1 - ) else ( - set /a COUNT+=1 - ) -) - -echo Operation completed. -echo Total files processed: %COUNT% -if %ERRORS% gtr 0 ( - echo There were %ERRORS% errors during the operation. -) else ( - echo No errors encountered. -) -pause \ No newline at end of file diff --git a/ShipyardsRevival/newfile.txt b/ShipyardsRevival/newfile.txt deleted file mode 100644 index 3820038..0000000 --- a/ShipyardsRevival/newfile.txt +++ /dev/null @@ -1,6154 +0,0 @@ -using System.Collections.Generic; -using Sandbox.Definitions; -using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using ShipyardMod.Utility; -using VRage.Game.ModAPI; -using VRageMath; - -namespace ShipyardMod.ItemClasses -{ - public class BlockTarget - { - public BlockTarget(IMySlimBlock block, ShipyardItem item) - { - Block = block; - if (CubeGrid.Physics == null && Projector != null) - CenterDist = Vector3D.DistanceSquared(block.GetPosition(), Projector.GetPosition()); - else - CenterDist = Vector3D.DistanceSquared(block.GetPosition(), block.CubeGrid.Center()); - - ToolDist = new Dictionary(); - foreach (IMyCubeBlock tool in item.Tools) - ToolDist.Add(tool.EntityId, Vector3D.DistanceSquared(block.GetPosition(), tool.GetPosition())); - var blockDef = (MyCubeBlockDefinition)block.BlockDefinition; - //IntegrityPointsPerSec = MaxIntegrity / BuildTimeSeconds - //this is much, much faster than pulling the objectbuilder and getting the data from there. - BuildTime = blockDef.MaxIntegrity / blockDef.IntegrityPointsPerSec; - } - - - public IMyCubeGrid CubeGrid - { - get { return Block.CubeGrid; } - } - - public IMyProjector Projector - { - get { return ((MyCubeGrid)Block.CubeGrid).Projector; } - } - - public Vector3I GridPosition - { - get { return Block.Position; } - } - - public bool CanBuild - { - get - { - if (CubeGrid.Physics != null) - return true; - - return Projector?.CanBuild(Block, false) == BuildCheckResult.OK; - } - } - - public IMySlimBlock Block { get; private set; } - public float BuildTime { get; } - public double CenterDist { get; } - public Dictionary ToolDist { get; } - - public void UpdateAfterBuild() - { - Vector3D pos = Block.GetPosition(); - IMyCubeGrid grid = Projector.CubeGrid; - Vector3I gridPos = grid.WorldToGridInteger(pos); - IMySlimBlock newBlock = grid.GetCubeBlock(gridPos); - if (newBlock != null) - Block = newBlock; - } - } -}using VRageMath; - -namespace ShipyardMod.ItemClasses -{ - public class BoxItem - { - public long GridId; - public Vector3D LastPos; - public LineItem[] Lines; - public uint PackedColor; - } -}using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Sandbox.Definitions; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.ProcessHandlers; -using ShipyardMod.Settings; -using SpaceEngineers.Game.ModAPI; -using VRage; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace ShipyardMod.Utility -{ - public class Communication - { - private const ushort MESSAGE_ID = 18597; - public static Dictionary> LineDict = new Dictionary>(); - public static HashSet FadeList = new HashSet(); - public static List ScanList = new List(); - public static string FullName = typeof(Communication).FullName; - - private static void Recieve(byte[] data) - { - try - { - var recieveBlock = Profiler.Start(FullName, nameof(Recieve)); - var messageId = (MessageTypeEnum)data[0]; - var newData = new byte[data.Length - 1]; - Array.Copy(data, 1, newData, 0, newData.Length); - //Logging.Instance.WriteDebug("Recieve: " + messageId); - - switch (messageId) - { - case MessageTypeEnum.ToolLine: - HandleToolLine(newData); - break; - - case MessageTypeEnum.NewYard: - HandleNewShipyard(newData); - break; - - case MessageTypeEnum.YardState: - HandleShipyardState(newData); - break; - - case MessageTypeEnum.ClientChat: - HandleClientChat(newData); - break; - - case MessageTypeEnum.ServerChat: - HandleServerChat(newData); - break; - - case MessageTypeEnum.ServerDialog: - HandleServerDialog(newData); - break; - - case MessageTypeEnum.ClearLine: - HandleClearLine(newData); - break; - - case MessageTypeEnum.ShipyardSettings: - HandleShipyardSettings(newData); - break; - - case MessageTypeEnum.YardCommand: - HandleYardCommand(newData); - break; - - case MessageTypeEnum.ButtonAction: - HandleButtonAction(newData); - break; - - case MessageTypeEnum.RequestYard: - HandleYardRequest(newData); - break; - - case MessageTypeEnum.ToolPower: - HandleToolPower(newData); - break; - - case MessageTypeEnum.ShipyardCount: - HandleYardCount(newData); - break; - - case MessageTypeEnum.CustomInfo: - HandleCustomInfo(newData); - break; - } - recieveBlock.End(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine("Recieve(): " + ex); - } - } - - #region Comms Structs - - //(ノ◕ヮ◕)ノ*:・゚✧ ABSTRACTION! (ノ◕ヮ◕)ノ*:・゚✧ - public enum MessageTypeEnum : byte - { - ToolLine, - NewYard, - YardState, - ClientChat, - ServerChat, - ServerDialog, - ClearLine, - ShipyardSettings, - YardCommand, - ButtonAction, - RequestYard, - ToolPower, - ShipyardCount, - CustomInfo - } - - public struct ToolLineStruct - { - public long ToolId; - public long GridId; - public SerializableVector3I BlockPos; - public uint PackedColor; - public bool Pulse; - public byte EmitterIndex; - } - - public struct YardStruct - { - public long GridId; - public long[] ToolIds; - public ShipyardType YardType; - public long ButtonId; - } - - public struct DialogStruct - { - public string Title; - public string Subtitle; - public string Message; - public string ButtonText; - } - - #endregion - - #region Send Methods - - public static void SendLine(ToolLineStruct toolLine, Vector3D target) - { - string messageString = MyAPIGateway.Utilities.SerializeToXML(toolLine); - byte[] data = Encoding.UTF8.GetBytes(messageString); - SendMessageToNearby(target, 2000, MessageTypeEnum.ToolLine, data); - } - - public static void ClearLine(long toolId, int index) - { - var data = new byte[sizeof(long) + 1]; - data[0] = (byte)index; - BitConverter.GetBytes(toolId).CopyTo(data, 1); - SendMessageToClients(MessageTypeEnum.ClearLine, data); - } - - public static void SendYardState(ShipyardItem item) - { - var data = new byte[sizeof(long) + 1]; - BitConverter.GetBytes(item.EntityId).CopyTo(data, 0); - data[sizeof(long)] = (byte)item.YardType; - - SendMessageToClients(MessageTypeEnum.YardState, data); - } - - public static void SendNewYard(ShipyardItem item, ulong steamId = 0) - { - Logging.Instance.WriteLine("Sent Yard"); - var newYard = new YardStruct - { - GridId = item.EntityId, - ToolIds = item.Tools.Select(x => x.EntityId).ToArray(), - YardType = item.YardType - }; - - if (item.Menu?.Buttons != null) - { - Logging.Instance.WriteLine("Button ID: " + item.Menu.Buttons.EntityId); - newYard.ButtonId = item.Menu.Buttons.EntityId; - } - - string message = MyAPIGateway.Utilities.SerializeToXML(newYard); - byte[] data = Encoding.UTF8.GetBytes(message); - - if (steamId == 0) - SendMessageToClients(MessageTypeEnum.NewYard, data); - else - SendMessageTo(MessageTypeEnum.NewYard, data, steamId); - } - - public static void SendMessageToNearby(Vector3D target, double distance, MessageTypeEnum messageId, byte[] data) - { - var players = new List(); - MyAPIGateway.Players.GetPlayers(players); - double distSq = distance * distance; - foreach (IMyPlayer player in players) - { - //compare squared values for M A X I M U M S P E E D - if (Vector3D.DistanceSquared(player.GetPosition(), target) <= distSq) - SendMessageTo(messageId, data, player.SteamUserId); - } - } - - //SendMessageToOthers is apparently bugged - public static void SendMessageToClients(MessageTypeEnum messageId, byte[] data, bool skipLocal = false) - { - var players = new List(); - MyAPIGateway.Players.GetPlayers(players); - foreach (IMyPlayer player in players) - { - if (skipLocal && player == MyAPIGateway.Session.Player) - continue; - - SendMessageTo(messageId, data, player.SteamUserId); - } - } - - public static void SendMessageToServer(MessageTypeEnum messageId, byte[] data) - { - var newData = new byte[data.Length + 1]; - newData[0] = (byte)messageId; - data.CopyTo(newData, 1); - - Utilities.Invoke(() => MyAPIGateway.Multiplayer.SendMessageToServer(MESSAGE_ID, newData)); - } - - public static void SendMessageTo(MessageTypeEnum messageId, byte[] data, ulong steamId) - { - var newData = new byte[data.Length + 1]; - newData[0] = (byte)messageId; - data.CopyTo(newData, 1); - - Utilities.Invoke(() => MyAPIGateway.Multiplayer.SendMessageTo(MESSAGE_ID, newData, steamId)); - } - - public static void SendShipyardSettings(long entityId, YardSettingsStruct settings) - { - string message = MyAPIGateway.Utilities.SerializeToXML(settings); - byte[] messageBytes = Encoding.UTF8.GetBytes(message); - - var data = new byte[sizeof(long) + messageBytes.Length]; - - BitConverter.GetBytes(entityId).CopyTo(data, 0); - messageBytes.CopyTo(data, sizeof(long)); - - SendMessageToServer(MessageTypeEnum.ShipyardSettings, data); - } - - public static void SendPrivateInfo(ulong steamId, string message) - { - byte[] data = Encoding.ASCII.GetBytes(message); - - SendMessageTo(MessageTypeEnum.ServerChat, data, steamId); - } - - public static void SendDialog(ulong steamId, string title, string subtitle, string message, string button = "close") - { - var msg = new DialogStruct - { - Title = title, - Subtitle = subtitle, - Message = message, - ButtonText = button - }; - string serialized = MyAPIGateway.Utilities.SerializeToXML(msg); - byte[] data = Encoding.ASCII.GetBytes(serialized); - - SendMessageTo(MessageTypeEnum.ServerDialog, data, steamId); - } - - public static void SendYardCommand(long yardId, ShipyardType type) - { - var data = new byte[sizeof(long) + 1]; - BitConverter.GetBytes(yardId).CopyTo(data, 0); - data[sizeof(long)] = (byte)type; - - SendMessageToServer(MessageTypeEnum.YardCommand, data); - } - - private static DateTime _lastButtonSend = DateTime.Now; - - public static void SendButtonAction(long yardId, int index) - { - if (DateTime.Now - _lastButtonSend < TimeSpan.FromMilliseconds(100)) - return; - - _lastButtonSend = DateTime.Now; - var data = new byte[sizeof(long) + 1]; - BitConverter.GetBytes(yardId).CopyTo(data, 0); - data[sizeof(long)] = (byte)index; - SendMessageToServer(MessageTypeEnum.ButtonAction, data); - } - - public static void RequestShipyards() - { - byte[] data = BitConverter.GetBytes(MyAPIGateway.Session.Player.SteamUserId); - SendMessageToServer(MessageTypeEnum.RequestYard, data); - } - - public static void SendToolPower(long blockId, float power) - { - var data = new byte[sizeof(long) + sizeof(float)]; - BitConverter.GetBytes(blockId).CopyTo(data, 0); - BitConverter.GetBytes(power).CopyTo(data, sizeof(long)); - - SendMessageToClients(MessageTypeEnum.ToolPower, data); - } - - public static void SendYardCount() - { - byte[] data = BitConverter.GetBytes(ProcessShipyardDetection.ShipyardsList.Count); - - SendMessageToClients(MessageTypeEnum.ShipyardCount, data); - } - - public static void SendCustomInfo(long entityId, string info) - { - byte[] message = Encoding.UTF8.GetBytes(info); - var data = new byte[message.Length + sizeof(long)]; - BitConverter.GetBytes(entityId).CopyTo(data, 0); - Array.Copy(message, 0, data, sizeof(long), message.Length); - - SendMessageToClients(MessageTypeEnum.CustomInfo, data); - } - - #endregion - - #region Receive Methods - - private static void HandleToolPower(byte[] data) - { - long blockId = BitConverter.ToInt64(data, 0); - float power = BitConverter.ToSingle(data, sizeof(long)); - - IMyEntity entity; - if (!MyAPIGateway.Entities.TryGetEntityById(blockId, out entity)) - return; - - ((IMyCollector)entity).GameLogic.GetAs().SetPowerUse(power); - } - - private static void HandleToolLine(byte[] data) - { - string message = Encoding.UTF8.GetString(data); - var item = MyAPIGateway.Utilities.SerializeFromXML(message); - - IMyEntity toolEntity; - IMyEntity gridEntity; - - MyAPIGateway.Entities.TryGetEntityById(item.ToolId, out toolEntity); - MyAPIGateway.Entities.TryGetEntityById(item.GridId, out gridEntity); - - var tool = toolEntity as IMyCubeBlock; - var grid = gridEntity as IMyCubeGrid; - if (grid == null || tool == null) - return; - - var newLine = new LineItem - { - Start = MathUtility.CalculateEmitterOffset(tool, item.EmitterIndex), - End = grid.GridIntegerToWorld(item.BlockPos), - Color = new Color(item.PackedColor).ToVector4(), - Pulse = item.Pulse, - PulseVal = 0, - Index = item.EmitterIndex, - EmitterBlock = tool, - TargetGrid = grid, - TargetBlock = item.BlockPos, - }; - - if (newLine.Color == Color.OrangeRed.ToVector4()) - { - newLine.Color.W = 0.5f; - newLine.LinePackets = new PacketManager(newLine.End, newLine.Start, Color.Yellow.ToVector4(), invert: true); - } - else if (newLine.Color == Color.DarkCyan.ToVector4()) - newLine.LinePackets = new PacketManager(newLine.Start, newLine.End, Color.CadetBlue.ToVector4()); - else - newLine.LinePackets = null; - - if (LineDict.ContainsKey(item.ToolId)) - { - LineItem oldLine = null; - foreach (LineItem line in LineDict[item.ToolId]) - if (line.Index == item.EmitterIndex) - oldLine = line; - - if (oldLine == null) - { - LineDict[item.ToolId].Add(newLine); - return; - } - - if (oldLine.Color == Color.Purple.ToVector4() && item.ToolId != 0) - { - //if the old line is pulsing, and the new one is set to pulse, leave it alone so we don't reset the pulse cycle - if (newLine.Color == Color.Purple.ToVector4() && (oldLine.End == newLine.End)) - return; - - FadeList.Add(oldLine); - LineDict[item.ToolId].Remove(oldLine); - LineDict[item.ToolId].Add(newLine); - return; - } - - if (oldLine.End == newLine.End) - return; - - LineDict[item.ToolId].Remove(oldLine); - - if (newLine.Start != Vector3D.Zero) - LineDict[item.ToolId].Add(newLine); - - if (LineDict[item.ToolId].Count == 0) - LineDict.Remove(item.ToolId); - - FadeList.Add(oldLine); - } - else - LineDict[item.ToolId] = new List(3) {newLine}; - } - - private static void HandleNewShipyard(byte[] data) - { - Logging.Instance.WriteLine("ReceivedYard"); - string message = Encoding.UTF8.GetString(data); - var yardStruct = MyAPIGateway.Utilities.SerializeFromXML(message); - - //the server has already verified this shipyard. Don't question it, just make the shipyard item - if (yardStruct.ToolIds.Length != 8) - return; - - IMyEntity outEntity; - - if (!MyAPIGateway.Entities.TryGetEntityById(yardStruct.GridId, out outEntity)) - return; - - var yardGrid = outEntity as IMyCubeGrid; - if (yardGrid == null) - return; - - var points = new List(8); - var tools = new List(8); - foreach (long toolId in yardStruct.ToolIds) - { - IMyEntity entity; - if (!MyAPIGateway.Entities.TryGetEntityById(toolId, out entity)) - return; - - var block = entity as IMyCubeBlock; - if (block == null) - return; - - tools.Add(block); - points.Add(block.GetPosition()); - } - - MyOrientedBoundingBoxD yardBox = MathUtility.CreateOrientedBoundingBox(yardGrid, points, 2.5); - - var yardItem = new ShipyardItem(yardBox, tools.ToArray(), yardStruct.YardType, yardGrid); - - if (MyAPIGateway.Entities.TryGetEntityById(yardStruct.ButtonId, out outEntity)) - { - Logging.Instance.WriteLine("Bind Buttons"); - var buttons = (IMyButtonPanel)outEntity; - buttons.ButtonPressed += yardItem.HandleButtonPressed; - var blockDef = (MyButtonPanelDefinition)MyDefinitionManager.Static.GetCubeBlockDefinition(buttons.BlockDefinition); - for (int i = 1; i <= 4; i ++) - { - var c = blockDef.ButtonColors[i % blockDef.ButtonColors.Length]; - buttons.SetEmissiveParts($"Emissive{i}", new Color(c.X, c.Y, c.Z), c.W); - } - buttons.SetCustomButtonName(0, "Exit"); - buttons.SetCustomButtonName(1, "Up"); - buttons.SetCustomButtonName(2, "Down"); - buttons.SetCustomButtonName(3, "Select"); - //buttons.SetEmissiveParts("Emissive1", blockDef.ButtonColors[1 % blockDef.ButtonColors.Length], 1); - //buttons.SetEmissiveParts("Emissive2", blockDef.ButtonColors[2 % blockDef.ButtonColors.Length], 1); - //buttons.SetEmissiveParts("Emissive3", blockDef.ButtonColors[3 % blockDef.ButtonColors.Length], 1); - //buttons.SetEmissiveParts("Emissive4", blockDef.ButtonColors[4 % blockDef.ButtonColors.Length], 1); - } - - foreach (IMyCubeBlock tool in yardItem.Tools) - { - var corner = ((IMyCollector)tool).GameLogic.GetAs(); - corner.Shipyard = yardItem; - //tool.SetEmissiveParts("Emissive1", Color.Yellow, 0f); - } - - yardItem.UpdateMaxPowerUse(); - yardItem.Settings = ShipyardSettings.Instance.GetYardSettings(yardItem.EntityId); - ProcessLocalYards.LocalYards.Add(yardItem); - } - - private static void HandleShipyardState(byte[] data) - { - long gridId = BitConverter.ToInt64(data, 0); - var type = (ShipyardType)data.Last(); - - Logging.Instance.WriteDebug("Got yard state: " + type); - Logging.Instance.WriteDebug($"Details: {gridId} {ProcessLocalYards.LocalYards.Count} [{string.Join(" ", data)}]"); - - foreach (ShipyardItem yard in ProcessLocalYards.LocalYards) - { - if (yard.EntityId != gridId) - continue; - - yard.YardType = type; - Logging.Instance.WriteLine(type.ToString()); - - foreach (IMyCubeBlock yardTool in yard.Tools) - yardTool?.GameLogic?.GetAs()?.UpdateVisuals(); - - switch (type) - { - case ShipyardType.Disabled: - case ShipyardType.Invalid: - yard.Disable(false); - - foreach (IMyCubeBlock tool in yard.Tools) - { - if (LineDict.ContainsKey(tool.EntityId)) - { - foreach (LineItem line in LineDict[tool.EntityId]) - FadeList.Add(line); - LineDict.Remove(tool.EntityId); - } - //tool.SetEmissiveParts("Emissive0", Color.White, 0.5f); - } - break; - case ShipyardType.Scanning: - ScanList.Add(new ScanAnimation(yard)); - //foreach(var tool in yard.Tools) - // tool.SetEmissiveParts("Emissive0", Color.Green, 0.5f); - break; - //case ShipyardType.Weld: - //foreach (var tool in yard.Tools) - // tool.SetEmissiveParts("Emissive0", Color.DarkCyan, 0.5f); - //break; - //case ShipyardType.Grind: - //foreach (var tool in yard.Tools) - // tool.SetEmissiveParts("Emissive0", Color.OrangeRed, 0.5f); - //break; - //default: - // throw new ArgumentOutOfRangeException(); - } - - yard.UpdateMaxPowerUse(); - - } - } - - private static void HandleClientChat(byte[] data) - { - ulong remoteSteamId = BitConverter.ToUInt64(data, 0); - string command = Encoding.UTF8.GetString(data, sizeof(ulong), data.Length - sizeof(ulong)); - if (!command.StartsWith("/shipyard")) - return; - - Logging.Instance.WriteLine("Received chat: " + command); - if (MyAPIGateway.Session.IsUserAdmin(remoteSteamId)) - { - if (command.Equals("/shipyard debug on")) - { - Logging.Instance.WriteLine("Debug turned on"); - ShipyardCore.Debug = true; - } - else if (command.Equals("/shipyard debug off")) - { - Logging.Instance.WriteLine("Debug turned off"); - ShipyardCore.Debug = false; - } - } - /* - foreach (ChatHandlerBase handler in ChatHandlers) - { - if (handler.CanHandle(remoteSteamId, command)) - { - string[] splits = command.Replace(handler.CommandText(), "").Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries); - handler.Handle(remoteSteamId, splits); - return; - } - } - */ - } - - private static void HandleServerChat(byte[] data) - { - if (MyAPIGateway.Session.Player == null) - return; - - string message = Encoding.ASCII.GetString(data); - - MyAPIGateway.Utilities.ShowMessage("Shipyard Overlord", message); - } - - private static void HandleServerDialog(byte[] data) - { - string serialized = Encoding.ASCII.GetString(data); - var msg = MyAPIGateway.Utilities.SerializeFromXML(serialized); - - MyAPIGateway.Utilities.ShowMissionScreen(msg.Title, null, msg.Subtitle, msg.Message.Replace("|", "\r\n"), null, msg.ButtonText); - } - - private static void HandleClearLine(byte[] bytes) - { - byte index = bytes[0]; - long toolId = BitConverter.ToInt64(bytes, 1); - - if (!LineDict.ContainsKey(toolId)) - return; - - List entry = LineDict[toolId]; - var toRemove = new HashSet(); - foreach (LineItem line in entry) - { - if (line.Index != index) - continue; - - line.FadeVal = 1; - FadeList.Add(line); - toRemove.Add(line); - } - - if (entry.Count == 0) - LineDict.Remove(toolId); - - foreach (LineItem line in toRemove) - LineDict[toolId].Remove(line); - } - - private static void HandleShipyardSettings(byte[] data) - { - long entityId = BitConverter.ToInt64(data, 0); - string message = Encoding.UTF8.GetString(data, sizeof(long), data.Length - sizeof(long)); - var settings = MyAPIGateway.Utilities.SerializeFromXML(message); - - bool found = false; - - Logging.Instance.WriteDebug($"Received shipyard settings:\r\n" + - $"\t{settings.EntityId}\r\n" + - $"\t{settings.BeamCount}\r\n" + - $"\t{settings.GuideEnabled}\r\n" + - $"\t{settings.GrindMultiplier}\r\n" + - $"\t{settings.WeldMultiplier}"); - - foreach (ShipyardItem yard in ProcessShipyardDetection.ShipyardsList) - { - if (yard.EntityId != entityId) - continue; - - yard.Settings = settings; - yard.UpdateMaxPowerUse(); // BeamCount and Multiplier affect our maxpower calculation - ShipyardSettings.Instance.SetYardSettings(yard.EntityId, settings); - found = true; - break; - } - - foreach (ShipyardItem yard in ProcessLocalYards.LocalYards) - { - if (yard.EntityId != entityId) - continue; - - yard.Settings = settings; - yard.UpdateMaxPowerUse(); // BeamCount and Multiplier affect our maxpower calculation - ShipyardSettings.Instance.SetYardSettings(yard.EntityId, settings); - - foreach (IMyCubeBlock tool in yard.Tools) - { - tool.GameLogic.GetAs().UpdateVisuals(); - } - - found = true; - break; - } - - if (found && MyAPIGateway.Multiplayer.IsServer) - { - ShipyardSettings.Instance.Save(); - //pass true to skip the local player - //on player-hosted MP we can get caught in an infinite loop if we don't - SendMessageToClients(MessageTypeEnum.ShipyardSettings, data, true); - } - } - - private static void HandleYardCommand(byte[] data) - { - long yardId = BitConverter.ToInt64(data, 0); - var type = (ShipyardType)data.Last(); - Logging.Instance.WriteDebug($"Received Yard Command: {type} for {yardId}"); - - foreach (ShipyardItem yard in ProcessShipyardDetection.ShipyardsList) - { - if (yard.EntityId != yardId) - continue; - - if (type == ShipyardType.Disabled || type == ShipyardType.Invalid) - yard.Disable(); - else - yard.Init(type); - - break; - } - } - - private static void HandleButtonAction(byte[] data) - { - long yardId = BitConverter.ToInt64(data, 0); - byte index = data.Last(); - - foreach (ShipyardItem yard in ProcessShipyardDetection.ShipyardsList) - { - if (yard.EntityId == yardId && yard.Menu != null) - { - yard.Menu.ButtonPanelHandler(index); - return; - } - } - } - - private static void HandleYardRequest(byte[] data) - { - ulong steamId = BitConverter.ToUInt64(data, 0); - - Logging.Instance.WriteLine("Recieved shipyard request from " + steamId); - - if (!ProcessShipyardDetection.ShipyardsList.Any()) - return; - - foreach (ShipyardItem yard in ProcessShipyardDetection.ShipyardsList) - SendNewYard(yard, steamId); - } - - private static void HandleYardCount(byte[] data) - { - if (MyAPIGateway.Multiplayer.IsServer || MyAPIGateway.Session.Player?.Controller?.ControlledEntity?.Entity == null) - return; - - int count = BitConverter.ToInt32(data, 0); - - if (ProcessLocalYards.LocalYards.Count != count) - { - RequestShipyards(); - } - } - - private static void HandleCustomInfo(byte[] data) - { - long entityId = BitConverter.ToInt64(data, 0); - string info = Encoding.UTF8.GetString(data, sizeof(long), data.Length - sizeof(long)); - IMyEntity outEntity; - if (!MyAPIGateway.Entities.TryGetEntityById(entityId, out outEntity)) - return; - - IMyCollector col = outEntity as IMyCollector; - - if (col == null) - return; - - col.GameLogic.GetAs()?.SetInfo(info); - } - - #endregion - - #region Handler Setup - - public static void RegisterHandlers() - { - MyAPIGateway.Multiplayer.RegisterMessageHandler(MESSAGE_ID, Recieve); - } - - public static void UnregisterHandlers() - { - MyAPIGateway.Multiplayer.UnregisterMessageHandler(MESSAGE_ID, Recieve); - } - - #endregion - } -}using System.Collections.Generic; -using System.Linq; -using Sandbox.Game; -using Sandbox.Game.Entities; -using Sandbox.Game.Entities.Cube; -using Sandbox.ModAPI; -using VRage.Collections; -using VRage.Game; -using VRage.Game.Entity; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace ShipyardMod.Utility -{ - public static class Extensions - { - public static void Stop(this IMyEntity entity) - { - if (entity?.Physics == null || entity.Closed) - return; - - Utilities.Invoke(() => - { - if (entity.Physics == null || entity.Closed) - return; - entity.Physics.SetSpeeds(Vector3.Zero, Vector3.Zero); - /* - entity.Physics.Clear(); - if (!Vector3.IsZero(entity.Physics.LinearAcceleration) || !Vector3.IsZero(entity.Physics.AngularAcceleration)) - { - entity.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_IMPULSE_AND_WORLD_ANGULAR_IMPULSE, - Vector3.Negate(entity.Physics.LinearAcceleration) * entity.Physics.Mass, - entity.Center(), - Vector3.Negate(entity.Physics.AngularAcceleration) * entity.Physics.Mass); - } - */ - }); - } - - public static Vector3D Center(this IMyEntity ent) - { - return ent.WorldAABB.Center; - } - - public static Vector3D GetPosition(this IMySlimBlock block) - { - return block.CubeGrid.GridIntegerToWorld(block.Position); - } - - public static long SlimId(this IMySlimBlock block) - { - return block.CubeGrid.EntityId.GetHashCode() + block.Position.GetHashCode(); - } - - public static bool Closed(this IMySlimBlock block) - { - return block.CubeGrid?.GetCubeBlock(block.Position) == null; - } - - public static IMySlimBlock ProjectionResult(this IMySlimBlock block) - { - IMyProjector projector = block.CubeGrid.Projector(); - if (projector == null) - return null; - - Vector3D pos = block.GetPosition(); - IMyCubeGrid grid = projector.CubeGrid; - Vector3I gridPos = grid.WorldToGridInteger(pos); - return grid.GetCubeBlock(gridPos); - } - - public static IMyProjector Projector(this IMyCubeGrid grid) - { - return ((MyCubeGrid)grid).Projector; - } - - public static float BuildPercent(this IMySlimBlock block) - { - return block.Integrity / block.MaxIntegrity; - } - - public static bool PullAny(this MyInventory inventory, HashSet sourceInventories, string component, int count) - { - return PullAny(inventory, sourceInventories, new Dictionary {{component, count}}); - } - - public static bool PullAny(this MyInventory inventory, HashSet sourceInventories, Dictionary toPull) - { - bool result = false; - foreach (KeyValuePair entry in toPull) - { - int remainingAmount = entry.Value; - //Logging.Instance.WriteDebug(entry.Key + entry.Value); - foreach (IMyTerminalBlock block in sourceInventories) - { - if (block == null || block.Closed) - continue; - - MyInventory sourceInventory; - //get the output inventory for production blocks - if (((MyEntity)block).InventoryCount > 1) - sourceInventory = ((MyEntity)block).GetInventory(1); - else - sourceInventory = ((MyEntity)block).GetInventory(); - - List sourceItems = sourceInventory.GetItems(); - if (sourceItems.Count == 0) - continue; - - var toMove = new List>(); - foreach (MyPhysicalInventoryItem item in sourceItems) - { - if (item.Content.SubtypeName == entry.Key) - { - if (item.Amount <= 0) //KEEEN - continue; - - if (item.Amount >= remainingAmount) - { - toMove.Add(new KeyValuePair(item, remainingAmount)); - remainingAmount = 0; - result = true; - } - else - { - remainingAmount -= (int)item.Amount; - toMove.Add(new KeyValuePair(item, (int)item.Amount)); - result = true; - } - } - } - - foreach (KeyValuePair itemEntry in toMove) - { - if (inventory.ComputeAmountThatFits(itemEntry.Key.Content.GetId()) < itemEntry.Value) - return false; - - sourceInventory.Remove(itemEntry.Key, itemEntry.Value); - inventory.Add(itemEntry.Key, itemEntry.Value); - } - - if (remainingAmount == 0) - break; - } - } - - return result; - } - - public static bool IsInVoxels(this IMyCubeGrid grid) - { - /* - if (MyAPIGateway.Session.SessionSettings.StationVoxelSupport) - return grid.IsStatic; - - - List blocks = new List(); - List entities = new List(); - var box = grid.PositionComp.WorldAABB; - Utilities.InvokeBlocking(() => - { - entities = MyAPIGateway.Entities.GetTopMostEntitiesInBox(ref box); - grid.GetBlocks(blocks); - }); - - var voxels = entities.Where(e => e is IMyVoxelBase).ToList(); - - if (!voxels.Any()) - return false; - - foreach (var block in blocks) - { - BoundingBoxD blockBox; - block.GetWorldBoundingBox(out blockBox); - var cubeSize = block.CubeGrid.GridSize; - BoundingBoxD localAAABB = new BoundingBoxD(cubeSize * ((Vector3D)block.Position - 0.5), cubeSize * ((Vector3D)block.Max + 0.5)); - var gridWorldMatrix = block.CubeGrid.WorldMatrix; - foreach (var map in voxels) - { - if (((IMyVoxelBase)map).IsAnyAabbCornerInside(gridWorldMatrix, localAAABB)) - { - return true; - } - } - } - */ - return true; - - } - } -}using System; -using System.Collections.Generic; -using System.Text; -using Sandbox.Definitions; -using Sandbox.ModAPI; -using SpaceEngineers.Game.ModAPI; -using VRage.Game.GUI.TextPanel; - -//Jimmacle provided the original LCD classes. These are heavily modified :P - -namespace ShipyardMod.ItemClasses -{ - public class LCDMenu - { - public IMyButtonPanel Buttons; - private MenuItem currentItem; - public IMyTextPanel Panel; - private int selectedItemIndex; - - public LCDMenu() - { - Root = new MenuItem("Root", null); - Root.root = this; - SetCurrentItem(Root); - } - - public MenuItem Root { get; set; } - - public void BindButtonPanel(IMyButtonPanel btnpnl) - { - //if (Buttons != null) - //{ - // Buttons.ButtonPressed -= ButtonPanelHandler; - //} - Buttons = btnpnl; - //Buttons.ButtonPressed += ButtonPanelHandler; - var blockDef = (MyButtonPanelDefinition)MyDefinitionManager.Static.GetCubeBlockDefinition(Buttons.BlockDefinition); - Buttons.SetEmissiveParts("Emissive1", blockDef.ButtonColors[1 % blockDef.ButtonColors.Length], 1); - Buttons.SetEmissiveParts("Emissive2", blockDef.ButtonColors[2 % blockDef.ButtonColors.Length], 1); - Buttons.SetEmissiveParts("Emissive3", blockDef.ButtonColors[3 % blockDef.ButtonColors.Length], 1); - Buttons.SetEmissiveParts("Emissive4", blockDef.ButtonColors[4 % blockDef.ButtonColors.Length], 1); - } - - public void BindLCD(IMyTextPanel txtpnl) - { - if (Panel is IMyTextSurfaceProvider && Panel != null) - { - Panel.WriteText("MENU UNBOUND"); - } - Panel = txtpnl; - Panel.ContentType = ContentType.TEXT_AND_IMAGE; - UpdateLCD(); - } - - public void SetCurrentItem(MenuItem item) - { - currentItem = item; - selectedItemIndex = 0; - } - - public void ButtonPanelHandler(int button) - { - switch (button) - { - case 0: - MenuActions.UpLevel(this, currentItem.Items[selectedItemIndex]); - break; - case 1: - if (selectedItemIndex > 0) - { - selectedItemIndex--; - } - else - selectedItemIndex = currentItem.Items.Count - 1; - break; - case 2: - if (selectedItemIndex < currentItem.Items.Count - 1) - { - selectedItemIndex++; - } - else - selectedItemIndex = 0; - break; - case 3: - currentItem.Items[selectedItemIndex].Invoke(); - break; - } - UpdateLCD(); - } - - public void UpdateLCD() - { - if (Panel != null) - { - var sb = new StringBuilder(); - - sb.Append(currentItem.UpdateDesc() ?? currentItem.Description); - for (int i = 0; i < currentItem.Items.Count; i++) - { - if (i == selectedItemIndex) - { - sb.Append("[ " + currentItem.Items[i].Name + " ]" + '\n'); - } - else - { - sb.Append(" " + currentItem.Items[i].Name + '\n'); - } - } - string result = sb.ToString(); - //saves bandwidth and render time - if (Panel.GetText() == result) - return; - Panel.WriteText(result); - //Panel.ShowPrivateTextOnScreen(); - } - } - - public void WriteHugeString(string message) - { - Panel.WriteText(""); - int charcount = 4200; //KEEN - - for (int i = 0; i < message.Length * charcount; i++) - { - string substring = message.Substring(i * charcount, Math.Min(charcount, message.Length - i * charcount)); - Panel.WriteText(substring, true); - } - } - } - - public class MenuItem - { - public delegate string DescriptionAction(); - - public delegate void MenuAction(); - - public LCDMenu root; - - public MenuItem(string name, string desc, MenuAction action = null, DescriptionAction descAct = null) - { - Name = name; - Action = action; - Description = desc; - DescAction = descAct; - Items = new List(); - } - - public MenuAction Action { get; set; } - - public DescriptionAction DescAction { get; set; } - - public MenuItem Parent { get; private set; } - public List Items { get; set; } - - public string Name { get; } - public string Description { get; set; } - - public void Add(MenuItem child) - { - child.root = root; - child.Parent = this; - Items.Add(child); - } - - public void Invoke() - { - if (Action != null) - Action.Invoke(); - } - - public string UpdateDesc() - { - if (DescAction != null) - return DescAction.Invoke(); - return null; - } - } - - public static class MenuActions - { - public static void DownLevel(LCDMenu root, MenuItem item) - { - root.SetCurrentItem(item); - } - - public static void UpLevel(LCDMenu root, MenuItem item) - { - if (item.Parent.Parent != null) - { - root.SetCurrentItem(item.Parent.Parent); - } - } - } -}using VRage.Game.ModAPI; -using VRageMath; - -namespace ShipyardMod.ItemClasses -{ - public class LineItem - { - public Vector4 Color; - public bool Descend; - public Vector3D End; - public float FadeVal; - public byte Index; - public PacketManager LinePackets; - public bool Pulse; - public double PulseVal; - public Vector3D Start; - public IMyCubeBlock EmitterBlock; - public IMyCubeGrid TargetGrid; - public Vector3I TargetBlock; - - public LineItem(Vector3D start, Vector3D end) - { - Start = start; - End = end; - } - - public LineItem() - { - } - } -}using System; -using System.IO; -using System.Text; -using Sandbox.ModAPI; - -namespace ShipyardMod.Utility -{ - internal class Logging - { - private static Logging _instance; - - private static int lineCount; - private static bool init; - private static readonly string[] log = new string[10]; - private readonly StringBuilder _cache = new StringBuilder(); - private readonly TextWriter _writer; - private int _indent; - - public Logging(string logFile) - { - try - { - _writer = MyAPIGateway.Utilities.WriteFileInLocalStorage(logFile, typeof(Logging)); - _instance = this; - } - catch - { - } - } - - public static Logging Instance - { - get - { - if (MyAPIGateway.Utilities == null) - return null; - - if (_instance == null) - { - _instance = new Logging("ShipyardMod.log"); - } - - return _instance; - } - } - - public void IncreaseIndent() - { - _indent++; - } - - public void DecreaseIndent() - { - if (_indent > 0) - _indent--; - } - - public void WriteLine(string text) - { - try - { - if (_cache.Length > 0) - _writer.WriteLine(_cache); - - _cache.Clear(); - _cache.Append(DateTime.Now.ToString("[HH:mm:ss:ffff] ")); - for (int i = 0; i < _indent; i++) - _cache.Append("\t"); - - _writer.WriteLine(_cache.Append(text)); - _writer.Flush(); - _cache.Clear(); - } - catch - { - //logger failed, all hope is lost - } - } - - public void WriteDebug(string text) - { - if (ShipyardCore.Debug) - WriteLine(text); - } - - public void Debug_obj(string text) - { - if (!ShipyardCore.Debug) - return; - - WriteLine("\tDEBUG_OBJ: " + text); - - //server can't show objectives. probably. - if (MyAPIGateway.Session.Player == null) - return; - - //I'm the only one that needs to see this - if (MyAPIGateway.Session.Player.SteamUserId != 76561197996829390) - return; - - text = $"{DateTime.Now.ToString("[HH:mm:ss:ffff]")}: {text}"; - - if (!init) - { - init = true; - MyAPIGateway.Utilities.GetObjectiveLine().Title = "Shipyard debug"; - MyAPIGateway.Utilities.GetObjectiveLine().Objectives.Clear(); - MyAPIGateway.Utilities.GetObjectiveLine().Objectives.Add("Start"); - MyAPIGateway.Utilities.GetObjectiveLine().Show(); - } - if (lineCount > 9) - lineCount = 0; - log[lineCount] = text; - string[] oldLog = log; - for (int i = 0; i < 9; i++) - { - log[i] = oldLog[i + 1]; - } - log[9] = text; - - MyAPIGateway.Utilities.GetObjectiveLine().Objectives[0] = string.Join("\r\n", log); - lineCount++; - } - - public void Write(string text) - { - _cache.Append(text); - } - - - internal void Close() - { - if (_cache.Length > 0) - _writer.WriteLine(_cache); - - _writer.Flush(); - _writer.Close(); - } - } -}using System; -using System.Collections.Generic; -using System.Linq; -using ShipyardMod.ItemClasses; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace ShipyardMod.Utility -{ - public static class MathUtility - { - private static readonly Vector3D[] Offsets = - { - new Vector3D(0.8103569, 0.2971599, 0.7976569), - new Vector3D(0.81244801, -0.813483256, -0.312517739), - new Vector3D(-0.311901606, -0.81946053, 0.802108187) - }; - - private static readonly Vector3D[] SmallOffsets = - { - new Vector3D(-0.84227, 0.84227, 0.34794), - new Vector3D(0.34649, 0.84227, -0.84083), - new Vector3D(-0.84227, -0.34649, -0.84083), - }; - - /// - /// Determines if a list of 8 Vector3I defines a rectangular prism aligned to the grid - /// - /// - /// - public static bool ArePointsOrthogonal(List points) - { - //get a list of unique Z values - int[] zVals = points.Select(p => p.Z).Distinct().ToArray(); - - //we should only have two - if (zVals.Length != 2) - return false; - - //get a list of all points in the two Z planes - List zPlane0 = points.FindAll(p => p.Z == zVals[0]); - List zPlane1 = points.FindAll(p => p.Z == zVals[1]); - - //we should have four of each - if (zPlane1.Count != 4 || zPlane0.Count != 4) - return false; - - //make sure each vertex in the maxZ plane has the same X and Y as only one point in the minZ plane - foreach (Vector3I zMaxPoint in zPlane0) - { - if (zPlane1.Count(zMinPoint => zMinPoint.X == zMaxPoint.X && zMinPoint.Y == zMaxPoint.Y) != 1) - return false; - } - - return true; - } - - /// - /// Create an OBB that encloses a grid - /// - /// - /// - public static MyOrientedBoundingBoxD CreateOrientedBoundingBox(IMyCubeGrid grid) - { - //quaternion to rotate the box - Quaternion gridQuaternion = Quaternion.CreateFromForwardUp( - grid.WorldMatrix.Forward, - grid.WorldMatrix.Up); - - //get the width of blocks for this grid size - float blocksize = grid.GridSize; - - //get the halfextents of the grid, then multiply by block size to get world halfextents - //add one so the line sits on the outside edge of the block instead of the center - var halfExtents = new Vector3D( - (Math.Abs(grid.Max.X - grid.Min.X) + 1) * blocksize / 2, - (Math.Abs(grid.Max.Y - grid.Min.Y) + 1) * blocksize / 2, - (Math.Abs(grid.Max.Z - grid.Min.Z) + 1) * blocksize / 2); - - return new MyOrientedBoundingBoxD(grid.PositionComp.WorldAABB.Center, halfExtents, gridQuaternion); - } - - /// - /// Create an OBB from a list of 8 verticies and align it to a grid - /// - /// - /// - /// Allows you to expand or shrink the box by a given number of meters - /// - public static MyOrientedBoundingBoxD CreateOrientedBoundingBox(IMyCubeGrid grid, List verticies, double offset = 0) - { - //create the quaternion to rotate the box around - Quaternion yardQuaternion = Quaternion.CreateFromForwardUp( - grid.WorldMatrix.Forward, - grid.WorldMatrix.Up); - - //find the center of the volume - var yardCenter = new Vector3D(); - - foreach (Vector3D vertex in verticies) - yardCenter = Vector3D.Add(yardCenter, vertex); - - yardCenter = Vector3D.Divide(yardCenter, verticies.Count); - - //find the dimensions of the box. - - //convert verticies to grid coordinates to find adjoining neighbors - var gridVerticies = new List(verticies.Count); - - foreach (Vector3D vertext in verticies) - gridVerticies.Add(grid.WorldToGridInteger(vertext)); - - double xLength = 0d; - double yLength = 0d; - double zLength = 0d; - - //finds the length of each axis - for (int i = 1; i < gridVerticies.Count; ++i) - { - if (gridVerticies[0].Y == gridVerticies[i].Y && gridVerticies[0].Z == gridVerticies[i].Z) - { - xLength = Math.Abs(gridVerticies[0].X - gridVerticies[i].X) * grid.GridSize; - continue; - } - - if (gridVerticies[0].X == gridVerticies[i].X && gridVerticies[0].Z == gridVerticies[i].Z) - { - yLength = Math.Abs(gridVerticies[0].Y - gridVerticies[i].Y) * grid.GridSize; - continue; - } - - if (gridVerticies[0].X == gridVerticies[i].X && gridVerticies[0].Y == gridVerticies[i].Y) - { - zLength = Math.Abs(gridVerticies[0].Z - gridVerticies[i].Z) * grid.GridSize; - } - } - - var halfExtents = new Vector3D(offset + xLength / 2, offset + yLength / 2, offset + zLength / 2); - - //FINALLY we can make the bounding box - return new MyOrientedBoundingBoxD(yardCenter, halfExtents, yardQuaternion); - } - - public static Vector3D CalculateEmitterOffset(IMyCubeBlock tool, byte index) - { - if (tool.BlockDefinition.SubtypeId.EndsWith("_Large")) - return Vector3D.Transform(Offsets[index], tool.WorldMatrix); - else - return Vector3D.Transform(SmallOffsets[index], tool.WorldMatrix); - } - - /// - /// Calculates an array of LineItems that describes an oriented bounding box - /// - /// - /// - public static LineItem[] CalculateObbLines(MyOrientedBoundingBoxD obb) - { - // ZMax ZMin - // 0----1 4----5 - // | | | | - // | | | | - // 3----2 7----6 - - var corners = new Vector3D[8]; - obb.GetCorners(corners, 0); - var lines = new LineItem[12]; - - //ZMax face - lines[0] = new LineItem(corners[0], corners[1]); - lines[1] = new LineItem(corners[1], corners[2]); - lines[2] = new LineItem(corners[2], corners[3]); - lines[3] = new LineItem(corners[3], corners[0]); - - //ZMin face - lines[4] = new LineItem(corners[4], corners[5]); - lines[5] = new LineItem(corners[5], corners[6]); - lines[6] = new LineItem(corners[6], corners[7]); - lines[7] = new LineItem(corners[7], corners[4]); - - //vertical lines - lines[8] = new LineItem(corners[0], corners[4]); - lines[9] = new LineItem(corners[1], corners[5]); - lines[10] = new LineItem(corners[2], corners[6]); - lines[11] = new LineItem(corners[3], corners[7]); - - return lines; - } - - /// - /// Calculates an array of LineItems that describes a bounding box - /// - /// - /// - public static LineItem[] CalculateBoxLines(BoundingBoxD box) - { - // ZMax ZMin - // 0----1 4----5 - // | | | | - // | | | | - // 3----2 7----6 - - Vector3D[] corners = box.GetCorners(); - var lines = new LineItem[12]; - - //ZMax face - lines[0] = new LineItem(corners[0], corners[1]); - lines[1] = new LineItem(corners[1], corners[2]); - lines[2] = new LineItem(corners[2], corners[3]); - lines[3] = new LineItem(corners[3], corners[0]); - - //ZMin face - lines[4] = new LineItem(corners[4], corners[5]); - lines[5] = new LineItem(corners[5], corners[6]); - lines[6] = new LineItem(corners[6], corners[7]); - lines[7] = new LineItem(corners[7], corners[4]); - - //vertical lines - lines[8] = new LineItem(corners[0], corners[4]); - lines[9] = new LineItem(corners[1], corners[5]); - lines[10] = new LineItem(corners[2], corners[6]); - lines[11] = new LineItem(corners[3], corners[7]); - - return lines; - } - - /// - /// Iterates through Vector3I positions between two points in a straight line - /// - public struct Vector3ILineIterator - { - private readonly Vector3I _start; - private readonly Vector3I _end; - private readonly Vector3I _direction; - - public Vector3I Current; - - public Vector3ILineIterator(Vector3I start, Vector3I end) - { - if (start == end) - throw new ArgumentException("Start and end cannot be equal"); - - _start = start; - _end = end; - Current = start; - _direction = Vector3I.Clamp(end - start, -Vector3I.One, Vector3I.One); - - if (_direction.RectangularLength() > 1) - throw new ArgumentException("Start and end are not in a straight line"); - } - - public bool IsValid() - { - return (_end - _start).RectangularLength() >= (Current - _start).RectangularLength(); - } - - public void MoveNext() - { - Current += _direction; - } - } - - private static readonly double[] SqrtCache = new double[50000]; - - /// - /// Caching square root method - /// - /// - /// - public static double Sqrt(int val) - { - if (val < 0) - return double.NaN; - - if (val == 0) - return 0; - - if (val >= SqrtCache.Length) - return Math.Sqrt(val); - - if (SqrtCache[val] != default(double)) - return SqrtCache[val]; - - SqrtCache[val] = Math.Sqrt(val); - return SqrtCache[val]; - } - - public static double Sqrt(double val) - { - return Sqrt((int)val); - } - - /// - /// Raises a value to the one-half power using our cached sqrt method - /// - /// - /// - public static double Pow15(double val) - { - return val * Sqrt(val); - } - - public static double MatchShipVelocity(IMyEntity modify, IMyEntity dest, bool recoil) - { - // local velocity of dest - var velTarget = dest.Physics.GetVelocityAtPoint(modify.Physics.CenterOfMassWorld); - var distanceFromTargetCom = modify.Physics.CenterOfMassWorld - dest.Physics.CenterOfMassWorld; - - var accelLinear = dest.Physics.LinearAcceleration; - var omegaVector = dest.Physics.AngularVelocity + dest.Physics.AngularAcceleration * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS; - var omegaSquared = omegaVector.LengthSquared(); - // omega^2 * r == a - var accelRotational = omegaSquared * -distanceFromTargetCom; - var accelTarget = accelLinear + accelRotational; - - var velTargetNext = velTarget + accelTarget * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS; - var velModifyNext = modify.Physics.LinearVelocity;// + modify.Physics.LinearAcceleration * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS; - - var linearImpulse = modify.Physics.Mass * (velTargetNext - velModifyNext); - - // Angular matching. - // (dAA*dt + dAV) == (mAA*dt + mAV + tensorInverse*mAI) - var avelModifyNext = modify.Physics.AngularVelocity + modify.Physics.AngularAcceleration * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS; - var angularDV = omegaVector - avelModifyNext; - var angularImpulse = Vector3.Zero; - // var angularImpulse = Vector3.TransformNormal(angularDV, modify.Physics.RigidBody.InertiaTensor); not accessible :/ - - // based on the large grid, small ion thruster. - const double wattsPerNewton = (3.36e6 / 288000); - // based on the large grid gyro - const double wattsPerNewtonMeter = (0.00003 / 3.36e7); - // (W/N) * (N*s) + (W/(N*m))*(N*m*s) == W - var powerCorrectionInJoules = (wattsPerNewton * linearImpulse.Length()) + (wattsPerNewtonMeter * angularImpulse.Length()); - modify.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_IMPULSE_AND_WORLD_ANGULAR_IMPULSE, linearImpulse, modify.Physics.CenterOfMassWorld, angularImpulse); - if(recoil) - dest.Physics.AddForce(MyPhysicsForceType.APPLY_WORLD_IMPULSE_AND_WORLD_ANGULAR_IMPULSE, -linearImpulse, dest.Physics.CenterOfMassWorld, -angularImpulse); - - return powerCorrectionInJoules * MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS; - } - } -}using System; -using System.Collections.Generic; -using System.Linq; -using VRage.Game; -using VRage.Utils; -using VRageMath; - -namespace ShipyardMod.ItemClasses -{ - public class PacketManager - { - private readonly List _packets = new List(); - private readonly double _spacingSq; - private bool _init; - private double _multiplier; - private double _travelDist; - public Vector4 Color; - public Vector3D Origin { get; private set; } - public Vector3D Target { get; private set; } - private readonly bool _invert; - - public PacketManager(Vector3D origin, Vector3D target, Vector4 color, double spacing = 10, bool invert = false) - { - Origin = origin; - Target = target; - Color = color; - _invert = invert; - _spacingSq = spacing * spacing; - } - - public void Update(Vector3D origin, Vector3D target) - { - if (_invert) - { - Origin = target; - Target = origin; - } - else - { - Origin = origin; - Target = target; - } - } - - private void Init() - { - _travelDist = Vector3D.Distance(Origin, Target); - _packets.Add(new PacketItem(Origin)); - //packets move at 20 - 40m/s - Color.W = 1; - double speed = Math.Max(20, Math.Min(40, _travelDist / 3)); - _multiplier = 1 / (_travelDist / speed * 60); - } - - public void DrawPackets() - { - UpdatePackets(); - - foreach (PacketItem packet in _packets) - { - //thanks to Digi for showing me how this thing works - MyTransparentGeometry.AddPointBillboard(MyStringId.GetOrCompute("ShipyardPacket"), Color, packet.Position, 0.3f, packet.Ticks); - } - } - - private void UpdatePackets() - { - if (!_init) - { - _init = true; - Init(); - } - - var toRemove = new List(); - foreach (PacketItem packet in _packets) - { - packet.Ticks++; - packet.Position = Vector3D.Lerp(Origin, Target, _multiplier * packet.Ticks); - - //delete the packet once it gets to the destination - if (_multiplier * packet.Ticks > 1) - toRemove.Add(packet); - } - - foreach (PacketItem removePacket in toRemove) - _packets.Remove(removePacket); - - //if the last packet to go out is more than 10m from origin, add a new one - PacketItem lastPacket = _packets.LastOrDefault(); - if (lastPacket != null && Vector3D.DistanceSquared(lastPacket.Position, Origin) > _spacingSq) - _packets.Add(new PacketItem(Origin)); - } - - private class PacketItem - { - public Vector3D Position; - public int Ticks; - - public PacketItem(Vector3D position) - { - Position = position; - Ticks = 0; - } - } - } -}using System.Collections.Generic; -using System.Linq; -using Sandbox.Game; -using Sandbox.Game.Entities; -using Sandbox.Game.GameSystems; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.Utility; -using VRage.Game.Entity; -using VRage.Game.ModAPI; - -namespace ShipyardMod.ProcessHandlers -{ - public class ProcessConveyorCache : ProcessHandlerBase - { - public override int GetUpdateResolution() - { - return 5000; // holy shit this causes a lot of lag due to conveyors!!!!!!!!!!!!!!!!! - } - - public override bool ServerOnly() - { - return true; - } - - private int _currentShipyardIndex = 0; - - public override void Handle() - { - if (ProcessShipyardDetection.ShipyardsList.Count == 0) return; - - ShipyardItem currentItem = ProcessShipyardDetection.ShipyardsList.ElementAt(_currentShipyardIndex); - _currentShipyardIndex = (_currentShipyardIndex + 1) % ProcessShipyardDetection.ShipyardsList.Count; - - var grid = (IMyCubeGrid)currentItem.YardEntity; - - if (grid.Physics == null || grid.Closed || currentItem.YardType == ShipyardType.Invalid) - { - currentItem.ConnectedCargo.Clear(); - return; - } - var gts = MyAPIGateway.TerminalActionsHelper.GetTerminalSystemForGrid(grid); - - var blocks = new List(); - gts.GetBlocks(blocks); - - //assume that all the tools are connected, so only check against the first one in the list - var cornerInventory = (IMyInventory)((MyEntity)currentItem.Tools[0]).GetInventory(); - - var disconnectedInventories = new HashSet(); - - //remove blocks which are closed or no longer in the terminal system - foreach (var block in currentItem.ConnectedCargo) - { - if (block.Closed || !blocks.Contains(block)) - disconnectedInventories.Add(block); - } - - foreach (var dis in disconnectedInventories) - { - currentItem.ConnectedCargo.Remove(dis); - } - - var newConnections = new HashSet(); - Utilities.InvokeBlocking(() => - { - //check our cached inventories for connected-ness - foreach (IMyTerminalBlock cargo in currentItem.ConnectedCargo) - { - if (cornerInventory == null) - return; - - if (!cornerInventory.IsConnectedTo(((MyEntity)cargo).GetInventory())) - disconnectedInventories.Add(cargo); - } - - foreach (var block in blocks) - { - //avoid duplicate checks - if (disconnectedInventories.Contains(block) || currentItem.ConnectedCargo.Contains(block)) - continue; - - //to avoid shipyard corners pulling from each other. Circles are no fun. - if (block.BlockDefinition.SubtypeName.Contains("ShipyardCorner")) - continue; - - //ignore reactors - if (block is IMyReactor) - continue; - - //ignore oxygen generators and tanks - if (block is IMyGasGenerator || block is IMyGasTank) - continue; - - if (currentItem.ConnectedCargo.Contains(block) || disconnectedInventories.Contains(block)) - continue; - - if (((MyEntity)block).HasInventory) - { - MyInventory inventory = ((MyEntity)block).GetInventory(); - if (cornerInventory == null) - return; - if (cornerInventory.IsConnectedTo(inventory)) - newConnections.Add(block); - } - } - }); - - foreach (IMyTerminalBlock removeBlock in disconnectedInventories) - currentItem.ConnectedCargo.Remove(removeBlock); - - foreach (IMyTerminalBlock newBlock in newConnections) - currentItem.ConnectedCargo.Add(newBlock); - } - - } -}using System; -using Sandbox.ModAPI; - -namespace ShipyardMod.ProcessHandlers -{ - public abstract class ProcessHandlerBase - { - protected DateTime _lastUpdate; - - public ProcessHandlerBase() - { - LastUpdate = DateTime.Now; - } - - public DateTime LastUpdate - { - get { return _lastUpdate; } - set { _lastUpdate = value; } - } - - public virtual int GetUpdateResolution() - { - return 1000; - } - - public virtual bool ServerOnly() - { - return true; - } - - public virtual bool ClientOnly() - { - return false; - } - - public bool CanRun() - { - if (DateTime.Now - LastUpdate > TimeSpan.FromMilliseconds(GetUpdateResolution())) - { - if (ServerOnly() && MyAPIGateway.Multiplayer.IsServer) - return true; - if (ClientOnly() && MyAPIGateway.Session.Player != null) - return true; - } - return false; - } - - public abstract void Handle(); - } -}using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Sandbox.Definitions; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.Utility; -using SpaceEngineers.Game.ModAPI; -using VRage.Game.GUI.TextPanel; -using VRage.Game.ModAPI; -using VRageMath; - -namespace ShipyardMod.ProcessHandlers -{ - public class ProcessLCDMenu : ProcessHandlerBase - { - private static readonly string FullName = typeof(ProcessLCDMenu).FullName; - private readonly Dictionary _stats = new Dictionary(); - - private int _updateCount; - - public override int GetUpdateResolution() - { - return 200; - } - - public override bool ServerOnly() - { - return true; - } - - public override bool ClientOnly() - { - return false; - } - - public override void Handle() - { - foreach (ShipyardItem item in ProcessShipyardDetection.ShipyardsList) - { - if (item.Menu != null) - { - if (item.Menu.Panel.Closed) - item.Menu = null; - } - - if (item.Menu != null) - Utilities.Invoke(() => item.Menu.UpdateLCD()); - else - { - if (_updateCount < 5) - { - _updateCount++; - return; - } - _updateCount = 0; - - var yardGrid = (IMyCubeGrid)item.YardEntity; - - var blocks = new List(); - yardGrid.GetBlocks(blocks); - bool superFound = false; - - foreach (IMySlimBlock slimBlock in blocks) - { - var panel = slimBlock.FatBlock as IMyTextPanel; - - if (panel == null) - continue; - - if (!panel.CustomName.ToLower().Contains("shipyard")) - continue; - - var bound = new BoundingSphereD(panel.GetPosition(), 5); - List nearblocks = yardGrid.GetBlocksInsideSphere(ref bound); - bool found = true; - - foreach (IMySlimBlock block in nearblocks) - { - var buttons = block.FatBlock as IMyButtonPanel; - - if (buttons == null) - continue; - - superFound = true; - found = true; - Logging.Instance.WriteDebug("Found LCD pair for grid: " + item.EntityId); - //Utilities.Invoke(() => - //{ - // long id = item.EntityId; - // //item.Menu = new LCDMenu(); - // ShipyardControlReader reader = new ShipyardControlReader(); - // reader.ReadLCDContent(panel); - - // panel.Enabled = true; - - // if (panel.ContentType != ContentType.SCRIPT) - // { - // panel.ContentType = ContentType.SCRIPT; - // } - - // if (panel.Script != "ShipyardControl") - // { - // panel.Script = "ShipyardControl"; - // } - - // var shipyardControlScript = panel.GetScript(); - // var targetsVariable = shipyardControlScript.GetVariable("shipyardTargetList"); - - // if (targetsVariable != null) - // { - // // Get the value of the 'shipyardTargetList' variable - // string targetsValue = targetsVariable.GetValue().ToString(); - - // // Update the menu or perform any other actions based on the variable value - // // For example, update the shipyardTargetList label in the LCDMenu: - // reader.shipyardTargetList.Text = $"Targets: {targetsValue}"; - // } - - // // Rest of the code... - //}); - - Utilities.Invoke(() => - { - long id = item.EntityId; - item.Menu = new LCDMenu(); - panel.Enabled = true; - //panel.RequestEnable(false); - //panel.RequestEnable(true); - //item.Menu.BindButtonPanel(buttons); - item.Menu.BindLCD(panel); - var mainMenu = new MenuItem("", "", null, MenuDel(id)); - var statusMenu = new MenuItem("", "", null, StatusDel(id)); - var scanMenu = new MenuItem("", "Get details for: \r\n"); - var grindStatsMenu = new MenuItem("SCANNING...", "", null, GrindStatsDel(id)); - var weldStatsMenu = new MenuItem("SCANNING...", "", null, WeldStatsDel(id)); - var returnMenu = new MenuItem("Return", "", ScanDel(item.Menu, mainMenu, 0)); - mainMenu.root = item.Menu; - mainMenu.Add(new MenuItem("Grind", "", GrindDel(item.Menu, statusMenu))); - mainMenu.Add(new MenuItem("Weld", "", WeldDel(item.Menu, statusMenu))); - mainMenu.Add(new MenuItem("Scan", "", ScanDel(item.Menu, scanMenu))); - statusMenu.root = item.Menu; - statusMenu.Add(new MenuItem("Stop", "", StopDel(item.Menu, mainMenu))); - scanMenu.Add(new MenuItem("Grind", "", ScanDel(item.Menu, grindStatsMenu, ShipyardType.Scanning))); - scanMenu.Add(new MenuItem("Weld", "", ScanDel(item.Menu, weldStatsMenu, ShipyardType.Scanning))); - grindStatsMenu.Add(returnMenu); - weldStatsMenu.Add(returnMenu); - item.Menu.Root = mainMenu; - item.Menu.SetCurrentItem(mainMenu); - item.Menu.UpdateLCD(); - Communication.SendNewYard(item); - }); - - break; - } - - if (found) - break; - } - Logging.Instance.WriteDebug($"SuperFind {item.EntityId}: {superFound}"); - } - } - } - - public string FormatStatus(ShipyardItem item) - { - bool welding = false; - var result = new StringBuilder(); - - result.Append("Shipyard Status:\r\n"); - switch (item.YardType) - { - case ShipyardType.Disabled: - result.Append("IDLE"); - break; - - case ShipyardType.Grind: - result.Append("GRINDING"); - break; - - case ShipyardType.Weld: - result.Append("WELDING"); - welding = true; - break; - - default: - result.Append("ERROR"); - return result.ToString(); - } - - result.Append("\r\n\r\n"); - - if (welding && item.MissingComponentsDict.Count > 0) - { - result.Append("Missing Components:\r\n"); - foreach (KeyValuePair component in item.MissingComponentsDict) - { - result.Append($"{component.Key}: {component.Value}\r\n"); - } - result.Append("\r\n"); - } - - float time = 0f; - - if (item.YardType == ShipyardType.Grind) - { - foreach (BlockTarget target in item.TargetBlocks) - { - if (target.Projector != null) - continue; - time += target.Block.Integrity / ((MyCubeBlockDefinition)target.Block.BlockDefinition).IntegrityPointsPerSec; - } - time /= item.Settings.GrindMultiplier; - time = time / (item.Settings.BeamCount * 8); - } - else //welding - { - foreach (BlockTarget target in item.TargetBlocks) - { - if (target.Projector != null) - time += target.BuildTime; - else - time += (target.Block.MaxIntegrity - target.Block.Integrity) / ((MyCubeBlockDefinition)target.Block.BlockDefinition).IntegrityPointsPerSec; - } - time /= item.Settings.WeldMultiplier; - time = time / (item.Settings.BeamCount * 8); - } - - int active = item.BlocksToProcess.Sum(entry => entry.Value.Count(target => target != null)); - - result.Append("Targets: " + item.YardGrids.Count); - result.Append("\r\n"); - result.Append($"Active beams: {active}/{item.Settings.BeamCount * 8}"); - result.Append("\r\n"); - result.Append("Blocks remaining: " + item.TargetBlocks.Count); - result.Append("\r\n"); - result.Append("Estimated time remaining: " + TimeSpan.FromSeconds((int)time).ToString("g")); - result.Append("\r\n\r\n"); - return result.ToString(); - } - - private string FormatGrindStats(ShipyardItem item) - { - if (!_stats.ContainsKey(item)) - _stats.Add(item, new StatInfo()); - - StatInfo stats = _stats[item]; - if (stats.StartTime == 0) - { - stats.StartTime = MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds; - //calculating stats can take tens or hundreds of ms, so drop it in a thread and check back when the scan animation is finished. - MyAPIGateway.Parallel.StartBackground(() => - { - try - { - var statBlock = Profiler.Start(FullName, nameof(FormatGrindStats)); - stats.TotalComponents = new Dictionary(); - var blockList = new List(); - var processGrids = new HashSet(); - processGrids.UnionWith(item.YardGrids); - processGrids.UnionWith(item.ContainsGrids); - foreach (IMyCubeGrid grid in processGrids) - { - if (grid?.Physics == null || grid.Closed) - continue; - grid.GetBlocks(blockList); - stats.BlockCount += blockList.Count; - - var missingComponents = new Dictionary(); - foreach (IMySlimBlock block in blockList) - { - //var blockDef = (MyObjectBuilder_CubeBlockDefinition)block.BlockDefinition.GetObjectBuilder(); - //grindTime += Math.Max(blockDef.BuildTimeSeconds / ShipyardSettings.Instance.GrindMultiplier, 0.5f); - - var blockDef = (MyCubeBlockDefinition)block.BlockDefinition; - //IntegrityPointsPerSec = MaxIntegrity / BuildTimeSeconds - //this is much, much faster than pulling the objectbuilder and getting the data from there. - stats.GrindTime += Math.Max(block.BuildPercent() * (blockDef.MaxIntegrity / blockDef.IntegrityPointsPerSec) / MyAPIGateway.Session.GrinderSpeedMultiplier / item.Settings.GrindMultiplier, 0.5f); - - block.GetMissingComponents(missingComponents); - - foreach (MyCubeBlockDefinition.Component component in blockDef.Components) - { - if (stats.TotalComponents.ContainsKey(component.Definition.Id.SubtypeName)) - stats.TotalComponents[component.Definition.Id.SubtypeName] += component.Count; - else - stats.TotalComponents.Add(component.Definition.Id.SubtypeName, component.Count); - } - } - - foreach (KeyValuePair missing in missingComponents) - { - if (stats.TotalComponents.ContainsKey(missing.Key)) - stats.TotalComponents[missing.Key] -= missingComponents[missing.Key]; - } - - blockList.Clear(); - } - - var result = new StringBuilder(); - result.Append("Scan Results:\r\n\r\n"); - result.Append("Targets: " + item.YardGrids.Count); - result.Append("\r\n"); - result.Append("Block Count: " + stats.BlockCount); - result.Append("\r\n"); - float grindTime = stats.GrindTime / (item.Settings.BeamCount * 8); - if (grindTime >= 7200) - result.Append("Estimated Deconstruct Time: " + (grindTime / 3600).ToString("0.00") + " hours"); - else if (grindTime >= 120) - result.Append("Estimated Deconstruct Time: " + (grindTime / 60).ToString("0.00") + " min"); - else - result.Append("Estimated Deconstruct Time: " + grindTime.ToString("0.00") + "s"); - result.Append("\r\n"); - result.Append("Estimated Component Gain:\r\n\r\n"); - double multiplier = Math.Max(item.ShipyardBox.HalfExtent.LengthSquared() / 200000, 1); - foreach (KeyValuePair component in stats.TotalComponents) - { - if (component.Value != 0) - result.Append($"{component.Key}: {component.Value / multiplier}\r\n"); - } - - stats.Output = result.ToString(); - statBlock.End(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine(ex.ToString()); - } - }); - } - - if (!string.IsNullOrEmpty(stats.Output) && MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds - stats.StartTime > 6000) - return stats.Output; - return "SCANNING...\r\n\r\n"; - } - - private string FormatWeldStats(ShipyardItem item) - { - if (!_stats.ContainsKey(item)) - _stats.Add(item, new StatInfo()); - - StatInfo stats = _stats[item]; - if (stats.StartTime == 0) - { - stats.StartTime = MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds; - //calculating stats can take tens or hundreds of ms, so drop it in a thread and check back when the scan animation is finished. - MyAPIGateway.Parallel.StartBackground(() => - { - try - { - var statBlock = Profiler.Start(FullName, nameof(FormatWeldStats)); - stats.TotalComponents = new Dictionary(); - var blockList = new List(); - var processGrids = new HashSet(); - processGrids.UnionWith(item.YardGrids); - processGrids.UnionWith(item.ContainsGrids); - - var projections = new HashSet(); - - foreach (IMyCubeGrid grid in processGrids) - { - if (grid.MarkedForClose || grid.Closed) - continue; - - grid.GetBlocks(blockList); - - foreach (IMySlimBlock block in blockList) - { - var projector = block?.FatBlock as IMyProjector; - if (projector == null) - continue; - if (projector.IsProjecting && projector.ProjectedGrid != null) - projections.Add(projector.ProjectedGrid); - } - - blockList.Clear(); - } - - processGrids.UnionWith(projections); - - foreach (IMyCubeGrid grid in processGrids) - { - if (grid.MarkedForClose || grid.Closed) - continue; - - grid.GetBlocks(blockList); - stats.BlockCount += blockList.Count; - - var missingComponents = new Dictionary(); - foreach (IMySlimBlock block in blockList) - { - //var blockDef = (MyObjectBuilder_CubeBlockDefinition)block.BlockDefinition.GetObjectBuilder(); - //grindTime += Math.Max(blockDef.BuildTimeSeconds / ShipyardSettings.Instance.GrindMultiplier, 0.5f); - - var blockDef = (MyCubeBlockDefinition)block.BlockDefinition; - //IntegrityPointsPerSec = MaxIntegrity / BuildTimeSeconds - //this is much, much faster than pulling the objectbuilder and getting the data from there. - if (grid.Physics != null) - stats.GrindTime += Math.Max((1 - block.BuildPercent()) * (blockDef.MaxIntegrity / blockDef.IntegrityPointsPerSec) / MyAPIGateway.Session.WelderSpeedMultiplier / item.Settings.WeldMultiplier, 0.5f); - else - stats.GrindTime += Math.Max(blockDef.MaxIntegrity / blockDef.IntegrityPointsPerSec / MyAPIGateway.Session.WelderSpeedMultiplier / item.Settings.WeldMultiplier, 0.5f); - - block.GetMissingComponents(missingComponents); - - if (grid.Physics != null) - { - foreach (KeyValuePair missing in missingComponents) - { - if (!stats.TotalComponents.ContainsKey(missing.Key)) - stats.TotalComponents.Add(missing.Key, missing.Value); - else - stats.TotalComponents[missing.Key] += missing.Value; - } - } - else - { - //projections will always consume the fully component count - foreach (MyCubeBlockDefinition.Component component in blockDef.Components) - { - if (stats.TotalComponents.ContainsKey(component.Definition.Id.SubtypeName)) - stats.TotalComponents[component.Definition.Id.SubtypeName] += component.Count; - else - stats.TotalComponents.Add(component.Definition.Id.SubtypeName, component.Count); - } - } - missingComponents.Clear(); - } - - blockList.Clear(); - } - - var result = new StringBuilder(); - result.Append("Scan Results:\r\n\r\n"); - result.Append("Targets: " + item.YardGrids.Count); - result.Append("\r\n"); - result.Append("Block Count: " + stats.BlockCount); - result.Append("\r\n"); - float grindTime = stats.GrindTime / (item.Settings.BeamCount * 8); - if (grindTime >= 7200) - result.Append("Estimated Construct Time: " + (grindTime / 3600).ToString("0.00") + " hours"); - else if (grindTime >= 120) - result.Append("Estimated Construct Time: " + (grindTime / 60).ToString("0.00") + " min"); - else - result.Append("Estimated Construct Time: " + grindTime.ToString("0.00") + "s"); - result.Append("\r\n"); - if (stats.TotalComponents.Any()) - { - result.Append("Estimated Components Used:\r\n\r\n"); - double multiplier = Math.Max(item.ShipyardBox.HalfExtent.LengthSquared() / 200000, 1); - foreach (KeyValuePair component in stats.TotalComponents) - { - if (component.Value != 0) - result.Append($"{component.Key}: {component.Value / multiplier}\r\n"); - } - } - - stats.Output = result.ToString(); - statBlock.End(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine(ex.ToString()); - } - }); - } - - if (!string.IsNullOrEmpty(stats.Output) && MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds - stats.StartTime > 6000) - return stats.Output; - return "SCANNING..."; - } - - private string FormatMainMenu(ShipyardItem item) - { - var result = new StringBuilder(); - result.Append("Automated Shipyard Main Menu\r\n\r\n"); - result.Append("Current targets: " + item.ContainsGrids.Count); - result.Append("\r\n\r\n"); - - result.Append(". Exit : Up :. Down :: Select"); - result.Append("\r\n\r\n"); - - return result.ToString(); - } - - private MenuItem.MenuAction WeldDel(LCDMenu root, MenuItem item) - { - MenuItem.MenuAction handler = () => - { - long id = root.Panel.CubeGrid.EntityId; - - root.SetCurrentItem(item); - Communication.SendYardCommand(id, ShipyardType.Weld); - }; - return handler; - } - - private MenuItem.MenuAction GrindDel(LCDMenu root, MenuItem item) - { - MenuItem.MenuAction handler = () => - { - long id = root.Panel.CubeGrid.EntityId; - - root.SetCurrentItem(item); - Communication.SendYardCommand(id, ShipyardType.Grind); - }; - return handler; - } - - private MenuItem.MenuAction ScanDel(LCDMenu root, MenuItem item, ShipyardType? itemType = null) - { - MenuItem.MenuAction handler = () => - { - long id = root.Panel.CubeGrid.EntityId; - - root.SetCurrentItem(item); - if (itemType.HasValue) - Communication.SendYardCommand(id, itemType.Value); - if (itemType == 0) - foreach (ShipyardItem yard in ProcessShipyardDetection.ShipyardsList) - { - if (yard.EntityId == id) - { - _stats.Remove(yard); - break; - } - } - }; - return handler; - } - - private MenuItem.MenuAction StopDel(LCDMenu root, MenuItem item) - { - MenuItem.MenuAction handler = () => - { - long id = root.Panel.CubeGrid.EntityId; - - root.SetCurrentItem(item); - Communication.SendYardCommand(id, ShipyardType.Disabled); - }; - return handler; - } - - private MenuItem.DescriptionAction StatusDel(long id) - { - MenuItem.DescriptionAction handler = () => - { - foreach (ShipyardItem item in ProcessShipyardDetection.ShipyardsList) - { - if (item.EntityId == id) - return FormatStatus(item); - } - return ""; - }; - return handler; - } - - private MenuItem.DescriptionAction GrindStatsDel(long id) - { - MenuItem.DescriptionAction handler = () => - { - foreach (ShipyardItem item in ProcessShipyardDetection.ShipyardsList) - { - if (item.EntityId == id) - return FormatGrindStats(item); - } - return ""; - }; - return handler; - } - - private MenuItem.DescriptionAction WeldStatsDel(long id) - { - MenuItem.DescriptionAction handler = () => - { - foreach (ShipyardItem item in ProcessShipyardDetection.ShipyardsList) - { - if (item.EntityId == id) - return FormatWeldStats(item); - } - return ""; - }; - return handler; - } - - private MenuItem.DescriptionAction MenuDel(long id) - { - MenuItem.DescriptionAction handler = () => - { - foreach (ShipyardItem item in ProcessShipyardDetection.ShipyardsList) - { - if (item.EntityId == id) - return FormatMainMenu(item); - } - return ""; - }; - return handler; - } - - private class StatInfo - { - public int BlockCount; - public float GrindTime; - public string Output; - public double StartTime; - public Dictionary TotalComponents; - } - } -}using System.Collections.Generic; -using System.Linq; -using Sandbox.Definitions; -using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.Utility; -using VRage.Collections; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace ShipyardMod.ProcessHandlers -{ - internal class ProcessLocalYards : ProcessHandlerBase - { - public static MyConcurrentHashSet LocalYards = new MyConcurrentHashSet(); - private static readonly string FullName = typeof(ProcessLocalYards).FullName; - - public override int GetUpdateResolution() - { - return 500; - } - - public override bool ServerOnly() - { - return false; - } - - public override bool ClientOnly() - { - return true; - } - - public override void Handle() - { - Logging.Instance.WriteDebug("ProcessLocalYards Start"); - var removeYards = new HashSet(); - - foreach (ShipyardItem item in LocalYards) - { - //see if the shipyard has been deleted - if (item.YardEntity.Closed || item.YardEntity.Physics == null || item.YardType == ShipyardType.Invalid - || (item.StaticYard && !item.YardEntity.Physics.IsStatic)) - { - //the client shouldn't tell the server the yard is invalid - //item.Disable(); - removeYards.Add(item); - continue; - } - - if (item.StaticYard) - UpdateBoxLines(item); - - //don't draw boxes inside active yards, it's distracting - if (item.YardType != ShipyardType.Disabled) - continue; - - var corners = new Vector3D[8]; - item.ShipyardBox.GetCorners(corners, 0); - double dist = Vector3D.DistanceSquared(corners[0], item.ShipyardBox.Center); - - var sphere = new BoundingSphereD(item.ShipyardBox.Center, dist); - - //Utilities.InvokeBlocking(()=> entities = MyAPIGateway.Entities.GetTopMostEntitiesInSphere(ref sphere)); - List entities = MyAPIGateway.Entities.GetTopMostEntitiesInSphere(ref sphere); - - if (entities.Count == 0) - { - Logging.Instance.WriteDebug("Couldn't get entities in ProcessLocalYards"); - continue; - } - var removeGrids = new HashSet(); - foreach (IMyEntity entity in entities) - { - var grid = entity as IMyCubeGrid; - if (grid == null) - continue; - - if (grid.EntityId == item.EntityId) - continue; - - //workaround to not blind people with digi's helmet mod - if (grid.Physics == null && grid.Projector() == null) - continue; - - if (grid.Closed || grid.MarkedForClose) - { - removeGrids.Add(grid); - continue; - } - - if (LocalYards.Any(x => x.EntityId == grid.EntityId)) - continue; - - //create a bounding box around the ship - MyOrientedBoundingBoxD gridBox = MathUtility.CreateOrientedBoundingBox(grid); - - //check if the ship bounding box is completely inside the yard box - ContainmentType result = item.ShipyardBox.Contains(ref gridBox); - - if (result == ContainmentType.Contains) - { - item.ContainsGrids.Add(grid); - item.IntersectsGrids.Remove(grid); - } - else if (result == ContainmentType.Intersects) - { - item.IntersectsGrids.Add(grid); - item.ContainsGrids.Remove(grid); - } - else - { - removeGrids.Add(grid); - } - } - - foreach (IMyCubeGrid containGrid in item.ContainsGrids) - if (!entities.Contains(containGrid)) - removeGrids.Add(containGrid); - - foreach (IMyCubeGrid intersectGrid in item.IntersectsGrids) - if (!entities.Contains(intersectGrid)) - removeGrids.Add(intersectGrid); - - foreach (IMyCubeGrid removeGrid in removeGrids) - { - ShipyardCore.BoxDict.Remove(removeGrid.EntityId); - item.ContainsGrids.Remove(removeGrid); - item.IntersectsGrids.Remove(removeGrid); - } - } - - foreach (ShipyardItem removeItem in removeYards) - { - foreach (IMyCubeGrid grid in removeItem.ContainsGrids) - { - ShipyardCore.BoxDict.Remove(grid.EntityId); - } - foreach (IMyCubeGrid grid in removeItem.IntersectsGrids) - { - ShipyardCore.BoxDict.Remove(grid.EntityId); - } - - LocalYards.Remove(removeItem); - } - } - - // OBB corner structure - // ZMax ZMin - // 0----1 4----5 - // | | | | - // | | | | - // 3----2 7----6 - /// - /// Updates the internal list of lines so we only draw a laser if there is a frame to contain it - /// - /// - private void UpdateBoxLines(ShipyardItem item) - { - var lineBlock = Profiler.Start(FullName, nameof(UpdateBoxLines)); - var corners = new Vector3D[8]; - item.ShipyardBox.GetCorners(corners, 0); - var grid = (IMyCubeGrid)item.YardEntity; - - var gridCorners = new Vector3I[8]; - for (int i = 0; i < 8; i++) - gridCorners[i] = grid.WorldToGridInteger(corners[i]); - - item.BoxLines.Clear(); - - //okay, really long unrolled loop coming up, but it's the simplest way to do it - //zMax face - if (WalkLine(gridCorners[0], gridCorners[1], grid)) - item.BoxLines.Add(new LineItem(corners[0], corners[1])); - if (WalkLine(gridCorners[1], gridCorners[2], grid)) - item.BoxLines.Add(new LineItem(corners[1], corners[2])); - if (WalkLine(gridCorners[2], gridCorners[3], grid)) - item.BoxLines.Add(new LineItem(corners[2], corners[3])); - if (WalkLine(gridCorners[3], gridCorners[0], grid)) - item.BoxLines.Add(new LineItem(corners[3], corners[0])); - //zMin face - if (WalkLine(gridCorners[4], gridCorners[5], grid)) - item.BoxLines.Add(new LineItem(corners[4], corners[5])); - if (WalkLine(gridCorners[5], gridCorners[6], grid)) - item.BoxLines.Add(new LineItem(corners[5], corners[6])); - if (WalkLine(gridCorners[6], gridCorners[7], grid)) - item.BoxLines.Add(new LineItem(corners[6], corners[7])); - if (WalkLine(gridCorners[7], gridCorners[4], grid)) - item.BoxLines.Add(new LineItem(corners[7], corners[4])); - //connecting lines - if (WalkLine(gridCorners[0], gridCorners[4], grid)) - item.BoxLines.Add(new LineItem(corners[0], corners[4])); - if (WalkLine(gridCorners[1], gridCorners[5], grid)) - item.BoxLines.Add(new LineItem(corners[1], corners[5])); - if (WalkLine(gridCorners[2], gridCorners[6], grid)) - item.BoxLines.Add(new LineItem(corners[2], corners[6])); - if (WalkLine(gridCorners[3], gridCorners[7], grid)) - item.BoxLines.Add(new LineItem(corners[3], corners[7])); - - lineBlock.End(); - } - - private bool WalkLine(Vector3I start, Vector3I end, IMyCubeGrid grid) - { - var it = new MathUtility.Vector3ILineIterator(start, end); - while (it.IsValid()) - { - IMySlimBlock block = grid.GetCubeBlock(it.Current); - it.MoveNext(); - - if (block == null) - return false; - - if (!block.BlockDefinition.Id.SubtypeName.Contains("Shipyard")) - return false; - - if (block.BuildPercent() < ((MyCubeBlockDefinition)block.BlockDefinition).CriticalIntegrityRatio) - return false; - } - return true; - } - } -}using System; -using System.Collections.Generic; -using System.Linq; -using Sandbox.Definitions; -using Sandbox.Game; -using Sandbox.Game.Entities; -using Sandbox.Game.Weapons; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.Utility; -using VRage; -using VRage.Game; -using VRage.Game.Entity; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace ShipyardMod.ProcessHandlers -{ - public class ProcessShipyardAction : ProcessHandlerBase - { - private static readonly string FullName = typeof(ProcessShipyardAction).FullName; - - private readonly HashSet _stalledTargets = new HashSet(); - - private readonly MyInventory _tmpInventory = new MyInventory(); - - public override int GetUpdateResolution() - { - return 500; - } - - public override bool ServerOnly() - { - return true; - } - - public override void Handle() - { - foreach (ShipyardItem item in ProcessShipyardDetection.ShipyardsList) - { - item.ProcessDisable(); - var startBlock = Profiler.Start(FullName, nameof(Handle), "Target Detection"); - //see if the shipyard has been deleted - //just disable the yard and skip it, the ShipyardHandler will take care of deleting the item - if (item.YardEntity.Closed || item.YardEntity.MarkedForClose) - { - item.Disable(); - item.ProcessDisable(); - continue; - } - - item.UpdatePowerUse(); - - if (item.Tools.Any(x => !((IMyFunctionalBlock)x).Enabled)) - { - item.Disable(); - item.ProcessDisable(); - continue; - } - - //look for grids inside the shipyard - if (item.YardType == ShipyardType.Disabled) - { - var allEntities = new HashSet(); - var grids = new HashSet(); - - MyAPIGateway.Entities.GetEntities(allEntities); - if (allEntities.Count == 0) - { - //this can happen when we pull the entitiy list outside of the game thread - Logging.Instance.WriteLine("Failed to get entity list in ShipyardAction"); - continue; - } - - //do a fast check to get entities near the shipyard - //we check against the OBB later, but it takes more time than this - double centerDist = item.ShipyardBox.HalfExtent.LengthSquared(); - - foreach (IMyEntity entity in allEntities) - { - if (!(entity is IMyCubeGrid) || entity.EntityId == item.EntityId) - continue; - - if (entity.Closed || entity.MarkedForClose) - continue; - - //use the center of the ship's bounding box. GetPosition() returns the pivot point, which can be far away from grid center - if (Vector3D.DistanceSquared(entity.Center(), item.ShipyardBox.Center) <= centerDist) - grids.Add(entity as IMyCubeGrid); - } - - if (grids.Count == 0) - continue; - - foreach (IMyCubeGrid grid in grids) - { - //create a bounding box around the ship - MyOrientedBoundingBoxD gridBox = MathUtility.CreateOrientedBoundingBox(grid); - - //check if the ship is completely inside the shipyard - if (item.ShipyardBox.Contains(ref gridBox) == ContainmentType.Contains) - item.ContainsGrids.Add(grid); - else - //in case it was previously inside, but moved - item.ContainsGrids.Remove(grid); - } - - continue; - } - - var toRemove = new List(); - - foreach (IMyCubeGrid yardGrid in item.YardGrids) - { - if (yardGrid.Closed || yardGrid.MarkedForClose) - { - toRemove.Add(yardGrid); - continue; - } - - //check if the target has left the shipyard - MyOrientedBoundingBoxD gridBox = MathUtility.CreateOrientedBoundingBox(yardGrid); - if (item.ShipyardBox.Contains(ref gridBox) != ContainmentType.Contains) - { - Logging.Instance.WriteLine("Target grid left yard"); - toRemove.Add(yardGrid); - } - } - - foreach (IMyCubeGrid removeGrid in toRemove) - { - item.YardGrids.Remove(removeGrid); - ((MyCubeGrid)removeGrid).OnGridSplit -= item.OnGridSplit; - var blocks = new List(); - removeGrid.GetBlocks(blocks); - var targetsToRemove = new HashSet(); - foreach (BlockTarget target in item.TargetBlocks.Where(x => blocks.Contains(x.Block))) - { - targetsToRemove.Add(target); - - foreach (KeyValuePair> entry in item.ProxDict) - entry.Value.Remove(target); - foreach (KeyValuePair procEntry in item.BlocksToProcess) - { - for (int i = 0; i < procEntry.Value.Length; i++) - { - if (procEntry.Value[i] == target) - { - procEntry.Value[i] = null; - Communication.ClearLine(procEntry.Key, i); - } - } - } - } - foreach (BlockTarget remove in targetsToRemove) - item.TargetBlocks.Remove(remove); - } - - if (item.YardGrids.Count == 0 || item.YardGrids.All(g => g.Physics == null && g.Projector() != null)) - { - Logging.Instance.WriteLine("Disabling shipyard; no more targets"); - //clear out and disable the shipyard - item.Disable(); - } - - startBlock.End(); - if (item.YardType == ShipyardType.Grind) - { - var grindBlock = Profiler.Start(FullName, "StepGrind"); - StepGrind(item); - grindBlock.End(); - } - else if (item.YardType == ShipyardType.Weld) - { - var weldBlock = Profiler.Start(FullName, "StepWeld"); - if (!StepWeld(item)) - item.Disable(); - weldBlock.End(); - } - - item.UpdatePowerUse(); - item.ProcessDisable(); - } - } - - private void StepGrind(ShipyardItem shipyardItem) - { - var targetsToRedraw = new HashSet(); - //we need to multiply this by MyShipGrinderConstants.GRINDER_AMOUNT_PER_SECOND / 2... which evaluates to 1 - float grindAmount = MyAPIGateway.Session.GrinderSpeedMultiplier * shipyardItem.Settings.GrindMultiplier; - if (shipyardItem.TargetBlocks.Count == 0) - { - var targetblock = Profiler.Start(FullName, nameof(StepGrind), "Populate target blocks"); - var blocks = new List(); - Logging.Instance.WriteLine(shipyardItem.YardGrids.Count.ToString()); - foreach (IMyCubeGrid yardGrid in shipyardItem.YardGrids.Where(g => g.Physics != null)) //don't process projections - { - var tmpBlocks = new List(); - yardGrid.GetBlocks(tmpBlocks); - blocks.AddRange(tmpBlocks); - } - - foreach (IMySlimBlock tmpBlock in blocks) - shipyardItem.TargetBlocks.Add(new BlockTarget(tmpBlock, shipyardItem)); - targetblock.End(); - } - List allBlocks = shipyardItem.TargetBlocks.ToList(); - - if (allBlocks.Count == 0) - return; - - if (!shipyardItem.ProxDict.Any()) - { - //sort blocks by distance to each tool - foreach (IMyCubeBlock tool in shipyardItem.Tools) - { - var targetSortBlock = Profiler.Start(FullName, nameof(StepGrind), "Sort Targets"); - List sortTargets = allBlocks.ToList(); - sortTargets.Sort((a, b) => a.ToolDist[tool.EntityId].CompareTo(b.ToolDist[tool.EntityId])); - shipyardItem.ProxDict[tool.EntityId] = sortTargets; - targetSortBlock.End(); - } - } - - var targetFindBlock = Profiler.Start(FullName, nameof(StepGrind), "Find Targets"); - foreach (IMyCubeBlock tool in shipyardItem.Tools) - { - if (tool.Closed || tool.MarkedForClose) - { - //this is bad - shipyardItem.Disable(); - return; - } - - BlockTarget[] blockArray = shipyardItem.BlocksToProcess[tool.EntityId]; - - //find the next target for each grinder, if it needs one - for (int i = 0; i < shipyardItem.Settings.BeamCount; i++) - { - var toRemove = new HashSet(); - if (blockArray[i] != null) - continue; - - BlockTarget nextTarget = null; - - for (int b = 0; b < shipyardItem.ProxDict[tool.EntityId].Count; b++) - { - nextTarget = shipyardItem.ProxDict[tool.EntityId][b]; - - if (nextTarget.CubeGrid.Closed || nextTarget.CubeGrid.MarkedForClose) - continue; - - //one grinder per block, please - bool found = false; - foreach (KeyValuePair entry in shipyardItem.BlocksToProcess) - { - foreach (BlockTarget target in entry.Value) - { - if (target == null) - continue; - - if (target == nextTarget) - { - found = true; - break; - } - } - } - - if (found) - { - toRemove.Add(nextTarget); - continue; - } - - targetsToRedraw.Add(nextTarget); - break; - } - - foreach (BlockTarget removeTarget in toRemove) - { - shipyardItem.ProxDict[tool.EntityId].Remove(removeTarget); - shipyardItem.TargetBlocks.Remove(removeTarget); - } - - //we found a block to pair with our grinder, add it to the dictionary and carry on with destruction - if (nextTarget != null) - { - shipyardItem.BlocksToProcess[tool.EntityId][i] = nextTarget; - } - } - } - targetFindBlock.End(); - shipyardItem.UpdatePowerUse(); - var grindActionBlock = Profiler.Start(FullName, nameof(StepGrind), "Grind action"); - var removeTargets = new List(); - //do the grinding - Utilities.InvokeBlocking(() => - { - foreach (IMyCubeBlock tool in shipyardItem.Tools) - { - for (int b = 0; b < shipyardItem.BlocksToProcess[tool.EntityId].Length; b++) - { - BlockTarget target = shipyardItem.BlocksToProcess[tool.EntityId][b]; - if (target == null) - continue; - - if (target.CubeGrid.Closed || target.CubeGrid.MarkedForClose) - { - Logging.Instance.WriteDebug("Error in grind action: Target closed"); - removeTargets.Add(target); - return; - } - - if (targetsToRedraw.Contains(target)) - { - var toolLine = new Communication.ToolLineStruct - { - ToolId = tool.EntityId, - GridId = target.CubeGrid.EntityId, - BlockPos = target.GridPosition, - PackedColor = Color.OrangeRed.PackedValue, - Pulse = false, - EmitterIndex = (byte)b - }; - - Communication.SendLine(toolLine, shipyardItem.ShipyardBox.Center); - } - - /* - * Grinding laser "efficiency" is a float between 0-1 where: - * 0.0 => 0% of components recovered - * 1.0 => 100% of components recovered - * - * Efficiency decays exponentially as distance to the target (length of the "laser") increases - * 0m => 1.0000 - * 10m => 0.9995 - * 25m => 0.9969 - * 50m => 0.9875 - * 100m => 0.9500 - * 150m => 0.8875 - * 250m => 0.6875 - * 400m => 0.2000 - * inf => 0.1000 - * We impose a minimum efficiency of 0.1 (10%), which happens at distances > ~450m - */ - - // edited, same thing as welders - double efficiency = 1; - //double efficiency = 1 - (target.ToolDist[tool.EntityId] / 200000); - //if (!shipyardItem.StaticYard) - // efficiency /= 2; - if (efficiency < 0.1) - efficiency = 0.1; - //Logging.Instance.WriteDebug(String.Format("Grinder[{0}]block[{1}] distance=[{2:F2}m] efficiency=[{3:F5}]", tool.DisplayNameText, b, Math.Sqrt(target.ToolDist[tool.EntityId]), efficiency)); - - if (!shipyardItem.YardGrids.Contains(target.CubeGrid)) - { - //we missed this grid or its split at some point, so add it to the list and register the split event - shipyardItem.YardGrids.Add(target.CubeGrid); - ((MyCubeGrid)target.CubeGrid).OnGridSplit += shipyardItem.OnGridSplit; - } - MyInventory grinderInventory = ((MyEntity)tool).GetInventory(); - - if (!target.Block.IsFullyDismounted) - { - var decreaseBlock = Profiler.Start(FullName, nameof(StepGrind), "DecreaseMountLevel"); - target.Block.DecreaseMountLevel(grindAmount, grinderInventory); - decreaseBlock.End(); - - var inventoryBlock = Profiler.Start(FullName, nameof(StepGrind), "Grind Inventory"); - // First move everything into _tmpInventory - target.Block.MoveItemsFromConstructionStockpile(_tmpInventory); - - // Then move items into grinder inventory, factoring in our efficiency ratio - foreach (MyPhysicalInventoryItem item in _tmpInventory.GetItems()) - { - //Logging.Instance.WriteDebug(String.Format("Grinder[{0}]block[{1}] Item[{2}] grind_amt[{3:F2}] collect_amt[{4:F2}]", tool.DisplayNameText, b, item.Content.SubtypeName, item.Amount, (double)item.Amount*efficiency)); - grinderInventory.Add(item, (MyFixedPoint)Math.Round((double)item.Amount * efficiency)); - } - - // Then clear out everything left in _tmpInventory - _tmpInventory.Clear(); - inventoryBlock.End(); - } - - // This isn't an clause because target.Block may have become FullyDismounted above, - // in which case we need to run both code blocks - if (target.Block.IsFullyDismounted) - { - var dismountBlock = Profiler.Start(FullName, nameof(StepGrind), "FullyDismounted"); - var tmpItemList = new List(); - var blockEntity = target.Block.FatBlock as MyEntity; - if (blockEntity != null && blockEntity.HasInventory) - { - var dismountInventory = Profiler.Start(FullName, nameof(StepGrind), "DismountInventory"); - for (int i = 0; i < blockEntity.InventoryCount; ++i) - { - MyInventory blockInventory = blockEntity.GetInventory(i); - if (blockInventory == null) - continue; - - if (blockInventory.Empty()) - continue; - - tmpItemList.Clear(); - tmpItemList.AddRange(blockInventory.GetItems()); - - foreach (MyPhysicalInventoryItem item in tmpItemList) - { - //Logging.Instance.WriteDebug(String.Format("Grinder[{0}]block[{1}] Item[{2}] inventory[{3:F2}] collected[{4:F2}]", tool.DisplayNameText, b, item.Content.SubtypeName, item.Amount, (double)item.Amount * efficiency)); - blockInventory.Remove(item, item.Amount); - grinderInventory.Add(item, (MyFixedPoint)Math.Round((double)item.Amount * efficiency)); - } - } - dismountInventory.End(); - } - target.Block.SpawnConstructionStockpile(); - target.CubeGrid.RazeBlock(target.GridPosition); - removeTargets.Add(target); - shipyardItem.TargetBlocks.Remove(target); - dismountBlock.End(); - } - } - } - }); - - foreach (KeyValuePair> entry in shipyardItem.ProxDict) - { - foreach (BlockTarget removeBlock in removeTargets) - entry.Value.Remove(removeBlock); - } - - foreach (BlockTarget removeBlock in removeTargets) - shipyardItem.TargetBlocks.Remove(removeBlock); - - //shipyardItem.ActiveTargets = 0; - //clear lines for any destroyed blocks and update our target count - foreach (KeyValuePair entry in shipyardItem.BlocksToProcess) - { - for (int i = 0; i < entry.Value.Length; i++) - { - BlockTarget removeBlock = entry.Value[i]; - - if (removeTargets.Contains(removeBlock)) - { - Communication.ClearLine(entry.Key, i); - entry.Value[i] = null; - } - - //if (!removeTargets.Contains(removeBlock) && removeBlock != null) - //{ - // shipyardItem.ActiveTargets++; - //} - } - } - - grindActionBlock.End(); - } - - private bool StepWeld(ShipyardItem shipyardItem) - { - var targetsToRemove = new HashSet(); - var targetsToRedraw = new HashSet(); - - //we need to multiply this by MyShipWelder.WELDER_AMOUNT_PER_SECOND / 2, which also evaluates to 1 - float weldAmount = MyAPIGateway.Session.WelderSpeedMultiplier * shipyardItem.Settings.WeldMultiplier; - float boneAmount = weldAmount * .1f; - - if (shipyardItem.TargetBlocks.Count == 0) - { - var sortBlock = Profiler.Start(FullName, nameof(StepWeld), "Sort Targets"); - shipyardItem.TargetBlocks.Clear(); - shipyardItem.ProxDict.Clear(); - //precalculate all the BlockTarget for our target grids to speed things up - var gridTargets = new Dictionary>(); - foreach (IMyCubeGrid targetGrid in shipyardItem.YardGrids) - { - if (targetGrid.Closed || targetGrid.MarkedForClose) - continue; - - var tmpBlocks = new List(); - targetGrid.GetBlocks(tmpBlocks); - - gridTargets.Add(targetGrid.EntityId, new List(tmpBlocks.Count)); - foreach (IMySlimBlock block in tmpBlocks.ToArray()) - { - if (block == null || (targetGrid.Physics != null && block.IsFullIntegrity && !block.HasDeformation)) - continue; - - var target = new BlockTarget(block, shipyardItem); - - shipyardItem.TargetBlocks.Add(target); - - gridTargets[targetGrid.EntityId].Add(target); - } - /* - var proj = targetGrid.Projector(); - if (proj != null && !shipyardItem.YardGrids.Contains(proj.CubeGrid)) - { - proj.CubeGrid.GetBlocks(tmpBlocks); - gridTargets.Add(proj.CubeGrid.EntityId,new List()); - foreach (var block in tmpBlocks.ToArray()) - { - if (block == null || (targetGrid.Physics != null && block.IsFullIntegrity && !block.HasDeformation)) - continue; - - var pos = block.GetPosition(); - if (!shipyardItem.ShipyardBox.Contains(ref pos)) - continue; - - var target = new BlockTarget(block, shipyardItem); - - shipyardItem.TargetBlocks.Add(target); - gridTargets[block.CubeGrid.EntityId].Add(target); - } - } - */ - } - int count = 0; - foreach (KeyValuePair> entry in gridTargets) - count += entry.Value.Count; - - foreach (IMyCubeBlock tool in shipyardItem.Tools) - { - shipyardItem.ProxDict.Add(tool.EntityId, new List()); - //first sort grids by distance to tool - shipyardItem.YardGrids.Sort((a, b) => - Vector3D.DistanceSquared(a.Center(), tool.GetPosition()).CompareTo( - Vector3D.DistanceSquared(b.Center(), tool.GetPosition()))); - - foreach (IMyCubeGrid grid in shipyardItem.YardGrids) - { - //then sort blocks by distance to grid center - List list = gridTargets[grid.EntityId]; - list.Sort((a, b) => a.CenterDist.CompareTo(b.CenterDist)); - - shipyardItem.ProxDict[tool.EntityId].AddRange(list); - } - } - sortBlock.End(); - } - - //nothing to do - if (shipyardItem.TargetBlocks.Count == 0) - return false; - - //assign blocks to our welders - foreach (IMyCubeBlock welder in shipyardItem.Tools) - { - for (int i = 0; i < shipyardItem.Settings.BeamCount; i++) - { - if (shipyardItem.BlocksToProcess[welder.EntityId][i] != null) - continue; - - var toRemove = new List(); - BlockTarget nextTarget = null; - - foreach (BlockTarget target in shipyardItem.ProxDict[welder.EntityId]) - { - bool found = false; - //one block per tool - foreach (KeyValuePair entry in shipyardItem.BlocksToProcess) - { - if (entry.Value.Contains(target)) - { - found = true; - break; - } - } - if (found) - { - toRemove.Add(target); - continue; - } - - if (target.Projector != null) - { - BuildCheckResult res = target.Projector.CanBuild(target.Block, false); - if (res == BuildCheckResult.AlreadyBuilt) - { - target.UpdateAfterBuild(); - } - else if (res != BuildCheckResult.OK) - { - continue; - } - else - { - bool success = false; - Utilities.InvokeBlocking(() => success = BuildTarget(target, shipyardItem, welder)); - if (!success) - { - //toRemove.Add(target); - continue; - } - target.UpdateAfterBuild(); - } - } - if (target.Block.IsFullIntegrity && !target.Block.HasDeformation) - { - toRemove.Add(target); - continue; - } - nextTarget = target; - break; - } - - if (nextTarget != null) - { - targetsToRedraw.Add(nextTarget); - shipyardItem.BlocksToProcess[welder.EntityId][i] = nextTarget; - } - - foreach (BlockTarget removeTarget in toRemove) - { - shipyardItem.ProxDict[welder.EntityId].Remove(removeTarget); - shipyardItem.TargetBlocks.Remove(removeTarget); - } - } - } - - //update lasers - foreach (KeyValuePair entry in shipyardItem.BlocksToProcess) - { - for (int i = 0; i < entry.Value.Length; i++) - { - BlockTarget targetBlock = entry.Value[i]; - - if (targetBlock == null || _stalledTargets.Contains(targetBlock)) - continue; - - if (targetsToRedraw.Contains(targetBlock)) - { - var toolLine = new Communication.ToolLineStruct - { - ToolId = entry.Key, - GridId = targetBlock.CubeGrid.EntityId, - BlockPos = targetBlock.GridPosition, - PackedColor = Color.DarkCyan.PackedValue, - Pulse = false, - EmitterIndex = (byte)i - }; - - Communication.SendLine(toolLine, shipyardItem.ShipyardBox.Center); - } - } - } - - if (shipyardItem.BlocksToProcess.All(e => e.Value.All(t => t == null))) - return false; - - shipyardItem.UpdatePowerUse(); - targetsToRedraw.Clear(); - - Utilities.InvokeBlocking(() => - { - foreach (IMyCubeBlock welder in shipyardItem.Tools) - { - var tool = (IMyCollector)welder; - MyInventory welderInventory = ((MyEntity)tool).GetInventory(); - int i = 0; - foreach (BlockTarget target in shipyardItem.BlocksToProcess[tool.EntityId]) - { - if (target == null) - continue; - - if (target.CubeGrid.Physics == null || target.CubeGrid.Closed || target.CubeGrid.MarkedForClose) - { - targetsToRemove.Add(target); - continue; - } - - // if (MyAPIGateway.Session.CreativeMode) - // { - /* - * Welding laser "efficiency" is a float between 0-1 where: - * 0.0 => 0% of component stock used for construction (100% loss) - * 1.0 => 100% of component stock used for construction (0% loss) - * - * Efficiency decay/distance formula is the same as above for grinder - */ - - // Set efficiency to 1 because wtf ew no, good ideas aren't allowed - //double efficiency = 1 - (target.ToolDist[tool.EntityId] / 200000); - double efficiency = 1; - //if (!shipyardItem.StaticYard) - // efficiency /= 2; - if (efficiency < 0.1) - efficiency = 0.1; - //Logging.Instance.WriteDebug(String.Format("Welder[{0}]block[{1}] distance=[{2:F2}m] efficiency=[{3:F5}]", tool.DisplayNameText, i, Math.Sqrt(target.ToolDist[tool.EntityId]), efficiency)); - /* - * We have to factor in our efficiency ratio before transferring to the block "construction stockpile", - * but that math isn't nearly as easy as it was with the grinder. - * - * For each missing component type, we know how many items are "missing" from the construction model, M - * - * The simplest approach would be to pull M items from the conveyor system (if enabled), then move - * (M*efficiency) items to the "construction stockpile" and vaporize the other (M*(1-efficiency)) items. - * However, this approach would leave the construction stockpile incomplete and require several iterations - * before M items have actually been copied. - * - * A better solution is to pull enough "extra" items from the conveyors that the welder has enough to - * move M items to the construction stockpile even after losses due to distance inefficiency - * - * For example, if the target block is missing M=9 items and we are running at 0.9 (90%) efficiency, - * ideally that means we should pull 10 units, vaporize 1, and still have 9 for construction. However, - * if the conveyor system was only able to supply us with 2 components, we should not continue to blindly - * vaporize 1 unit. - * - * Instead, we have to consult the post-conveyor-pull welder inventory to determine if it has at least - * the required number of components. If it does, we vaporize M*(1-efficiency). Otherwise we only - * vaporize current_count*(1-efficiency) and transfer the rest to the construction stockpile - */ - var missingComponents = new Dictionary(); - target.Block.GetMissingComponents(missingComponents); - - var wasteComponents = new Dictionary(); - foreach (KeyValuePair entry in missingComponents) - { - var componentId = new MyDefinitionId(typeof(MyObjectBuilder_Component), entry.Key); - int missing = entry.Value; - MyFixedPoint totalRequired = (MyFixedPoint)(missing / efficiency); - - MyFixedPoint currentStock = welderInventory.GetItemAmount(componentId); - - //Logging.Instance.WriteDebug(String.Format("Welder[{0}]block[{1}] Component[{2}] missing[{3:F3}] inefficiency requires[{4:F3}] in_stock[{5:F3}]", tool.DisplayNameText, i, entry.Key, missing, totalRequired, currentStock)); - if (currentStock < totalRequired && tool.UseConveyorSystem) - { - // Welder doesn't have totalRequired, so try to pull the difference from conveyors - welderInventory.PullAny(shipyardItem.ConnectedCargo, entry.Key, (int)Math.Ceiling((double)(totalRequired - currentStock))); - currentStock = welderInventory.GetItemAmount(componentId); - //Logging.Instance.WriteDebug(String.Format("Welder[{0}]block[{1}] Component[{2}] - after conveyor pull - in_stock[{3:F3}]", tool.DisplayNameText, i, entry.Key, currentStock)); - } - - // Now compute the number of components to delete - // MoveItemsToConstructionPile() below won't move anything if we have less than 1 unit, - // so don't bother "losing" anything due to ineffeciency - if (currentStock >= 1) - { - // The lesser of (missing, currentStock), times (1 minus) our efficiency fraction - MyFixedPoint toDelete = MyFixedPoint.Min(MyFixedPoint.Floor(currentStock), missing) * (MyFixedPoint)(1 - efficiency); - //Logging.Instance.WriteDebug(String.Format("Welder[{0}]block[{1}] Component[{2}] amount lost due to distance [{3:F3}]", tool.DisplayNameText, i, entry.Key, toDelete)); - welderInventory.RemoveItemsOfType(toDelete, componentId); - } - } - - target.Block.MoveItemsToConstructionStockpile(welderInventory); - - missingComponents.Clear(); - target.Block.GetMissingComponents(missingComponents); - - if (missingComponents.Any() && !target.Block.HasDeformation) - { - if (_stalledTargets.Add(target)) - targetsToRedraw.Add(target); - } - else - { - if (_stalledTargets.Remove(target)) - targetsToRedraw.Add(target); - } - - target.Block.IncreaseMountLevel(weldAmount, 0, welderInventory, boneAmount, true); - if (target.Block.IsFullIntegrity && !target.Block.HasDeformation) - targetsToRemove.Add(target); - // } - i++; - } - } - }); - - shipyardItem.MissingComponentsDict.Clear(); - - foreach (KeyValuePair entry in shipyardItem.BlocksToProcess) - { - for (int i = 0; i < entry.Value.Length; i++) - { - BlockTarget target = entry.Value[i]; - - if (target == null) - continue; - - if (targetsToRemove.Contains(target)) - { - Communication.ClearLine(entry.Key, i); - _stalledTargets.Remove(target); - shipyardItem.TargetBlocks.Remove(target); - entry.Value[i] = null; - continue; - } - - if (_stalledTargets.Contains(target)) - { - var blockComponents = new Dictionary(); - target.Block.GetMissingComponents(blockComponents); - - foreach (KeyValuePair component in blockComponents) - { - if (shipyardItem.MissingComponentsDict.ContainsKey(component.Key)) - shipyardItem.MissingComponentsDict[component.Key] += component.Value; - else - shipyardItem.MissingComponentsDict.Add(component.Key, component.Value); - } - - var toolLine = new Communication.ToolLineStruct - { - ToolId = entry.Key, - GridId = target.CubeGrid.EntityId, - BlockPos = target.GridPosition, - PackedColor = Color.Purple.PackedValue, - Pulse = true, - EmitterIndex = (byte)i - }; - - if (targetsToRedraw.Contains(target)) - { - Communication.ClearLine(entry.Key, i); - Communication.SendLine(toolLine, shipyardItem.ShipyardBox.Center); - } - continue; - } - } - } - - return true; - } - - private bool BuildTarget(BlockTarget target, ShipyardItem item, IMyCubeBlock tool) - { - IMyProjector projector = target.Projector; - IMySlimBlock block = target.Block; - - if (projector == null || block == null) - return false; - - if (projector.CanBuild(block, false) != BuildCheckResult.OK) - return false; - - if (MyAPIGateway.Session.CreativeMode) - { - projector.Build(block, projector.OwnerId, projector.EntityId, false, projector.OwnerId); - return projector.CanBuild(block, true) != BuildCheckResult.OK; - } - - //try to remove the first component from inventory - string name = ((MyCubeBlockDefinition)block.BlockDefinition).Components[0].Definition.Id.SubtypeName; - if (_tmpInventory.PullAny(item.ConnectedCargo, name, 1)) - { - _tmpInventory.Clear(); - - projector.Build(block, projector.OwnerId, projector.EntityId, false, projector.OwnerId); - - return projector.CanBuild(block, true) != BuildCheckResult.OK; - } - - return false; - } - } -}using System.Collections.Generic; -using System.Linq; -using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.Settings; -using ShipyardMod.Utility; -using VRage.Game.Entity; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace ShipyardMod.ProcessHandlers -{ - public class ProcessShipyardDetection : ProcessHandlerBase - { - public static HashSet ShipyardsList = new HashSet(); - private readonly List _corners = new List(); - private readonly string FullName = typeof(ProcessShipyardDetection).FullName; - - public override int GetUpdateResolution() - { - return 5000; - } - - public override bool ServerOnly() - { - return true; - } - - public override void Handle() - { - var tmpEntities = new HashSet(); - MyAPIGateway.Entities.GetEntities(tmpEntities); - if (tmpEntities.Count == 0) - { - Logging.Instance.WriteLine("Failed to get list of entities in ShipyardDetection."); - return; - } - - //copy the list of entities because concurrency - IMyEntity[] entities = tmpEntities.ToArray(); - - //run through our current list of shipyards and make sure they're still valid - var itemsToRemove = new HashSet(); - var firstCheckBlock = Profiler.Start(FullName, nameof(Handle), "First Check"); - foreach (ShipyardItem item in ShipyardsList) - { - if (!AreToolsConnected(item.Tools)) - { - Logging.Instance.WriteLine("remove item tools " + item.Tools.Length); - item.Disable(); - itemsToRemove.Add(item); - foreach(var tool in item.Tools) - Communication.SendCustomInfo(tool.EntityId, "Invalid Shipyard: All tools must be on the same conveyor network!"); - continue; - } - - if (item.Tools.Any(x => x.Closed || x.MarkedForClose)) - { - Logging.Instance.WriteLine("remove item closed tools " + item.Tools.Length); - item.Disable(); - itemsToRemove.Add(item); - continue; - } - - if (!entities.Contains(item.YardEntity) || item.YardEntity.Closed || item.YardEntity.MarkedForClose) - { - Logging.Instance.WriteLine("remove item entity"); - item.Disable(); - itemsToRemove.Add(item); - continue; - } - - using (Profiler.Start(FullName, nameof(Handle), "Physics Check")) - { - if (item.YardEntity.Physics == null - || item.StaticYard && (!item.YardEntity.Physics.IsStatic) ) - { - Logging.Instance.WriteLine("remove item physics"); - itemsToRemove.Add(item); - item.Disable(); - foreach (var tool in item.Tools) - Communication.SendCustomInfo(tool.EntityId, "Invalid Shipyard: Shipyard must be Static!"); - continue; - } - } - - if (item.Tools.Any(t => ((IMyTerminalBlock)t).CustomInfo.Contains("Invalid Shipyard"))) - { - foreach (var tool in item.Tools) - Communication.SendCustomInfo(tool.EntityId, string.Empty); - } - } - firstCheckBlock.End(); - - foreach (ShipyardItem item in itemsToRemove) - { - item.YardType = ShipyardType.Invalid; - Communication.SendYardState(item); - ShipyardsList.Remove(item); - } - - foreach (IMyEntity entity in entities) - { - _corners.Clear(); - var grid = entity as IMyCubeGrid; - - if (grid?.Physics == null || grid.Closed || grid.MarkedForClose ) - continue; - - if (ShipyardsList.Any(x => x.EntityId == entity.EntityId)) - continue; - - var gridBlocks = new List(); - grid.GetBlocks(gridBlocks); - - foreach (IMySlimBlock slimBlock in gridBlocks) - { - var collector = slimBlock.FatBlock as IMyCollector; - if (collector == null) - continue; - - if (collector.BlockDefinition.SubtypeId.StartsWith("ShipyardCorner")) - { - _corners.Add(slimBlock.FatBlock); - } - } - - if (_corners.Count != 8) - { - foreach (var tool in _corners) - Communication.SendCustomInfo(tool.EntityId, $"Invalid Shipyard: Must be 8 corner blocks, there are {_corners.Count} on this grid!"); - continue; - } - - if (_corners.Any(c => c.BlockDefinition.SubtypeId != _corners[0].BlockDefinition.SubtypeId)) - { - foreach (var tool in _corners) - Communication.SendCustomInfo(tool.EntityId, $"Invalid Shipyard: All 8 corner blocks must be the same type!"); - continue; - } - - using (Profiler.Start(FullName, nameof(Handle), "Static Check")) - { - if (_corners[0].BlockDefinition.SubtypeId == "ShipyardCorner_Large" && !ShipyardCore.Debug) - { - if (!grid.IsStatic) - { - Logging.Instance.WriteDebug($"Yard {grid.EntityId} failed: Static check"); - foreach (var tool in _corners) - Communication.SendCustomInfo(tool.EntityId, "Invalid Shipyard: Shipyard must be static!"); - continue; - } - } - } - - if (!IsYardValid(entity, _corners)) - continue; - - //add an offset of 2.5m because the corner points are at the center of a 3^3 block, and the yard will be 2.5m short in all dimensions - MyOrientedBoundingBoxD testBox = MathUtility.CreateOrientedBoundingBox((IMyCubeGrid)entity, _corners.Select(x => x.GetPosition()).ToList(), 2.5); - - Logging.Instance.WriteLine("Found yard"); - var item = new ShipyardItem( - testBox, - _corners.ToArray(), - ShipyardType.Disabled, - entity); - item.Settings = ShipyardSettings.Instance.GetYardSettings(item.EntityId); - foreach (IMyCubeBlock tool in _corners) - item.BlocksToProcess.Add(tool.EntityId, new BlockTarget[3]); - - ShipyardsList.Add(item); - Communication.SendNewYard(item); - foreach (var tool in item.Tools) - Communication.SendCustomInfo(tool.EntityId, ""); - } - - Communication.SendYardCount(); - } - - /// - /// This makes sure all the tools are connected to the same conveyor system - /// - /// - /// - private bool AreToolsConnected(IReadOnlyList tools) - { - bool found = true; - - if (tools.Any(x => x.Closed || x.MarkedForClose)) - { - Logging.Instance.WriteDebug("tools closed?"); - return false; - } - - Utilities.InvokeBlocking(() => - { - using (Profiler.Start(FullName, nameof(AreToolsConnected))) - { - IMyInventory toolInventory = ((MyEntity)tools[0]).GetInventory(); - - if (toolInventory == null) - { - Logging.Instance.WriteDebug("null toolInventory"); - return; - } - - for (int i = 1; i < tools.Count; ++i) - { - IMyInventory compareInventory = ((MyEntity)tools[i]).GetInventory(); - - if (compareInventory == null) - { - Logging.Instance.WriteDebug($"Null inventory at {i}"); - found = false; - return; - } - - if (!toolInventory.IsConnectedTo(compareInventory)) - { - Logging.Instance.WriteDebug($"Tool not connected at {i}"); - found = false; - return; - } - } - } - }); - - return found; - } - - // OBB corner structure - // ZMax ZMin - // 0----1 4----5 - // | | | | - // | | | | - // 3----2 7----6 - - /// - /// Makes sure the shipyard has a complete frame made of shipyard conveyor blocks - /// - /// - /// - private bool IsFrameComplete(ShipyardItem item) - { - using (Profiler.Start(FullName, nameof(IsFrameComplete))) - { - var corners = new Vector3D[8]; - item.ShipyardBox.GetCorners(corners, 0); - - var gridCorners = new Vector3I[8]; - for (int i = 0; i < 8; i++) - gridCorners[i] = ((IMyCubeGrid)item.YardEntity).WorldToGridInteger(corners[i]); - - LinePair[] lines = - { - new LinePair(0, 1), - new LinePair(0, 4), - new LinePair(1, 2), - new LinePair(1, 5), - new LinePair(2, 3), - new LinePair(2, 6), - new LinePair(3, 0), - new LinePair(3, 7), - new LinePair(4, 5), - new LinePair(5, 6), - new LinePair(6, 7), - new LinePair(7, 4), - }; - - var grid = (IMyCubeGrid)item.YardEntity; - foreach (LinePair line in lines) - { - var it = new MathUtility.Vector3ILineIterator(gridCorners[line.Start], gridCorners[line.End]); - while (it.IsValid()) - { - IMySlimBlock block = grid.GetCubeBlock(it.Current); - it.MoveNext(); - if (block == null) - return false; - - if (!block.BlockDefinition.Id.SubtypeName.Contains("Shipyard")) - return false; - - if (block.BuildPercent() < .8) - return false; - } - } - - return true; - } - } - - /// - /// Checks if tools are on the same cargo system and are arranged orthogonally. - /// - /// - /// - /// - private bool IsYardValid(IMyEntity entity, List tools) - { - using (Profiler.Start(FullName, nameof(IsYardValid))) - { - var gridPoints = new List(); - foreach (IMyCubeBlock tool in tools) - { - Vector3D point = tool.PositionComp.GetPosition(); - //the grid position is not consistent with rotation, but world position is always the center of the block - //get the Vector3I of the center of the block and calculate APO on that - Vector3I adjustedPoint = ((IMyCubeGrid)entity).WorldToGridInteger(point); - gridPoints.Add(adjustedPoint); - } - - if (!MathUtility.ArePointsOrthogonal(gridPoints)) - { - Logging.Instance.WriteDebug($"Yard {entity.EntityId} failed: APO"); - foreach (var tool in tools) - Communication.SendCustomInfo(tool.EntityId, "Invalid Shipyard: Corners not aligned!"); - return false; - } - - if (!AreToolsConnected(tools)) - { - Logging.Instance.WriteDebug($"Yard {entity.EntityId} failed: ATC"); - foreach (var tool in tools) - Communication.SendCustomInfo(tool.EntityId, "Invalid Shipyard: All tools must be on the same conveyor network!"); - return false; - } - - return true; - } - } - - private struct LinePair - { - public LinePair(int start, int end) - { - Start = start; - End = end; - } - - public readonly int Start; - public readonly int End; - } - } -}using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Xml.Serialization; -using Sandbox.ModAPI; - -namespace ShipyardMod.Utility -{ - public static class Profiler - { - [Serializable] - public class Namespace - { - public List Classes; - public string Name; - - public Namespace() - { - } - - public Namespace(string name) - { - Name = name; - Classes = new List(); - } - } - - [Serializable] - public class Class - { - public double AvgRuntime; - public double MaxRuntime; - public List Members; - public string Name; - [XmlIgnore] - public List Runtimes; - - public Class() - { - } - - public Class(string name) - { - Name = name; - Members = new List(); - Runtimes = new List(); - } - } - - [Serializable] - public class Member - { - public double AvgRuntime; - public List Blocks; - public double MaxRuntime; - public string Name; - [XmlIgnore] - public List Runtimes; - - public Member() - { - } - - public Member(string name) - { - Name = name; - Blocks = new List(); - Runtimes = new List(); - } - } - - [Serializable] - public class Block - { - public double AvgRuntime; - public double MaxRuntime; - public string Name; - [XmlIgnore] - public List Runtimes; - - public Block() - { - } - - public Block(string name) - { - Name = name; - Runtimes = new List(); - } - } - - public static ProfilingBlockBase EmptyBlock = new ProfilingBlockBase(); - - public class ProfilingBlockBase : IDisposable - { - public virtual void End() { } - - public void Dispose() - { - End(); - } - } - - public class ProfilingBlock : ProfilingBlockBase - { - public readonly string Block; - public readonly string Class; - public readonly string Member; - - public readonly string Namespace; - public readonly Stopwatch Stopwatch; - private bool _stopped; - - public ProfilingBlock(string namespaceName, string className, string memberName = null, string blockName = null) - { - Namespace = namespaceName; - Class = className; - Member = memberName; - Block = blockName; - Stopwatch = new Stopwatch(); - } - - public override void End() - { - if (_stopped) - return; - - _stopped = true; - - Profiler.End(this); - } - } - - private static readonly List Namespaces = new List(); - - public static ProfilingBlockBase Start(string className, string memberName = null, string blockName = null) - { - if(!ShipyardCore.Debug) - return EmptyBlock; - - string[] splits = className.Split('.'); - - var profileblock = new ProfilingBlock(splits[1], splits[2], memberName, blockName); - profileblock.Stopwatch.Start(); - return profileblock; - } - - private static void End(ProfilingBlock profilingBlock) - { - profilingBlock.Stopwatch.Stop(); - - Utilities.QueueAction(() => - { - try - { - double runtime = 1000d * profilingBlock.Stopwatch.ElapsedTicks / Stopwatch.Frequency; - - Namespace thisNamespace = Namespaces.FirstOrDefault(n => n.Name == profilingBlock.Namespace); - if (thisNamespace == null) - { - thisNamespace = new Namespace(profilingBlock.Namespace); - Namespaces.Add(thisNamespace); - } - Class thisClass = thisNamespace.Classes.FirstOrDefault(c => c.Name == profilingBlock.Class); - if (thisClass == null) - { - thisClass = new Class(profilingBlock.Class); - thisNamespace.Classes.Add(thisClass); - } - - if (profilingBlock.Member == null) - { - if (thisClass.Runtimes.Count >= int.MaxValue) - thisClass.Runtimes.RemoveAt(0); - thisClass.Runtimes.Add(runtime); - thisClass.MaxRuntime = thisClass.Runtimes.Max(); - thisClass.AvgRuntime = thisClass.Runtimes.Average(); - return; - } - - Member thisMember = thisClass.Members.FirstOrDefault(m => m.Name == profilingBlock.Member); - if (thisMember == null) - { - thisMember = new Member(profilingBlock.Member); - thisClass.Members.Add(thisMember); - } - - if (profilingBlock.Block == null) - { - if (thisMember.Runtimes.Count >= int.MaxValue) - thisMember.Runtimes.RemoveAt(0); - thisMember.Runtimes.Add(runtime); - thisMember.MaxRuntime = thisMember.Runtimes.Max(); - thisMember.AvgRuntime = thisMember.Runtimes.Average(); - return; - } - - Block thisBlock = thisMember.Blocks.FirstOrDefault(b => b.Name == profilingBlock.Block); - if (thisBlock == null) - { - thisBlock = new Block(profilingBlock.Block); - thisMember.Blocks.Add(thisBlock); - } - - if (thisBlock.Runtimes.Count >= int.MaxValue) - thisBlock.Runtimes.RemoveAt(0); - thisBlock.Runtimes.Add(runtime); - thisBlock.MaxRuntime = thisBlock.Runtimes.Max(); - thisBlock.AvgRuntime = thisBlock.Runtimes.Average(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine(ex.ToString()); - } - }); - } - - private static bool _saving; - - public static void Save() - { - if (_saving) - return; - _saving = true; - MyAPIGateway.Parallel.Start(() => - { - TextWriter writer = MyAPIGateway.Utilities.WriteFileInLocalStorage("profiler.xml", typeof(Profiler)); - - writer.Write(MyAPIGateway.Utilities.SerializeToXML(Namespaces)); - writer.Flush(); - - writer.Close(); - _saving = false; - }); - } - } -}using ShipyardMod.Utility; -using VRage.Game; -using VRage.Utils; -using VRageMath; - -namespace ShipyardMod.ItemClasses -{ - public class ScanAnimation - { - //each sweep takes 3 seconds (times 60 updates) - //Lerp takes a percentage, to just take the reciprocal of 3*60 - private const double MULTIPLIER = 1d / 180d; - private static readonly MyStringId TextureId = MyStringId.GetOrCompute("ScanTexture"); - private readonly Vector4 _color = Color.Green.ToVector4(); - private readonly Vector3D[] _endpoints = new Vector3D[4]; - - private readonly ShipyardItem _shipyardItem; - private bool _init; - private ScanLine _line; - private bool _scanningZ = true; - private int _ticks; - - public ScanAnimation(ShipyardItem item) - { - _shipyardItem = item; - } - - // OBB corner structure - // ZMax ZMin - // 0----1 4----5 - // | | | | - // | | | | - // 3----2 7----6 - - private void Init() - { - var corners = new Vector3D[8]; - _shipyardItem.ShipyardBox.GetCorners(corners, 0); - - //our endpoints are in the center of the faces - //z plane - _endpoints[0] = (corners[0] + corners[1] + corners[2] + corners[3]) / 4; - _endpoints[1] = (corners[4] + corners[5] + corners[6] + corners[7]) / 4; - // x plane - _endpoints[2] = (corners[2] + corners[3] + corners[6] + corners[7]) / 4; - _endpoints[3] = (corners[0] + corners[1] + corners[4] + corners[5]) / 4; - - /* - * Scanning Z moves from [0] to [4] - * Scanning X moves from [0] to [3] - */ - - //start by scanning the line on the z plane, from zmax to zmin - _line = new ScanLine - { - Origin = _endpoints[0], - //get half the dimensions for each face - ZWidth = (float)_shipyardItem.ShipyardBox.HalfExtent.X, - ZLength = (float)_shipyardItem.ShipyardBox.HalfExtent.Y, - XWidth = (float)_shipyardItem.ShipyardBox.HalfExtent.X, - XLength = (float)_shipyardItem.ShipyardBox.HalfExtent.Z, - //we need the up and left vectors to align the billboard to the shipyard grid - ZLeft = _shipyardItem.ShipyardBox.Orientation.Right, - ZUp = -_shipyardItem.ShipyardBox.Orientation.Up, - XLeft = -_shipyardItem.ShipyardBox.Orientation.Right, - XUp = _shipyardItem.ShipyardBox.Orientation.Forward - }; - } - - public bool Draw() - { - if (!Update()) - return false; - - //draw the texture oriented to the shipyard grid - if (_scanningZ) - MyTransparentGeometry.AddBillboardOriented(TextureId, _color, _line.Origin, _line.ZLeft, _line.ZUp, _line.ZWidth, _line.ZLength); - else - MyTransparentGeometry.AddBillboardOriented(TextureId, _color, _line.Origin, _line.XLeft, _line.XUp, _line.XWidth, _line.XLength); - - return true; - } - - private bool Update() - { - if (!_init) - { - _init = true; - Init(); - } - - if (_scanningZ) - { - //calculate the next position - _line.Origin = Vector3D.Lerp(_endpoints[0], _endpoints[1], _ticks * MULTIPLIER); - - //line has reached the end - //flip the flag so we start scanning the x plane - if (_ticks * MULTIPLIER >= 1) - { - _ticks = 0; - _scanningZ = false; - } - } - else - { - _line.Origin = Vector3D.Lerp(_endpoints[2], _endpoints[3], _ticks * MULTIPLIER); - - //line has reached the end - //we're done, so return false to let the caller know to stop drawing - if (_ticks * MULTIPLIER >= 1) - { - return false; - } - } - - _ticks++; - return true; - } - - private class ScanLine - { - public Vector3D Origin; - public Vector3D XLeft; - public float XLength; - public Vector3D XUp; - public float XWidth; - public Vector3D ZLeft; - public float ZLength; - public Vector3D ZUp; - public float ZWidth; - } - } -}using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using ParallelTasks; -using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.ProcessHandlers; -using ShipyardMod.Settings; -using ShipyardMod.Utility; -using VRage.Collections; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRage.Utils; -using VRageMath; -using static VRageRender.MyBillboard; - -/* Hello there! - * - * One of the best things about the SE modding community is sharing information, so - * if you find something in here useful or interesting, feel free to use it in your own mod! - * I just ask that you leave a comment saying something like 'shamelessly stolen from rexxar' :) - * Or consider donating a few dollars at https://paypal.me/rexxar if you're able. - * Or hell, even just leaving a comment on the mod page or the forum or wherever letting me know - * you found an interesting tidbit in here is good enough for me. - * - * This mod never would have happened without the huge amount of help from the other modders - * in the KSH Discord server. Come hang out with some really cool people: https://discord.gg/Dqfhtuu - * - * - * This mod is something I've wanted in the game since I started playing SE. I spent over 8 - * months on it, and I sincerely hope you enjoy it :) - * - * <3 rexxar - */ - -namespace ShipyardMod -{ - [MySessionComponentDescriptor(MyUpdateOrder.BeforeSimulation)] - public class ShipyardCore : MySessionComponentBase - { - private const string Version = "v2.5"; - - //TODO - public static volatile bool Debug = false; - - //private readonly List _tasks = new List(); - private static Task _task; - public static readonly MyConcurrentDictionary BoxDict = new MyConcurrentDictionary(); - - private bool _initialized; - - private DateTime _lastMessageTime = DateTime.Now; - private ProcessHandlerBase[] _processHandlers; - private int _updateCount; - - private void Initialize() - { - AddMessageHandler(); - - _processHandlers = new ProcessHandlerBase[] - { - new ProcessShipyardAction(), - new ProcessLocalYards(), - new ProcessLCDMenu(), - new ProcessShipyardDetection(), - new ProcessConveyorCache(), - }; - - Logging.Instance.WriteLine($"Shipyard Script Initialized: {Version}"); - } - - private void HandleMessageEntered(string messageText, ref bool sendToOthers) - { - string messageLower = messageText.ToLower(); - - if (!messageLower.StartsWith("/shipyard")) - return; - - if (DateTime.Now - _lastMessageTime < TimeSpan.FromMilliseconds(200)) - return; - - if (messageLower.Equals("/shipyard debug on")) - { - Logging.Instance.WriteLine("Debug turned on"); - Debug = true; - } - else if (messageLower.Equals("/shipyard debug off")) - { - Logging.Instance.WriteLine("Debug turned off"); - Debug = false; - } - - _lastMessageTime = DateTime.Now; - - sendToOthers = false; - - byte[] commandBytes = Encoding.UTF8.GetBytes(messageLower); - byte[] idBytes = BitConverter.GetBytes(MyAPIGateway.Session.Player.SteamUserId); - - var message = new byte[commandBytes.Length + sizeof(ulong)]; - - idBytes.CopyTo(message, 0); - commandBytes.CopyTo(message, idBytes.Length); - - Communication.SendMessageToServer(Communication.MessageTypeEnum.ClientChat, message); - } - - private void CalculateBoxesContaining() - { - foreach (ShipyardItem item in ProcessLocalYards.LocalYards) - { - foreach (IMyCubeGrid grid in item.ContainsGrids) - { - if (item.YardType != ShipyardType.Disabled || grid.Closed || !ShipyardSettings.Instance.GetYardSettings(item.EntityId).GuideEnabled) - { - BoxDict.Remove(grid.EntityId); - continue; - } - //if (BoxDict.ContainsKey(grid.EntityId) && Vector3D.DistanceSquared(BoxDict[grid.EntityId].LastPos, grid.GetPosition()) < 0.01) - // continue; - - uint color; - - if (grid.Physics != null) - color = Color.Green.PackedValue; - else - { - var proj = grid.Projector(); - - if (proj == null) //ghost grid like Digi's helmet - continue; - - if (proj.RemainingBlocks == 0) //projection is complete - continue; - - color = Color.Cyan.PackedValue; - } - - BoxDict[grid.EntityId] = new BoxItem - { - Lines = MathUtility.CalculateObbLines(MathUtility.CreateOrientedBoundingBox(grid)), - GridId = grid.EntityId, - //PackedColor = grid.Physics == null ? Color.Cyan.PackedValue : Color.Green.PackedValue, - PackedColor = color, - LastPos = grid.GetPosition() - }; - } - } - } - - private void CalculateBoxesIntersecting() - { - foreach (var item in ProcessLocalYards.LocalYards) - { - foreach (IMyCubeGrid grid in item.IntersectsGrids) - { - if (item.YardType != ShipyardType.Disabled || grid.Closed || !ShipyardSettings.Instance.GetYardSettings(item.EntityId).GuideEnabled) - { - BoxDict.Remove(grid.EntityId); - continue; - } - //if (BoxDict.ContainsKey(grid.EntityId) && Vector3D.DistanceSquared(BoxDict[grid.EntityId].LastPos, grid.GetPosition()) < 0.01) - // continue; - - uint color; - - if (grid.Physics != null) - color = Color.Yellow.PackedValue; - else - { - var proj = grid.Projector(); - - if (proj == null) //ghost grid like Digi's helmet - continue; - - if (proj.RemainingBlocks == 0) //projection is complete - continue; - - color = Color.CornflowerBlue.PackedValue; - } - - BoxDict[grid.EntityId] = new BoxItem - { - Lines = MathUtility.CalculateObbLines(MathUtility.CreateOrientedBoundingBox(grid)), - GridId = grid.EntityId, - //PackedColor = grid.Physics == null ? Color.CornflowerBlue.PackedValue : Color.Yellow.PackedValue, - PackedColor = color, - LastPos = grid.GetPosition() - }; - } - } - } - - private void CalculateLines() - { - foreach (var e in Communication.LineDict) - { - foreach (var line in e.Value) - { - line.Start = MathUtility.CalculateEmitterOffset(line.EmitterBlock, line.Index); - var target = line.TargetGrid.GetCubeBlock(line.TargetBlock); - if (target == null || target.Closed()) - continue; - - line.End = target.GetPosition(); - - if (line.LinePackets != null) - { - line.LinePackets.Update(line.Start, line.End); - } - } - } - } - - private void AddMessageHandler() - { - MyAPIGateway.Utilities.MessageEntered += HandleMessageEntered; - Communication.RegisterHandlers(); - } - - private void RemoveMessageHandler() - { - MyAPIGateway.Utilities.MessageEntered -= HandleMessageEntered; - Communication.UnregisterHandlers(); - } - - public override void Draw() - { - if (MyAPIGateway.Session?.Player == null || !_initialized) - return; - - try - { - //these tasks are too simple to use Parallel.ForEach or similar in the body, but - //can all safely be run simultaneously, so do that. - var t1 = MyAPIGateway.Parallel.Start(CalculateBoxesContaining); - var t2 = MyAPIGateway.Parallel.Start(CalculateBoxesIntersecting); - var t3 = MyAPIGateway.Parallel.Start(CalculateLines); - //wait for all three to finish - t1.Wait(); - t2.Wait(); - t3.Wait(); - DrawLines(); - FadeLines(); - DrawScanning(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine($"Draw(): {ex}"); - MyLog.Default.WriteLineAndConsole("##SHIPYARD MOD: ENCOUNTERED ERROR DURING DRAW UPDATE. CHECK MOD LOG"); - if (Debug) - throw; - } - } - - public override void UpdateBeforeSimulation() - { - try - { - if (MyAPIGateway.Session == null) - return; - - if (!_initialized) - { - _initialized = true; - Initialize(); - } - - RunProcessHandlers(); - - foreach (var item in ProcessShipyardDetection.ShipyardsList) - { - if (item.StaticYard) - { - foreach (IMyCubeGrid yardGrid in item.YardGrids) - yardGrid.Stop(); - } - else - { - item.UpdatePosition(); - item.NudgeGrids(); - } - } - - foreach (var item in ProcessLocalYards.LocalYards) - { - if (!item.StaticYard) - item.UpdatePosition(); - } - - if (_updateCount++ % 10 != 0) - return; - - CheckAndDamagePlayer(); - Utilities.ProcessActionQueue(); - - if (Debug) - Profiler.Save(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine($"UpdateBeforeSimulation(): {ex}"); - MyLog.Default.WriteLineAndConsole("##SHIPYARD MOD: ENCOUNTERED ERROR DURING MOD UPDATE. CHECK MOD LOG"); - if (Debug) - throw; - } - } - - private void CheckAndDamagePlayer() - { - var character = MyAPIGateway.Session.Player?.Controller?.ControlledEntity?.Entity as IMyCharacter; - - if (character == null) - return; - - var damageBlock = Profiler.Start("0.ShipyardMod.ShipyardCore", nameof(CheckAndDamagePlayer)); - BoundingBoxD charbox = character.WorldAABB; - - MyAPIGateway.Parallel.ForEach(Communication.LineDict.Values.ToArray(), lineList => - { - foreach (LineItem line in lineList) - { - var ray = new Ray(line.Start, line.End - line.Start); - double? intersection = charbox.Intersects(ray); - if (intersection.HasValue) - { - if (Vector3D.DistanceSquared(charbox.Center, line.Start) < Vector3D.DistanceSquared(line.Start, line.End)) - { - Utilities.Invoke(() => character.DoDamage(5, MyStringHash.GetOrCompute("ShipyardLaser"), true)); - } - } - } - }); - damageBlock.End(); - } - - private void RunProcessHandlers() - { - //wait for execution to complete before starting up a new thread - if (!_task.IsComplete) - return; - - //exceptions are suppressed in tasks, so re-throw if one happens - if (_task.Exceptions != null && _task.Exceptions.Length > 0) - { - MyLog.Default.WriteLineAndConsole("##SHIPYARD MOD: THREAD EXCEPTION, CHECK MOD LOG FOR MORE INFO."); - MyLog.Default.WriteLineAndConsole("##SHIPYARD MOD: EXCEPTION: " + _task.Exceptions[0]); - if (Debug) - throw _task.Exceptions[0]; - } - - //run all process handlers in serial so we don't have to design for concurrency - _task = MyAPIGateway.Parallel.Start(() => - { - string handlerName = ""; - try - { - var processBlock = Profiler.Start("0.ShipyardMod.ShipyardCore", nameof(RunProcessHandlers)); - foreach (ProcessHandlerBase handler in _processHandlers) - { - if (handler.CanRun()) - { - handlerName = handler.GetType().Name; - var handlerBlock = Profiler.Start(handler.GetType().FullName); - //Logging.Instance.WriteDebug(handlerName + " start"); - handler.Handle(); - handler.LastUpdate = DateTime.Now; - handlerBlock.End(); - } - } - processBlock.End(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine($"Thread Exception: {handlerName}: {ex}"); - Logging.Instance.Debug_obj("Thread exception! Check the log!"); - throw; - } - }); - } - - private void DrawScanning() - { - var toRemove = new List(); - foreach (ScanAnimation animation in Communication.ScanList) - { - if (!animation.Draw()) - toRemove.Add(animation); - } - - foreach (ScanAnimation removeAnim in toRemove) - Communication.ScanList.Remove(removeAnim); - } - - private void DrawLines() - { - foreach (KeyValuePair> kvp in Communication.LineDict) - { - foreach (LineItem line in kvp.Value) - { - if (Communication.FadeList.Any(x => x.Start == line.Start)) - continue; - - if (line.Pulse) - { - PulseLines(line); - continue; - } - - line.LinePackets?.DrawPackets(); - - MySimpleObjectDraw.DrawLine(line.Start, line.End, MyStringId.GetOrCompute("ShipyardLaser"), ref line.Color, 0.4f, BlendTypeEnum.SDR); - } - } - - foreach (KeyValuePair entry in BoxDict) - { - BoxItem box = entry.Value; - Vector4 color = new Color(box.PackedColor).ToVector4(); - foreach (LineItem line in box.Lines) - { - MySimpleObjectDraw.DrawLine(line.Start, line.End, MyStringId.GetOrCompute("WeaponLaserIgnoreDepth"), ref color, 1f, BlendTypeEnum.SDR); - } - } - - foreach (ShipyardItem item in ProcessLocalYards.LocalYards) - { - Vector4 color = Color.White; - if (item.YardType == ShipyardType.Disabled || item.YardType == ShipyardType.Invalid) - continue; - - foreach (LineItem line in item.BoxLines) - { - MySimpleObjectDraw.DrawLine(line.Start, line.End, MyStringId.GetOrCompute("WeaponLaserIgnoreDepth"), ref color, 1f, BlendTypeEnum.SDR); - } - } - } - - private void PulseLines(LineItem item) - { - if (item.Descend) - item.PulseVal -= 0.025; - else - item.PulseVal += 0.025; - - Vector4 drawColor = item.Color; - drawColor.W = (float)((Math.Sin(item.PulseVal) + 1) / 2); - if (drawColor.W <= 0.05) - item.Descend = !item.Descend; - MySimpleObjectDraw.DrawLine(item.Start, item.End, MyStringId.GetOrCompute("ShipyardLaser"), ref drawColor, drawColor.W * 0.4f, BlendTypeEnum.SDR); - } - - private void FadeLines() - { - var linesToRemove = new List(); - foreach (LineItem line in Communication.FadeList) - { - line.FadeVal -= 0.075f; - if (line.FadeVal <= 0) - { - //blank the line for a couple frames. Looks better that way. - if (line.FadeVal <= -0.2f) - linesToRemove.Add(line); - continue; - } - Vector4 drawColor = line.Color; - //do a cubic fade out - drawColor.W = line.FadeVal * line.FadeVal * line.FadeVal; - MySimpleObjectDraw.DrawLine(line.Start, line.End, MyStringId.GetOrCompute("ShipyardLaser"), ref drawColor, drawColor.W * 0.4f, BlendTypeEnum.SDR); - } - - foreach (LineItem removeLine in linesToRemove) - { - Communication.FadeList.Remove(removeLine); - } - } - - protected override void UnloadData() - { - try - { - Utilities.SessionClosing = true; - - if (Utilities.AbortAllTasks()) - Logging.Instance.WriteDebug("CAUGHT AND ABORTED TASK!!!!"); - - RemoveMessageHandler(); - - if (Logging.Instance != null) - Logging.Instance.Close(); - - Communication.UnregisterHandlers(); - - foreach (ShipyardItem yard in ProcessShipyardDetection.ShipyardsList.ToArray()) - yard.Disable(false); - } - catch - { - //ignore errors on session close - } - } - } -} -using System; -using System.Collections.Generic; -using System.Text; -using Sandbox.Common.ObjectBuilders; -using Sandbox.Game.EntityComponents; -using Sandbox.ModAPI; -using Sandbox.ModAPI.Interfaces; -using Sandbox.ModAPI.Interfaces.Terminal; -using ShipyardMod.ItemClasses; -using ShipyardMod.Settings; -using ShipyardMod.Utility; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; -using VRage.ObjectBuilders; -using VRage.Utils; -using VRageRender.Import; -using Sandbox.Engine.Physics; -using System.Text.RegularExpressions; - -namespace ShipyardMod -{ - [MyEntityComponentDescriptor(typeof(MyObjectBuilder_Collector), false, "ShipyardCorner_Large", "ShipyardCorner_Small")] - public class ShipyardCorner : MyGameLogicComponent - { - private static bool _init; - private static readonly MyDefinitionId PowerDef = MyResourceDistributorComponent.ElectricityId; - private static readonly List Controls = new List(); - private IMyCollector _block; - private float _maxpower; - private float _power; - private string _info = String.Empty; - - private MyResourceSinkComponent _sink = new MyResourceSinkComponent(); - - public ShipyardItem Shipyard = null; - - public override void Init(MyObjectBuilder_EntityBase objectBuilder) - { - _block = (IMyCollector)Container.Entity; - _block.Components.TryGet(out _sink); - //_block.NeedsUpdate = MyEntityUpdateEnum.NONE; - NeedsUpdate |= MyEntityUpdateEnum.BEFORE_NEXT_FRAME; - NeedsUpdate |= MyEntityUpdateEnum.EACH_FRAME; - NeedsUpdate |= MyEntityUpdateEnum.EACH_10TH_FRAME; - _block.OnClosing += OnClosing; - _block.AppendingCustomInfo += AppendingCustomInfo; - } - - private void OnClosing(IMyEntity obj) - { - _block.OnClosing -= OnClosing; - _block.AppendingCustomInfo -= AppendingCustomInfo; - NeedsUpdate = MyEntityUpdateEnum.NONE; - } - - public override void Close() - { - NeedsUpdate = MyEntityUpdateEnum.NONE; - } - - public override void UpdateAfterSimulation() - { - if (_block?.CubeGrid?.Physics == null) - { - return; - } - - var pos = _block.GetPosition(); - var offset = 2.5f; - var mat = _block.WorldMatrix; - float width = 0.125f; - float length = 200.0f + offset; - pos += -mat.Forward * offset; - pos += -mat.Left * offset; - pos += -mat.Up * offset; - - Vector3D[] directions = new Vector3D[] { - mat.Forward, - mat.Left, - mat.Up - }; - - var lineColor = (VRageMath.Vector4)Color.Red; - - var red = (VRageMath.Vector4)Color.Red; - var green = (VRageMath.Vector4)Color.Green; - var blue = (VRageMath.Vector4)Color.Blue; - var yellow = (VRageMath.Vector4)Color.Yellow; - - var material = MyStringId.GetOrCompute("Square"); - var blend = VRageRender.MyBillboard.BlendTypeEnum.PostPP; - - string subtypeShipyardCorner = _block.BlockDefinition.SubtypeId.ToString(); - float maxLength = 100.0f; - var cells = new List(); - foreach (var direction in directions) - { - var endPoint = pos + direction * maxLength; - cells.Clear(); - _block.CubeGrid.RayCastCells(pos, endPoint, cells); - - // path statuses: - // red = blocked by invalid block - // yellow = is only blocked by valid blocks - // green = completely valid segment - // blue = is unbounded by opposing shipyard corner - - int indexOfOpposingShipyardCorner = -1; - for (int i = 0; i < cells.Count; i++) - { - var block = _block.CubeGrid.GetCubeBlock(cells[i]); - if (block != null) - { - var subtypeOther = block.BlockDefinition.Id.SubtypeId.ToString(); - if (subtypeOther == subtypeShipyardCorner && block.SlimId() != _block.SlimBlock.SlimId()) - { - indexOfOpposingShipyardCorner = i; - break; - } - } - } - if (indexOfOpposingShipyardCorner > -1) - { - // the segment is bounded by two shipyard corners - - // clamp endpoint to position of opposing shipyard corner - endPoint = pos + direction * (indexOfOpposingShipyardCorner + 4.5); - var blockedByInvalid = false; - var pathHasEmptyCells = false; - for (int i = 3; i < indexOfOpposingShipyardCorner-1; i++) - { - // iterate the closed segment to determine status - var block = _block.CubeGrid.GetCubeBlock(cells[i]); - if (block != null) - { - var subtypeOther = block.BlockDefinition.Id.SubtypeId.ToString(); - //encountered non-shipyard corner block - if (!(subtypeOther == "ShipyardConveyor_Large" || subtypeOther == "ShipyardConveyorMount_Large")) - { - // condition red; path is blocked by invalid block - blockedByInvalid = true; - break; - } - } - else - { - pathHasEmptyCells = true; - } - } - if (blockedByInvalid) - { - MySimpleObjectDraw.DrawLine(pos, endPoint, material, ref red, width, blend); - continue; - } - else if (pathHasEmptyCells) - { - MySimpleObjectDraw.DrawLine(pos, endPoint, material, ref yellow, width, blend); - continue; - } - else - { - MySimpleObjectDraw.DrawLine(pos, endPoint, material, ref green, width, blend); - continue; - } - } - else - { - MySimpleObjectDraw.DrawLine(pos, endPoint, material, ref blue, width, blend); - continue; - } - - } - } - - public override void UpdateOnceBeforeFrame() - { - if (_init) - return; - - _init = true; - _block = Entity as IMyCollector; - - if (_block == null) - return; - - //create terminal controls - IMyTerminalControlSeparator sep = MyAPIGateway.TerminalControls.CreateControl(string.Empty); - sep.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - MyAPIGateway.TerminalControls.AddControl(sep); - - IMyTerminalControlOnOffSwitch guideSwitch = MyAPIGateway.TerminalControls.CreateControl("Shipyard_GuideSwitch"); - guideSwitch.Title = MyStringId.GetOrCompute("Guide Boxes"); - guideSwitch.Tooltip = MyStringId.GetOrCompute("Toggles the guide boxes drawn around grids in the shipyard."); - guideSwitch.OnText = MyStringId.GetOrCompute("On"); - guideSwitch.OffText = MyStringId.GetOrCompute("Off"); - guideSwitch.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - guideSwitch.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner") && GetYard(b) != null; - guideSwitch.SupportsMultipleBlocks = true; - guideSwitch.Getter = GetGuideEnabled; - guideSwitch.Setter = SetGuideEnabled; - MyAPIGateway.TerminalControls.AddControl(guideSwitch); - Controls.Add(guideSwitch); - - var lockSwitch = MyAPIGateway.TerminalControls.CreateControl("Shipyard_LockSwitch"); - lockSwitch.Title = MyStringId.GetOrCompute("Advanced Locking"); - lockSwitch.Tooltip = MyStringId.GetOrCompute("Toggles locking grids in the shipyard when grinding or welding while moving."); - lockSwitch.OnText = MyStringId.GetOrCompute("On"); - lockSwitch.OffText = MyStringId.GetOrCompute("Off"); - lockSwitch.Visible = b => b.BlockDefinition.SubtypeId.Equals("ShipyardCorner_Small"); - lockSwitch.Enabled = b => b.BlockDefinition.SubtypeId.Equals("ShipyardCorner_Small") && GetYard(b) != null; - lockSwitch.SupportsMultipleBlocks = true; - lockSwitch.Getter = GetLockEnabled; - lockSwitch.Setter = SetLockEnabled; - MyAPIGateway.TerminalControls.AddControl(lockSwitch); - Controls.Add(lockSwitch); - - IMyTerminalControlButton grindButton = MyAPIGateway.TerminalControls.CreateControl("Shipyard_GrindButton"); - IMyTerminalControlButton weldButton = MyAPIGateway.TerminalControls.CreateControl("Shipyard_WeldButton"); - IMyTerminalControlButton stopButton = MyAPIGateway.TerminalControls.CreateControl("Shipyard_StopButton"); - - grindButton.Title = MyStringId.GetOrCompute("Grind"); - grindButton.Tooltip = MyStringId.GetOrCompute("Begins grinding ships in the yard."); - grindButton.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner") && GetYard(b)?.YardType == ShipyardType.Disabled; - grindButton.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - grindButton.SupportsMultipleBlocks = true; - grindButton.Action = b => Communication.SendYardCommand(b.CubeGrid.EntityId, ShipyardType.Grind); - MyAPIGateway.TerminalControls.AddControl(grindButton); - Controls.Add(grindButton); - - weldButton.Title = MyStringId.GetOrCompute("Weld"); - weldButton.Tooltip = MyStringId.GetOrCompute("Begins welding ships in the yard."); - weldButton.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner") && GetYard(b)?.YardType == ShipyardType.Disabled; - weldButton.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - weldButton.SupportsMultipleBlocks = true; - weldButton.Action = b => Communication.SendYardCommand(b.CubeGrid.EntityId, ShipyardType.Weld); - MyAPIGateway.TerminalControls.AddControl(weldButton); - Controls.Add(weldButton); - - stopButton.Title = MyStringId.GetOrCompute("Stop"); - stopButton.Tooltip = MyStringId.GetOrCompute("Stops the shipyard."); - stopButton.Enabled = b => - { - if (!b.BlockDefinition.SubtypeId.Contains("ShipyardCorner")) - return false; - - ShipyardItem yard = GetYard(b); - - return yard?.YardType == ShipyardType.Weld || yard?.YardType == ShipyardType.Grind; - }; - stopButton.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - stopButton.SupportsMultipleBlocks = true; - stopButton.Action = b => Communication.SendYardCommand(b.CubeGrid.EntityId, ShipyardType.Disabled); - MyAPIGateway.TerminalControls.AddControl(stopButton); - Controls.Add(stopButton); - - IMyTerminalControlSlider beamCountSlider = MyAPIGateway.TerminalControls.CreateControl("Shipyard_BeamCount"); - beamCountSlider.Title = MyStringId.GetOrCompute("Beam Count"); - - beamCountSlider.Tooltip = MyStringId.GetOrCompute("Number of beams this shipyard can use per corner."); - beamCountSlider.SetLimits(1, 3); - beamCountSlider.Writer = (b, result) => result.Append(GetBeamCount(b)); - beamCountSlider.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - beamCountSlider.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner") && GetYard(b) != null; - beamCountSlider.Getter = b => GetBeamCount(b); - beamCountSlider.Setter = (b, v) => - { - SetBeamCount(b, (int)Math.Round(v, 0, MidpointRounding.ToEven)); - beamCountSlider.UpdateVisual(); - }; - beamCountSlider.SupportsMultipleBlocks = true; - MyAPIGateway.TerminalControls.AddControl(beamCountSlider); - Controls.Add(beamCountSlider); - - IMyTerminalControlSlider grindSpeedSlider = MyAPIGateway.TerminalControls.CreateControl("Shipyard_GrindSpeed"); - grindSpeedSlider.Title = MyStringId.GetOrCompute("Grind Speed"); - - grindSpeedSlider.Tooltip = MyStringId.GetOrCompute("How fast this shipyard grinds grids."); - grindSpeedSlider.SetLimits(0.01f, 2); - grindSpeedSlider.Writer = (b, result) => result.Append(GetGrindSpeed(b)); - grindSpeedSlider.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - grindSpeedSlider.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner") && GetYard(b) != null; - grindSpeedSlider.Getter = GetGrindSpeed; - grindSpeedSlider.Setter = (b, v) => - { - SetGrindSpeed(b, (float)Math.Round(v, 2, MidpointRounding.ToEven)); - grindSpeedSlider.UpdateVisual(); - }; - grindSpeedSlider.SupportsMultipleBlocks = true; - MyAPIGateway.TerminalControls.AddControl(grindSpeedSlider); - Controls.Add(grindSpeedSlider); - - IMyTerminalControlSlider weldSpeedSlider = MyAPIGateway.TerminalControls.CreateControl("Shipyard_WeldSpeed"); - weldSpeedSlider.Title = MyStringId.GetOrCompute("Weld Speed"); - - weldSpeedSlider.Tooltip = MyStringId.GetOrCompute("How fast this shipyard welds grids."); - weldSpeedSlider.SetLimits(0.01f, 2); - weldSpeedSlider.Writer = (b, result) => result.Append(GetWeldSpeed(b)); - weldSpeedSlider.Visible = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - weldSpeedSlider.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner") && GetYard(b) != null; - weldSpeedSlider.Getter = GetWeldSpeed; - weldSpeedSlider.Setter = (b, v) => - { - SetWeldSpeed(b, (float)Math.Round(v, 2, MidpointRounding.ToEven)); - weldSpeedSlider.UpdateVisual(); - }; - weldSpeedSlider.SupportsMultipleBlocks = true; - MyAPIGateway.TerminalControls.AddControl(weldSpeedSlider); - Controls.Add(weldSpeedSlider); - - IMyTerminalAction grindAction = MyAPIGateway.TerminalControls.CreateAction("Shipyard_GrindAction"); - grindAction.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - grindAction.Name = new StringBuilder("Grind"); - grindAction.Icon = @"Textures\GUI\Icons\Actions\Start.dds"; - grindAction.Action = b => Communication.SendYardCommand(b.CubeGrid.EntityId, ShipyardType.Grind); - MyAPIGateway.TerminalControls.AddAction(grindAction); - - IMyTerminalAction weldAction = MyAPIGateway.TerminalControls.CreateAction("Shipyard_WeldAction"); - weldAction.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - weldAction.Name = new StringBuilder("Weld"); - weldAction.Icon = @"Textures\GUI\Icons\Actions\Start.dds"; - weldAction.Action = b => Communication.SendYardCommand(b.CubeGrid.EntityId, ShipyardType.Weld); - MyAPIGateway.TerminalControls.AddAction(weldAction); - - IMyTerminalAction stopAction = MyAPIGateway.TerminalControls.CreateAction("Shipyard_StopAction"); - stopAction.Enabled = b => b.BlockDefinition.SubtypeId.Contains("ShipyardCorner"); - stopAction.Name = new StringBuilder("Stop"); - stopAction.Icon = @"Textures\GUI\Icons\Actions\Reset.dds"; - stopAction.Action = b => Communication.SendYardCommand(b.CubeGrid.EntityId, ShipyardType.Disabled); - MyAPIGateway.TerminalControls.AddAction(stopAction); - } - - private void AppendingCustomInfo(IMyTerminalBlock b, StringBuilder arg2) - { - try - { - float power = _power; - float maxpower = _maxpower; - if (GetYard(b) != null) - { - maxpower *= Math.Max(b.GetValueFloat("Shipyard_GrindSpeed"), b.GetValueFloat("Shipyard_WeldSpeed")); - maxpower *= GetBeamCount(b); - } - var sb = new StringBuilder(); - sb.Append("Required Input: "); - MyValueFormatter.AppendWorkInBestUnit(power, sb); - sb.AppendLine(); - sb.Append("Max required input: "); - MyValueFormatter.AppendWorkInBestUnit(maxpower, sb); - sb.AppendLine(); - sb.Append(_info); - sb.AppendLine(); - - arg2.Append(sb); - } - catch (Exception) - { - //don't really care, just don't crash - } - } - - private int GetBeamCount(IMyCubeBlock b) - { - if (GetYard(b) == null) - return 3; - - return ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId).BeamCount; - } - - private void SetBeamCount(IMyCubeBlock b, int value) - { - if (GetYard(b) == null) - return; - - //this value check stops infinite loops of sending the setting to server and immediately getting the same value back - if (value == GetBeamCount(b)) - return; - - YardSettingsStruct settings = ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId); - settings.BeamCount = value; - - ShipyardSettings.Instance.SetYardSettings(b.CubeGrid.EntityId, settings); - - Communication.SendShipyardSettings(b.CubeGrid.EntityId, settings); - } - - private bool GetGuideEnabled(IMyCubeBlock b) - { - if (GetYard(b) == null) - return true; - - return ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId).GuideEnabled; - } - - private void SetGuideEnabled(IMyCubeBlock b, bool value) - { - if (GetYard(b) == null) - return; - - if (value == GetGuideEnabled(b)) - return; - - YardSettingsStruct settings = ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId); - settings.GuideEnabled = value; - - ShipyardSettings.Instance.SetYardSettings(b.CubeGrid.EntityId, settings); - - Communication.SendShipyardSettings(b.CubeGrid.EntityId, settings); - } - - private bool GetLockEnabled(IMyCubeBlock b) - { - if (GetYard(b) == null) - return false; - - return ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId).AdvancedLocking; - } - - private void SetLockEnabled(IMyCubeBlock b, bool value) - { - if (GetYard(b) == null) - return; - - if (value == GetLockEnabled(b)) - return; - - YardSettingsStruct settings = ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId); - settings.AdvancedLocking = value; - - ShipyardSettings.Instance.SetYardSettings(b.CubeGrid.EntityId, settings); - - Communication.SendShipyardSettings(b.CubeGrid.EntityId, settings); - } - - private float GetGrindSpeed(IMyCubeBlock b) - { - if (GetYard(b) == null) - return 0.1f; - - return ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId).GrindMultiplier; - } - - private void SetGrindSpeed(IMyCubeBlock b, float value) - { - if (GetYard(b) == null) - return; - - if (value == GetGrindSpeed(b)) - return; - - YardSettingsStruct settings = ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId); - settings.GrindMultiplier = value; - - ShipyardSettings.Instance.SetYardSettings(b.CubeGrid.EntityId, settings); - - Communication.SendShipyardSettings(b.CubeGrid.EntityId, settings); - } - - private float GetWeldSpeed(IMyCubeBlock b) - { - if (GetYard(b) == null) - return 0.1f; - - return ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId).WeldMultiplier; - } - - private void SetWeldSpeed(IMyCubeBlock b, float value) - { - if (GetYard(b) == null) - return; - - if (value == GetWeldSpeed(b)) - return; - - YardSettingsStruct settings = ShipyardSettings.Instance.GetYardSettings(b.CubeGrid.EntityId); - settings.WeldMultiplier = value; - - ShipyardSettings.Instance.SetYardSettings(b.CubeGrid.EntityId, settings); - - Communication.SendShipyardSettings(b.CubeGrid.EntityId, settings); - } - - private ShipyardItem GetYard(IMyCubeBlock b) - { - return b.GameLogic.GetAs()?.Shipyard; - } - - public void SetPowerUse(float req) - { - _power = req; - } - - public void SetMaxPower(float req) - { - _maxpower = req; - } - - public void SetInfo(string info) - { - _info = info; - } - - public void UpdateVisuals() - { - foreach (IMyTerminalControl control in Controls) - control.UpdateVisual(); - } - - public override void UpdateBeforeSimulation() - { - if (!((IMyCollector)Container.Entity).Enabled) - _power = 0f; - _sink.SetMaxRequiredInputByType(PowerDef, _power); - _sink.SetRequiredInputByType(PowerDef, _power); - //sink.Update(); - } - - public override void UpdateBeforeSimulation10() - { - ((IMyTerminalBlock)Container.Entity).RefreshCustomInfo(); - } - - public override MyObjectBuilder_EntityBase GetObjectBuilder(bool copy = false) - { - return Entity.GetObjectBuilder(copy); - } - } -}using System.Collections.Generic; -using System.Linq; -using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using ShipyardMod.Settings; -using ShipyardMod.Utility; -using VRage; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; -using System; -using VRage.Collections; -using VRage.Game.Components; - -namespace ShipyardMod.ItemClasses -{ - public enum ShipyardType : byte - { - Disabled, - Weld, - Grind, - Invalid, - Scanning - } - - public class ShipyardItem - { - private MyTuple _shouldDisable; - //public int ActiveTargets; - - //these are set when processing a grid - //public IMyCubeGrid Grid; - //tool, target block - public Dictionary BlocksToProcess = new Dictionary(); - - public List BoxLines = new List(12); - public HashSet ConnectedCargo = new HashSet(); - - public MyConcurrentHashSet ContainsGrids = new MyConcurrentHashSet(); - public HashSet IntersectsGrids = new HashSet(); - - public LCDMenu Menu = null; - - public Dictionary MissingComponentsDict = new Dictionary(); - public Dictionary> ProxDict = new Dictionary>(); - - public YardSettingsStruct Settings; - public MyOrientedBoundingBoxD ShipyardBox; - public HashSet TargetBlocks = new HashSet(); - public IMyCubeBlock[] Tools; - //public int TotalBlocks; - public IMyEntity YardEntity; - public List YardGrids = new List(); - - public ShipyardType YardType; - public bool StaticYard; - - public ShipyardItem(MyOrientedBoundingBoxD box, IMyCubeBlock[] tools, ShipyardType yardType, IMyEntity yardEntity) - { - ShipyardBox = box; - Tools = tools; - YardType = yardType; - YardEntity = yardEntity; - StaticYard = tools[0].BlockDefinition.SubtypeId == "ShipyardCorner_Large"; - } - - public long EntityId - { - get { return YardEntity.EntityId; } - } - - public void Init(ShipyardType yardType) - { - if (YardType == yardType) - return; - - Logging.Instance.WriteDebug("YardItem.Init: " + yardType); - - YardType = yardType; - - foreach (IMyCubeGrid grid in ContainsGrids) - { - ((MyCubeGrid)grid).OnGridSplit += OnGridSplit; - } - - YardGrids = ContainsGrids.Where(x => !x.MarkedForClose).ToList(); - ContainsGrids.Clear(); - IntersectsGrids.Clear(); - Utilities.Invoke(() => - { - foreach (IMyCubeBlock tool in Tools) - { - var myFunctionalBlock = tool as IMyFunctionalBlock; - if (myFunctionalBlock != null) - myFunctionalBlock.Enabled = true; //.RequestEnable(true); - } - }); - - Communication.SendYardState(this); - } - - public void Disable(bool broadcast = true) - { - _shouldDisable.Item1 = true; - _shouldDisable.Item2 = broadcast; - } - - public void ProcessDisable() - { - if (!_shouldDisable.Item1) - return; - - foreach (IMyCubeGrid grid in YardGrids) - ((MyCubeGrid)grid).OnGridSplit -= OnGridSplit; - - YardGrids.Clear(); - - foreach (IMyCubeBlock tool in Tools) - { - BlocksToProcess[tool.EntityId] = new BlockTarget[3]; - if (YardType == ShipyardType.Invalid) - { - Utilities.Invoke(() => - { - var comp = tool.GameLogic.GetAs(); - comp.SetPowerUse(5); - comp.SetMaxPower(5); - comp.Shipyard = null; - }); - } - } - //TotalBlocks = 0; - MissingComponentsDict.Clear(); - ContainsGrids.Clear(); - IntersectsGrids.Clear(); - ProxDict.Clear(); - TargetBlocks.Clear(); - YardType = ShipyardType.Disabled; - if (_shouldDisable.Item2 && MyAPIGateway.Multiplayer.IsServer) - Communication.SendYardState(this); - - _shouldDisable.Item1 = false; - _shouldDisable.Item2 = false; - } - - public void HandleButtonPressed(int index) - { - Communication.SendButtonAction(YardEntity.EntityId, index); - } - - /* - * Worst case power usage would be a grid of 24+ blocks immediately - * inside one corner of our yard. All (up to) 24 of our lasers will be - * required to weld/grind this grid, but the lasers from the opposite - * corner would be the longest, effectively equal to the diagonal - * length of our yard. - * - * (each corner block) - * base power = 5 - * (each laser) - * base power = 30 - * additional power = 300 * Max(WeldMultiplier, GrindMultiplier) * (YardDiagonal / 200000) - * - * For balance, we scale power usage so that when YardDiag^2 == 200,000 - * (the same distance that our component effeciency bottoms out: ~450m), - * each //LASER// at 1x multiplier consumes the full output of a Large - * Reactor (300 MW). - * - * To put that in perspective, a cubical shipyard with a ~450m diagonal - * would be ~250m on each edge, or about 100 large blocks. But keep in - * mind: even with a shipyard this big, as long as the target grid is - * centered within the volume of the shipyard, laserLength would only - * be 225m, not the full 450m. And at 225m, each laser consumes only - * 75 MW: 25% of the max. So with a shipyard of this size, centering - * your target grid gets more and more important. - * - * Unlike component efficiency, however, which bottoms out to a fixed - * "minimum" efficiency past 450m, power requirements for lasers longer - * than this will continue to increase exponentially. - * - * This really only needs to be called whenever yard settings are changed - * (since BeamCount and multipliers affect this calculation), or when the - * yard changes state (weld/grind/disable) - */ - public void UpdateMaxPowerUse() - { - float multiplier; - var corners = new Vector3D[8]; - ShipyardBox.GetCorners(corners, 0); - - if (YardType == ShipyardType.Weld) - { - multiplier = Settings.WeldMultiplier; - } - else if (YardType == ShipyardType.Grind) - { - multiplier = Settings.GrindMultiplier; - } - else - { - // Yard is neither actively welding or grinding right now, so just show worst case - multiplier = Math.Max(Settings.WeldMultiplier, Settings.GrindMultiplier); - } - - float maxpower = 5 + Settings.BeamCount * (30 + 300 * multiplier * (float)Vector3D.DistanceSquared(corners[0], corners[6]) / 200000); - - if (!StaticYard) - maxpower *= 2; - - foreach (IMyCubeBlock tool in Tools) - { - ((IMyCollector)tool).GameLogic.GetAs().SetMaxPower(maxpower); - } - } - - public void UpdatePowerUse(float addedPower = 0) - { - addedPower /= 8; - if (YardType == ShipyardType.Disabled || YardType == ShipyardType.Invalid) - { - Utilities.Invoke(() => - { - foreach (IMyCubeBlock tool in Tools) - { - tool.GameLogic.GetAs().SetPowerUse(5 + addedPower); - Communication.SendToolPower(tool.EntityId, 5 + addedPower); - } - }); - } - else - { - float multiplier; - if (YardType == ShipyardType.Weld) - { - multiplier = Settings.WeldMultiplier; - } - else - { - multiplier = Settings.GrindMultiplier; - } - - Utilities.Invoke(() => - { - foreach (IMyCubeBlock tool in Tools) - { - float power = 5; - //Logging.Instance.WriteDebug(String.Format("Tool[{0}] Base power usage [{1:F1} MW]", tool.DisplayNameText, power)); - int i = 0; - foreach (BlockTarget blockTarget in BlocksToProcess[tool.EntityId]) - { - if (blockTarget == null) - continue; - - float laserPower = 30 + 300 * multiplier * (float)blockTarget.ToolDist[tool.EntityId] / 200000; - //Logging.Instance.WriteDebug(String.Format("Tool[{0}] laser[{1}] distance[{2:F1}m] multiplier[{3:F1}x] additional power req [{4:F1} MW]", tool.DisplayNameText, i, Math.Sqrt(blockTarget.ToolDist[tool.EntityId]), multiplier, laserPower)); - power += laserPower; - i++; - } - - if (!StaticYard) - power *= 2; - - power += addedPower; - - //Logging.Instance.WriteDebug(String.Format("Tool[{0}] Total computed power [{1:F1} MW]", tool.DisplayNameText, power)); - var log = tool.GameLogic.GetAs(); - if (log == null) - continue; - tool.GameLogic.GetAs().SetPowerUse(power); - Communication.SendToolPower(tool.EntityId, power); - } - }); - } - } - - public void OnGridSplit(MyCubeGrid oldGrid, MyCubeGrid newGrid) - { - if (YardGrids.Any(g => g.EntityId == oldGrid.EntityId)) - { - newGrid.OnGridSplit += OnGridSplit; - YardGrids.Add(newGrid); - } - } - - public void UpdatePosition() - { - ShipyardBox = MathUtility.CreateOrientedBoundingBox((IMyCubeGrid)YardEntity, Tools.Select(x => x.GetPosition()).ToList(), 2.5); - } - - /// - /// Gives grids in the shipyard a slight nudge to help them match velocity when the shipyard is moving. - /// - /// Code donated by Equinox - /// - public void NudgeGrids() - { - //magic value of 0.005 here was determined experimentally. - //value is just enough to assist with matching velocity to the shipyard, but not enough to prevent escape - foreach (var grid in ContainsGrids) - { - if (Vector3D.IsZero(grid.Physics.LinearVelocity - YardEntity.Physics.LinearVelocity)) - continue; - - grid.Physics.ApplyImpulse(grid.Physics.Mass * Vector3D.ClampToSphere((YardEntity.Physics.LinearVelocity - grid.Physics.LinearVelocity), 0.005), grid.Physics.CenterOfMassWorld); - } - - foreach (var grid in YardGrids) - { - if (!Settings.AdvancedLocking) - grid.Physics.ApplyImpulse(grid.Physics.Mass * Vector3D.ClampToSphere((YardEntity.Physics.LinearVelocity - grid.Physics.LinearVelocity), 0.005), grid.Physics.CenterOfMassWorld); - else - { - double powerUse = MathUtility.MatchShipVelocity(grid, YardEntity, true); - if(powerUse > 0) - UpdatePowerUse((float)powerUse); - } - } - } - } -}using System; -using System.Xml.Serialization; -using Sandbox.ModAPI; -using ShipyardMod.Utility; -using VRage.Serialization; - -namespace ShipyardMod.Settings -{ - public struct YardSettingsStruct - { - public YardSettingsStruct(long entityId) - { - EntityId = entityId; - BeamCount = 3; - GuideEnabled = true; - WeldMultiplier = 0.1f; - GrindMultiplier = 0.1f; - AdvancedLocking = false; - } - - public readonly long EntityId; - public int BeamCount; - public bool GuideEnabled; - public float WeldMultiplier; - public float GrindMultiplier; - public bool AdvancedLocking; - } - - [XmlInclude(typeof(YardSettingsStruct))] - public class ShipyardSettings - { - private static ShipyardSettings _instance; - - public SerializableDictionary BlockSettings; - - public ShipyardSettings() - { - BlockSettings = new SerializableDictionary(); - } - - public static ShipyardSettings Instance - { - get - { - if (_instance != null) - return _instance; - - if (!Load()) - _instance = new ShipyardSettings(); - - return _instance; - } - } - - public YardSettingsStruct GetYardSettings(long entityId) - { - YardSettingsStruct result; - if (!BlockSettings.Dictionary.TryGetValue(entityId, out result)) - { - result = new YardSettingsStruct(entityId); - SetYardSettings(entityId, result); - } - return result; - } - - public void SetYardSettings(long entityId, YardSettingsStruct newSet) - { - BlockSettings[entityId] = newSet; - } - - public void Save() - { - Logging.Instance.WriteLine("Saving settings"); - string serialized = MyAPIGateway.Utilities.SerializeToXML(this); - MyAPIGateway.Utilities.SetVariable("ShipyardSettings", serialized); - Logging.Instance.WriteLine("Done saving settings"); - } - - private static bool Load() - { - Logging.Instance.WriteLine("Loading settings"); - try - { - string value; - if (!MyAPIGateway.Utilities.GetVariable("ShipyardSettings", out value)) - { - Logging.Instance.WriteLine("Settings do not exist in world file"); - return false; - } - _instance = MyAPIGateway.Utilities.SerializeFromXML(value); - Logging.Instance.WriteLine("Done loading settings"); - return true; - } - catch (Exception ex) - { - Logging.Instance.WriteLine("Error loading settings: " + ex); - return false; - } - } - } -}using System; -using System.Collections.Generic; -using System.Linq; -using ParallelTasks; -using Sandbox.ModAPI; -using ShipyardMod.ItemClasses; -using ShipyardMod.ProcessHandlers; -using VRage; -using VRage.Collections; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace ShipyardMod.Utility -{ - public static class Utilities - { - private static readonly MyConcurrentHashSet ThreadLocks = new MyConcurrentHashSet(); - private static readonly MyConcurrentQueue ActionQueue = new MyConcurrentQueue(); - public static volatile bool SessionClosing; - private static readonly string FullName = typeof(Utilities).FullName; - - private static volatile bool Processing; - private static Task lastInvokeTask; - - /// - /// Invokes actions on the game thread, and blocks until completion - /// - /// - public static void InvokeBlocking(Action action) - { - var threadLock = new FastResourceLock(); - - if (!SessionClosing) - ThreadLocks.Add(threadLock); - - threadLock.AcquireExclusive(); - try - { - MyAPIGateway.Utilities.InvokeOnGameThread(() => - { - try - { - var invokeBlock = Profiler.Start(FullName, nameof(InvokeBlocking)); - action(); - invokeBlock.End(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine("Exception on blocking game thread invocation: " + ex); - - if (!SessionClosing && ShipyardCore.Debug) - throw; - } - finally - { - threadLock.ReleaseExclusive(); - } - }); - } - catch (Exception ex) - { - Logging.Instance.WriteLine("Exception in Utilities.InvokeBlocking: " + ex); - threadLock.ReleaseExclusive(); - - if (!SessionClosing && ShipyardCore.Debug) - throw; - } - - threadLock.AcquireExclusive(); - threadLock.ReleaseExclusive(); - - if (!SessionClosing) - ThreadLocks.Remove(threadLock); - } - - /// - /// Wraps InvokeOnGameThread in lots of try/catch to reduce failure on session close - /// - /// - public static void Invoke(Action action) - { - try - { - MyAPIGateway.Utilities.InvokeOnGameThread(() => - { - try - { - var invokeBlock = Profiler.Start(FullName, nameof(Invoke)); - action(); - invokeBlock.End(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine("Exception on game thread invocation: " + ex); - if (!SessionClosing && ShipyardCore.Debug) - throw; - } - }); - } - catch (Exception ex) - { - Logging.Instance.WriteLine("Exception in Utilities.Invoke: " + ex); - if (!SessionClosing && ShipyardCore.Debug) - throw; - } - } - - /// - /// Invokes an action on the game thread with a callback - /// - /// - /// - public static void Invoke(Action action, Action callback) - { - MyAPIGateway.Parallel.StartBackground(() => { InvokeBlocking(action); }, callback); - } - - /// - /// Enqueus Actions to be executed in an worker thread separate from the game thread - /// - /// - public static void QueueAction(Action action) - { - ActionQueue.Enqueue(action); - } - - /// - /// Processes the action queue - /// - public static void ProcessActionQueue() - { - if (Processing || ActionQueue.Count == 0) - return; - - if (lastInvokeTask.Exceptions != null && lastInvokeTask.Exceptions.Length > 0) - throw lastInvokeTask.Exceptions[0]; - - Processing = true; - lastInvokeTask = MyAPIGateway.Parallel.Start(() => - { - try - { - var queueBlock = Profiler.Start(FullName, nameof(ProcessActionQueue)); - while (ActionQueue.Count > 0) - { - Action action = ActionQueue.Dequeue(); - action(); - } - queueBlock.End(); - } - catch (Exception ex) - { - Logging.Instance.WriteLine("Exception in ProcessActionQueue: " + ex); - if (!SessionClosing && ShipyardCore.Debug) - throw; - } - finally - { - Processing = false; - } - }); - } - - /// - /// Causes any waiting calls to InvokeBlocking() to return immediately - /// - /// - public static bool AbortAllTasks() - { - bool result = false; - foreach (FastResourceLock threadLock in ThreadLocks) - { - result = true; - threadLock.ReleaseExclusive(); - } - - return result; - } - - /// - /// Gets the entity from the given list that is closest to the given point - /// - /// - /// - /// - public static IMyEntity GetNearestTo(Vector3D target, IEnumerable candidates) - { - IMyEntity result = null; - double minDist = 0; - - foreach (IMyEntity entity in candidates) - { - double distance = Vector3D.DistanceSquared(target, entity.GetPosition()); - if (result == null || distance < minDist) - { - minDist = distance; - result = entity; - } - } - - return result; - } - - /// - /// Finds the shipyard nearest to the given point - /// - /// - /// - public static ShipyardItem GetNearestYard(Vector3D target) - { - double minDist = 0; - IMyCubeBlock closestCorner = null; - - foreach (ShipyardItem yard in ProcessShipyardDetection.ShipyardsList.ToArray()) - { - foreach (IMyCubeBlock corner in yard.Tools) - { - double distance = Vector3D.DistanceSquared(target, corner.GetPosition()); - - if (closestCorner == null || distance < minDist) - { - minDist = distance; - closestCorner = corner; - } - } - } - - foreach (ShipyardItem toVerify in ProcessShipyardDetection.ShipyardsList.ToArray()) - { - if (toVerify.Tools.Contains(closestCorner)) - return toVerify; - } - - return null; - } - - public static bool TryGetPlayerFromSteamId(ulong steamId, out IMyPlayer result) - { - var players = new List(); - MyAPIGateway.Players.GetPlayers(players, x => x.SteamUserId == steamId); - - if (players.Count == 0) - { - result = null; - return false; - } - - result = players[0]; - return true; - } - } -} \ No newline at end of file diff --git a/ULTRABLOCKDEBUGGER/Data/Scripts/ultrablockdebug/ultrablockdebugger.cs b/ULTRABLOCKDEBUGGER/Data/Scripts/ultrablockdebug/ultrablockdebugger.cs new file mode 100644 index 0000000..f135ca2 --- /dev/null +++ b/ULTRABLOCKDEBUGGER/Data/Scripts/ultrablockdebug/ultrablockdebugger.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Sandbox.Definitions; +using Sandbox.Game.Entities; +using Sandbox.ModAPI; +using VRage; +using VRage.Game; +using VRage.Game.Components; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRage.ObjectBuilders; +using VRage.Utils; +using VRageMath; +using System.Linq; + +[MySessionComponentDescriptor(MyUpdateOrder.NoUpdate)] +public class BlockDebuggerComponent : MySessionComponentBase +{ + private const ushort NET_ID = 9972; // Unique network ID + private const string COMMAND = "/blockdebug"; + private const string TempGridDisplayName = "BlockDebugger_TemporaryGrid"; + private List spawnTimes = new List(); + private long minSpawnTime = long.MaxValue; + private long maxSpawnTime = long.MinValue; + + public override void LoadData() + { + MyAPIGateway.Utilities.MessageEntered += OnMessageEntered; + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(NET_ID, NetworkHandler); + } + + protected override void UnloadData() + { + MyAPIGateway.Utilities.MessageEntered -= OnMessageEntered; + MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(NET_ID, NetworkHandler); + } + + private void OnMessageEntered(string messageText, ref bool sendToOthers) + { + if (!messageText.Equals(COMMAND, StringComparison.InvariantCultureIgnoreCase)) return; + + sendToOthers = false; // Don't send this command to chat + + if (!MyAPIGateway.Multiplayer.IsServer) + { + MyAPIGateway.Utilities.ShowNotification("BlockDebug: Client detected. Sending request to server!", 5000); + MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, new byte[1]); + } + else + { + MyAPIGateway.Utilities.ShowNotification("BlockDebug: Server detected. Executing locally!", 5000); + ExecuteDebugSpawn(); + } + } + + private void NetworkHandler(ushort handler, byte[] data, ulong steamId, bool isFromServer) + { + if (MyAPIGateway.Multiplayer.IsServer) + { + MyAPIGateway.Utilities.ShowNotification($"BlockDebug: Request received from {steamId}. Executing on server!", 5000); + ExecuteDebugSpawn(); + } + else + { + MyAPIGateway.Utilities.ShowNotification("BlockDebug: Network message received by client. This shouldn't happen!", 5000, "Red"); + } + } + + private void ExecuteDebugSpawn() + { + MyAPIGateway.Utilities.ShowNotification("BlockDebug: Starting debug spawn process...", 5000); + + try + { + MatrixD camMatrix = MyAPIGateway.Session.Camera.WorldMatrix; + Vector3D playerPosition = camMatrix.Translation; + + var blockDefinitions = MyDefinitionManager.Static.GetAllDefinitions().OfType().Where(def => def.Public).ToList(); + int totalBlocks = blockDefinitions.Count; + + if (totalBlocks == 0) + { + MyAPIGateway.Utilities.ShowNotification("BlockDebug: No public block definitions found.", 5000, "Red"); + return; + } + + // Calculate the size of the 3D grid + int gridSize = (int)Math.Ceiling(Math.Pow(totalBlocks, 1.0 / 3.0)); + double spacing = 10.0; // Adjust the spacing as needed + + int index = 0; + foreach (var blockDef in blockDefinitions) + { + int x = index % gridSize; + int y = (index / gridSize) % gridSize; + int z = index / (gridSize * gridSize); + + Vector3D offset = new Vector3D(x * spacing, y * spacing, z * spacing); + Vector3D spawnPosition = playerPosition + offset; + + // Create and spawn a new grid for this block + var tempGridSpawn = new TempGridSpawn(blockDef, spawnPosition, TempGridDisplayName, OnGridSpawned); + index++; + } + } + catch (Exception e) + { + MyAPIGateway.Utilities.ShowNotification($"BlockDebug: Exception during grid spawn - {e.Message}", 10000, "Red"); + } + } + + private void OnGridSpawned(IMySlimBlock block, long spawnTime) + { + if (block != null) + { + // Update min and max spawn times + spawnTimes.Add(spawnTime); + minSpawnTime = Math.Min(minSpawnTime, spawnTime); + + // Update the maxSpawnTime to be the average of the top three times + var topThreeTimes = spawnTimes.OrderByDescending(t => t).Take(3).ToList(); + if (topThreeTimes.Count > 0) + { + maxSpawnTime = (long)topThreeTimes.Average(); + } + + // Calculate the color based on normalized time + Vector3 colorHSV = GetColorBasedOnNormalizedTime(spawnTime, minSpawnTime, maxSpawnTime); + IMyCubeGrid grid = block.CubeGrid; + + // Color the blocks in the grid + var blocks = new List(); + grid.GetBlocks(blocks, null); // Get all blocks + + foreach (var slimBlock in blocks) + { + Vector3I minPos = slimBlock.Min; + Vector3I maxPos = slimBlock.Min; + grid.ColorBlocks(minPos, maxPos, colorHSV); + } + + MyAPIGateway.Utilities.ShowNotification($"BlockDebug: Spawned grid with block {block.BlockDefinition.Id} at {block.CubeGrid.GetPosition()} in {spawnTime} ms", 5000); + } + else + { + MyAPIGateway.Utilities.ShowNotification("BlockDebug: Failed to spawn block!", 5000, "Red"); + } + } + + private class TempGridSpawn + { + private readonly Action Callback; + private readonly Stopwatch stopwatch; + + public TempGridSpawn(MyCubeBlockDefinition blockDef, Vector3D playerPosition, string gridName, Action callback) + { + Callback = callback; + stopwatch = new Stopwatch(); + stopwatch.Start(); // Start timing the spawn process + + var gridOB = MyObjectBuilderSerializer.CreateNewObject(); + gridOB.EntityId = 0; + gridOB.DisplayName = gridName + "_" + blockDef.DisplayNameText; + gridOB.CreatePhysics = true; + gridOB.GridSizeEnum = blockDef.CubeSize; + gridOB.PersistentFlags = MyPersistentEntityFlags2.InScene; + gridOB.IsStatic = false; + gridOB.Editable = true; + gridOB.DestructibleBlocks = true; + gridOB.IsRespawnGrid = false; + + var blockOB = MyObjectBuilderSerializer.CreateNewObject(blockDef.Id) as MyObjectBuilder_CubeBlock; + if (blockOB != null) + { + blockOB.Min = Vector3I.Zero; // Position the block at the grid's origin + + gridOB.PositionAndOrientation = new MyPositionAndOrientation(playerPosition, Vector3.Forward, Vector3.Up); + gridOB.CubeBlocks.Add(blockOB); + + MyAPIGateway.Entities.CreateFromObjectBuilderParallel(gridOB, true, OnGridCreated); + } + } + + private void OnGridCreated(IMyEntity ent) + { + stopwatch.Stop(); // Stop timing the spawn process + IMyCubeGrid grid = ent as IMyCubeGrid; + if (grid == null) + { + MyAPIGateway.Utilities.ShowNotification("BlockDebug: Failed to create grid!", 5000, "Red"); + return; + } + + var blocks = new List(); + grid.GetBlocks(blocks, null); // Fill the list with blocks + + if (blocks.Count > 0) + { + Callback?.Invoke(blocks[0], stopwatch.ElapsedMilliseconds); // Use the first block to represent the grid + } + } + } + + private Vector3 GetColorBasedOnNormalizedTime(long spawnTime, long minTime, long maxTime) + { + if (maxTime == minTime) + { + // If all times are the same, return green as default + return new Vector3(0.33f, 1f, 1f); + } + + // Normalize the spawn time between 0 (minTime) and 1 (maxTime) + float normalized = (float)(spawnTime - minTime) / (maxTime - minTime); + + // Map the normalized time to a hue (0 = red, 0.33 = green) + float hue = MathHelper.Lerp(0f, 0.33f, 1f - normalized); // Invert so shorter times are greener + return new Vector3(hue, 1f, 1f); // Full saturation and value for bright color + } + +}