Files
QuartZ-dump/GlobalShared/OcTree/Data/GridData.cs
2024-12-29 21:15:58 +01:00

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);
}
}
}