zz
This commit is contained in:
334
GlobalShared/OcTree/Data/GridData.cs
Normal file
334
GlobalShared/OcTree/Data/GridData.cs
Normal file
@@ -0,0 +1,334 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user