334 lines
12 KiB
C#
334 lines
12 KiB
C#
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Global.Shared.API;
|
|
using Global.Shared.Events;
|
|
using Global.Shared.Plugin;
|
|
using Sandbox.Game.Entities;
|
|
using Sandbox.Game.Entities.Cube;
|
|
using Sandbox.Game.World;
|
|
using VRage.Collections;
|
|
using VRage.Game;
|
|
using VRage.Game.ModAPI;
|
|
using VRage.Game.ObjectBuilders.Definitions.SessionComponents;
|
|
using VRage.ObjectBuilders;
|
|
using VRageMath;
|
|
|
|
namespace Global.Shared.OcTree.Data
|
|
{
|
|
public class GridData : IOcTreeData, IGridData
|
|
{
|
|
private static readonly Stopwatch _stopwatch = new Stopwatch();
|
|
public ConcurrentDictionary<MyObjectBuilderType, ArrayList> Blocks;
|
|
public MyConcurrentList<MyFunctionalBlock> FunctionalBlocks;
|
|
public List<MyCubeGrid> SubGrids;
|
|
|
|
public GridData(MyCubeGrid grid)
|
|
{
|
|
CubeGrid = grid;
|
|
Id = grid.EntityId;
|
|
Init();
|
|
}
|
|
|
|
public bool IsInVoxel { get; set; }
|
|
public int BlockCount { get; set; }
|
|
|
|
public MyCubeGrid CubeGrid { get; }
|
|
|
|
public IEnumerable<MyCubeBlock> GetBlocks(MyObjectBuilderType type)
|
|
{
|
|
// TODO: Improve memory allocation of this.
|
|
return Blocks.TryGetValue(type, out var list)
|
|
? list.Cast<MyCubeBlock>().ToArray()
|
|
: Enumerable.Empty<MyCubeBlock>();
|
|
}
|
|
|
|
|
|
public IEnumerable<MyFunctionalBlock> GetAllFunctionalBlocks()
|
|
{
|
|
return FunctionalBlocks ?? Enumerable.Empty<MyFunctionalBlock>();
|
|
}
|
|
|
|
public bool Contains(MyObjectBuilderType type)
|
|
{
|
|
return Blocks.ContainsKey(type) && Blocks[type].Count > 0;
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
Recompute();
|
|
}
|
|
|
|
public long Id { get; set; }
|
|
public List<long> ChildIds => SubGrids?.Select(x => x.EntityId).ToList();
|
|
public long Flags { get; private set; }
|
|
public BoundingBoxD BoundingBox { get; private set; }
|
|
|
|
public void Recompute()
|
|
{
|
|
Flags = GridFlag.None;
|
|
BoundingBox = CubeGrid.GetPhysicalGroupAABB();
|
|
Flags |= CubeGrid.IsStatic ? GridFlag.StaticGrid : GridFlag.DynamicGrid;
|
|
Flags |= CubeGrid.GridSizeEnum == MyCubeSize.Large ? GridFlag.LargeGrid : GridFlag.SmallGrid;
|
|
BlockCount = CubeGrid.BlocksCount + (SubGrids?.Sum(x => x.BlocksCount) ?? 0);
|
|
}
|
|
|
|
private bool Closing;
|
|
|
|
public void Dispose()
|
|
{
|
|
GlobalInstance.Log.Debug($"Disposing of grid data for {Id} {CubeGrid.DisplayName}");
|
|
if (GlobalInstance.Log.IsTraceEnabled) GlobalInstance.Log.Trace(new StackTrace(true).ToString());
|
|
CubeGrid.OnStaticChanged -= OnStaticChanged;
|
|
CubeGrid.OnConnectionChanged -= OnConnectionChanged;
|
|
CubeGrid.OnGridMerge -= OnGridMerged;
|
|
CubeGrid.OnGridSplit -= OnGridSplit;
|
|
CubeGrid.OnFatBlockAdded -= AddBlock;
|
|
CubeGrid.OnFatBlockRemoved -= OnFatBlockRemoved;
|
|
SubGrids?.ForEach(x =>
|
|
{
|
|
x.OnStaticChanged -= OnStaticChanged;
|
|
x.OnConnectionChanged -= OnConnectionChanged;
|
|
x.OnGridMerge -= OnGridMerged;
|
|
x.OnGridSplit -= OnGridSplit;
|
|
x.OnFatBlockAdded -= AddBlock;
|
|
x.OnFatBlockRemoved -= OnFatBlockRemoved;
|
|
});
|
|
Closing = true;
|
|
GlobalInstance.Run(() =>
|
|
{
|
|
Blocks.Clear();
|
|
|
|
FunctionalBlocks.Clear();
|
|
SubGrids.Clear();
|
|
});
|
|
}
|
|
|
|
private void OnStaticChanged(MyCubeGrid arg1, bool arg2)
|
|
{
|
|
RecheckVoxelStatus();
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
Blocks = new ConcurrentDictionary<MyObjectBuilderType, ArrayList>();
|
|
FunctionalBlocks = new MyConcurrentList<MyFunctionalBlock>();
|
|
RecalculateSubgrids();
|
|
Recompute();
|
|
CubeGrid.OnStaticChanged += OnStaticChanged;
|
|
CubeGrid.OnConnectionChanged += OnConnectionChanged;
|
|
CubeGrid.OnGridMerge += OnGridMerged;
|
|
CubeGrid.OnGridSplit += OnGridSplit;
|
|
CubeGrid.OnFatBlockAdded += AddBlock;
|
|
CubeGrid.OnFatBlockRemoved += OnFatBlockRemoved;
|
|
|
|
if (GlobalInstance.Log.IsDebugEnabled)
|
|
{
|
|
_stopwatch.Reset();
|
|
_stopwatch.Start();
|
|
}
|
|
|
|
ScanGridBlocks(CubeGrid);
|
|
if (SubGrids.Count > 0)
|
|
SubGrids.ForEach(ScanGridBlocks);
|
|
RecheckVoxelStatus();
|
|
|
|
if (GlobalInstance.Log.IsDebugEnabled)
|
|
GlobalInstance.Log.Debug(
|
|
$"Data Init took {_stopwatch.ElapsedMilliseconds}ms for {CubeGrid.DisplayName} with {CubeGrid.BlocksCount + SubGrids.Sum(e => e.BlocksCount)} blocks and {SubGrids.Count} subgrids");
|
|
}
|
|
|
|
private void OnFatBlockRemoved(MyCubeBlock block)
|
|
{
|
|
if (Closing) return;
|
|
if (Blocks.TryGetValue(block.BlockDefinition.Id.TypeId, out var bag)) bag.Remove(block);
|
|
|
|
GlobalInstance.Run(() =>
|
|
{
|
|
if (block is IMyControllableEntity entity)
|
|
{
|
|
entity.ControllerInfo.ControlAcquired -= OnControlAcquired;
|
|
entity.ControllerInfo.ControlReleased -= OnControlReleased;
|
|
OnControlReleased(entity.ControllerInfo.Controller);
|
|
}
|
|
|
|
BlockEvents.OnBlockRemoved?.Invoke(block);
|
|
});
|
|
}
|
|
|
|
public void RecheckVoxelStatus()
|
|
{
|
|
if (Closing) return;
|
|
var settings = new MyGridPlacementSettings
|
|
{
|
|
VoxelPlacement = new VoxelPlacementSettings
|
|
{
|
|
PlacementMode = VoxelPlacementMode.Volumetric
|
|
}
|
|
};
|
|
IsInVoxel = false;
|
|
if (!CheckGridVoxelStatic(CubeGrid, settings))
|
|
if (SubGrids.Count > 0 && SubGrids.All(e => !CheckGridVoxelStatic(e, settings)))
|
|
return;
|
|
IsInVoxel = true;
|
|
}
|
|
|
|
public bool CheckGridVoxelStatic(MyCubeGrid grid, MyGridPlacementSettings settings)
|
|
{
|
|
if (!MyCubeGrid.IsAabbInsideVoxel(grid.WorldMatrix, grid.PositionComp.LocalAABB, settings)) return false;
|
|
var worldAabb = grid.PositionComp.WorldAABB;
|
|
return MyGamePruningStructure.AnyVoxelMapInBox(ref worldAabb) &&
|
|
grid.GetBlocks().Any(block => MyCubeGrid.IsInVoxels(block, isVolumetric: false));
|
|
}
|
|
|
|
private bool RecalculateSubgrids()
|
|
{
|
|
var previous = SubGrids;
|
|
SubGrids = CubeGrid.GetConnectedGrids(GridLinkTypeEnum.Mechanical);
|
|
SubGrids.Remove(CubeGrid);
|
|
if (previous == null)
|
|
{
|
|
SubGrids.ForEach(OnSubGridAdded);
|
|
return SubGrids.Any();
|
|
}
|
|
|
|
var changed = false;
|
|
var newSubGrids = SubGrids.Except(previous).ToList();
|
|
if (newSubGrids.Count > 0)
|
|
{
|
|
changed = true;
|
|
newSubGrids.ForEach(OnSubGridAdded);
|
|
}
|
|
|
|
var removedSubGrids = previous.Except(SubGrids).ToList();
|
|
if (removedSubGrids.Count > 0)
|
|
{
|
|
changed = true;
|
|
removedSubGrids.ForEach(OnSubGridRemoved);
|
|
}
|
|
|
|
if (changed) GlobalStatic.OcTreeHandler.GridTree.Update(Id);
|
|
|
|
return changed;
|
|
}
|
|
|
|
private void OnGridSplit(MyCubeGrid arg1, MyCubeGrid arg2)
|
|
{
|
|
GlobalInstance.Log.Debug(
|
|
$"Grid split Invoke: {arg1.DisplayName} and {arg2.DisplayName} {Thread.CurrentThread.Name}");
|
|
GlobalInstance.Run(() =>
|
|
{
|
|
GlobalInstance.Log.Debug(
|
|
$"Grid split Run: {arg1.DisplayName} and {arg2.DisplayName} {Thread.CurrentThread.Name}");
|
|
GlobalStatic.OcTreeHandler.GridTree.Remove(arg1.EntityId);
|
|
GlobalStatic.OcTreeHandler.GridTree.Remove(arg2.EntityId);
|
|
GlobalStatic.OcTreeHandler.AddGrid(arg1);
|
|
GlobalStatic.OcTreeHandler.AddGrid(arg2);
|
|
});
|
|
}
|
|
|
|
private void OnGridMerged(MyCubeGrid arg1, MyCubeGrid arg2)
|
|
{
|
|
//GlobalInstance.Log.Info($"Merging {arg1.DisplayName} and {arg2.DisplayName}");
|
|
RecalculateSubgrids();
|
|
Recompute();
|
|
Blocks.Clear();
|
|
|
|
FunctionalBlocks.Clear();
|
|
ScanGridBlocks(CubeGrid);
|
|
if (SubGrids.Count > 0)
|
|
SubGrids.ForEach(ScanGridBlocks);
|
|
}
|
|
|
|
private void OnConnectionChanged(MyCubeGrid arg1, GridLinkTypeEnum arg2)
|
|
{
|
|
if (Closing) return;
|
|
if (arg2 != GridLinkTypeEnum.Mechanical) return;
|
|
|
|
RecalculateSubgrids();
|
|
Recompute();
|
|
Blocks.Clear();
|
|
FunctionalBlocks.Clear();
|
|
ScanGridBlocks(CubeGrid);
|
|
if (SubGrids.Count > 0)
|
|
SubGrids.ForEach(ScanGridBlocks);
|
|
}
|
|
|
|
private void ScanGridBlocks(MyCubeGrid obj)
|
|
{
|
|
Parallel.ForEach(obj.GetFatBlocks(), AddBlock);
|
|
}
|
|
|
|
private void OnSubGridAdded(MyCubeGrid grid)
|
|
{
|
|
GlobalStatic.OcTreeHandler.RemoveGrid(grid);
|
|
grid.OnFatBlockAdded += AddBlock;
|
|
grid.OnFatBlockRemoved += OnFatBlockRemoved;
|
|
grid.OnStaticChanged += OnStaticChanged;
|
|
grid.OnConnectionChanged += OnConnectionChanged;
|
|
grid.OnGridMerge += OnGridMerged;
|
|
grid.OnGridSplit += OnGridSplit;
|
|
GridEvents.SubGridAdded?.Invoke(CubeGrid, grid);
|
|
RecheckVoxelStatus();
|
|
}
|
|
|
|
private void OnSubGridRemoved(MyCubeGrid grid)
|
|
{
|
|
grid.OnFatBlockAdded -= AddBlock;
|
|
grid.OnFatBlockRemoved -= OnFatBlockRemoved;
|
|
grid.OnStaticChanged -= OnStaticChanged;
|
|
grid.OnConnectionChanged -= OnConnectionChanged;
|
|
grid.OnGridMerge -= OnGridMerged;
|
|
grid.OnGridSplit -= OnGridSplit;
|
|
GridEvents.SubGridRemoved?.Invoke(CubeGrid, grid);
|
|
RecheckVoxelStatus();
|
|
}
|
|
|
|
private void AddBlock(MyCubeBlock block)
|
|
{
|
|
if (Closing) return;
|
|
Blocks.AddOrUpdate(block.BlockDefinition.Id.TypeId,
|
|
_ => ArrayList.Synchronized(new ArrayList { block }),
|
|
(_, list) =>
|
|
{
|
|
list.Add(block);
|
|
|
|
return list;
|
|
});
|
|
|
|
GlobalInstance.Run(() =>
|
|
{
|
|
if (block is IMyControllableEntity entity)
|
|
{
|
|
entity.ControllerInfo.ControlAcquired += OnControlAcquired;
|
|
entity.ControllerInfo.ControlReleased += OnControlReleased;
|
|
if (entity.ControllerInfo.IsLocallyControlled() || entity.ControllerInfo.IsRemotelyControlled())
|
|
OnControlAcquired(entity.ControllerInfo.Controller);
|
|
}
|
|
|
|
if (block is MyFunctionalBlock functionalBlock) FunctionalBlocks.Add(functionalBlock);
|
|
|
|
BlockEvents.OnBlockAdded?.Invoke(block);
|
|
});
|
|
}
|
|
|
|
private void OnControlAcquired(MyEntityController obj)
|
|
{
|
|
BlockEvents.OnPlayerControlAcquired?.Invoke(obj.ControlledEntity);
|
|
}
|
|
|
|
private void OnControlReleased(MyEntityController obj)
|
|
{
|
|
if (obj != null) BlockEvents.OnPlayerControlReleased?.Invoke(obj.ControlledEntity);
|
|
}
|
|
|
|
public void RaiseAttachedEntityChanged()
|
|
{
|
|
OnConnectionChanged(CubeGrid, GridLinkTypeEnum.Mechanical);
|
|
}
|
|
}
|
|
} |