Compare commits

..

7 Commits

Author SHA1 Message Date
metalgearsloth
b6cadfedd5 Update arch (#4605) 2023-11-25 14:56:56 +11:00
metalgearsloth
9f57b705d7 Version: 182.0.0 2023-11-24 00:21:00 +11:00
metalgearsloth
68be9712ad Add entity gen to hashcode (#4601) 2023-11-24 00:19:58 +11:00
metalgearsloth
aaa446254c Version: 181.0.2 2023-11-23 23:43:47 +11:00
metalgearsloth
5e2d2ab317 Fix too many pointlights causing blackscreen (#4599) 2023-11-23 23:39:51 +11:00
metalgearsloth
20ae63fbbd Replace tile intersecting with enumerator (#4595) 2023-11-23 22:36:40 +11:00
metalgearsloth
a92c0cbef4 Fix nullable comps being raised for client gamestates (#4596) 2023-11-23 22:07:27 +11:00
11 changed files with 258 additions and 87 deletions

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -54,6 +54,22 @@ END TEMPLATE-->
*None yet*
## 182.0.0
### Breaking changes
* Add EntityUid's generation / version to the hashcode.
## 181.0.2
### Bugfixes
* Fix exceptions from having too many lights on screen and causing the game to go black.
* Fix components having events raised in ClientGameStateManager before fully set and causing nullable reference exceptions.
* Replace tile intersection IEnumerables with TileEnumerator internally. Also made it public for external callers that wish to avoid IEnumerable.
## 181.0.1
### Bugfixes

View File

@@ -1306,13 +1306,23 @@ namespace Robust.Client.GameStates
// To avoid shuffling the archetype we'll set the component range up-front.
if (addedComps.Count > 0)
{
// TODO: This fucking sucks but
// - Frequent archetype changes PER COMPONENT sucks
// - the components will be null in event handlers until it's done.
_entityManager.AddComponentRange(uid, addedCompTypes);
for (var i = 0; i < addedComps.Count; i++)
{
var comp = addedComps[i];
var component = addedComps[i];
var reg = addedRegistrations[i];
_entityManager.AddComponentInternal(uid, comp, reg, false, meta);
_entityManager.AddComponentInternalOnly(uid, component, reg, meta);
}
for (var i = 0; i < addedComps.Count; i++)
{
var component = addedComps[i];
var reg = addedRegistrations[i];
_entityManager.AddComponentEvents(uid, component, reg, false, meta);
}
}
}

View File

@@ -97,6 +97,9 @@ namespace Robust.Client.Graphics.Clyde
private (PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot)[] _lightsToRenderList = default!;
private LightCapacityComparer _lightCap = new();
private ShadowCapacityComparer _shadowCap = new ShadowCapacityComparer();
private unsafe void InitLighting()
{
@@ -570,6 +573,28 @@ namespace Robust.Client.Graphics.Clyde
return true;
}
private sealed class LightCapacityComparer : IComparer<(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot)>
{
public int Compare(
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) x,
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) y)
{
if (x.light.CastShadows && !y.light.CastShadows) return 1;
if (!x.light.CastShadows && y.light.CastShadows) return -1;
return 0;
}
}
private sealed class ShadowCapacityComparer : IComparer<(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot)>
{
public int Compare(
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) x,
(PointLightComponent light, Vector2 pos, float distanceSquared, Angle rot) y)
{
return x.distanceSquared.CompareTo(y.distanceSquared);
}
}
private (int count, Box2 expandedBounds) GetLightsToRender(
MapId map,
in Box2Rotated worldBounds,
@@ -595,20 +620,10 @@ namespace Robust.Client.Graphics.Clyde
// First, partition the array based on whether the lights are shadow casting or not
// (non shadow casting lights should be the first partition, shadow casting lights the second)
Array.Sort(_lightsToRenderList, 0, state.count,
Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
{
if (x.light.CastShadows && !y.light.CastShadows) return 1;
else if (!x.light.CastShadows && y.light.CastShadows) return -1;
else return 0;
}));
Array.Sort(_lightsToRenderList, 0, state.count, _lightCap);
// Next, sort just the shadow casting lights by distance.
Array.Sort(_lightsToRenderList, state.count - state.shadowCastingCount, state.shadowCastingCount,
Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
{
return x.distanceSquared.CompareTo(y.distanceSquared);
}));
Array.Sort(_lightsToRenderList, state.count - state.shadowCastingCount, state.shadowCastingCount, _shadowCap);
// Then effectively delete the furthest lights, by setting the end of the array to exclude N
// number of shadow casting lights (where N is the number above the max number per scene.)

View File

@@ -42,15 +42,20 @@ public sealed class TileEdgeOverlay : Overlay
_grids.Clear();
_mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds, ref _grids);
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
var mapSystem = _entManager.System<SharedMapSystem>();
var xformSystem = _entManager.System<SharedTransformSystem>();
foreach (var grid in _grids)
{
var tileSize = grid.Comp.TileSize;
var tileDimensions = new Vector2(tileSize, tileSize);
var xform = xformQuery.GetComponent(grid);
args.WorldHandle.SetTransform(xform.WorldMatrix);
var (_, _, worldMatrix, invMatrix) = xformSystem.GetWorldPositionRotationMatrixWithInv(grid.Owner);
args.WorldHandle.SetTransform(worldMatrix);
var localAABB = invMatrix.TransformBox(args.WorldBounds);
foreach (var tileRef in grid.Comp.GetTilesIntersecting(args.WorldBounds, false))
var enumerator = mapSystem.GetLocalTilesEnumerator(grid.Owner, grid.Comp, localAABB, false);
while (enumerator.MoveNext(out var tileRef))
{
var tileDef = _tileDefManager[tileRef.Tile.TypeId];
@@ -66,7 +71,7 @@ public sealed class TileEdgeOverlay : Overlay
continue;
var neighborIndices = new Vector2i(tileRef.GridIndices.X + x, tileRef.GridIndices.Y + y);
var neighborTile = grid.Comp.GetTileRef(neighborIndices);
var neighborTile = mapSystem.GetTileRef(grid.Owner, grid.Comp, neighborIndices);
var neighborDef = _tileDefManager[neighborTile.Tile.TypeId];
// If it's the same tile then no edge to be drawn.
@@ -118,9 +123,9 @@ public sealed class TileEdgeOverlay : Overlay
}
if (angle == Angle.Zero)
args.WorldHandle.DrawTextureRect(texture, box);
args.WorldHandle.DrawTextureRect(texture.Texture, box);
else
args.WorldHandle.DrawTextureRect(texture, new Box2Rotated(box, angle, box.Center));
args.WorldHandle.DrawTextureRect(texture.Texture, new Box2Rotated(box, angle, box.Center));
}
}
}

View File

@@ -5,30 +5,26 @@ namespace Robust.Shared.GameObjects;
internal struct ArchetypeIterator
{
private readonly Query _query;
private readonly PooledList<Archetype> _archetypes;
internal ArchetypeIterator(in Query query, PooledList<Archetype> archetypes)
internal ArchetypeIterator(PooledList<Archetype> archetypes)
{
_query = query;
_archetypes = archetypes;
}
public ArchetypeEnumerator GetEnumerator()
{
return new ArchetypeEnumerator(_query, _archetypes);
return new ArchetypeEnumerator(_archetypes);
}
}
internal struct ArchetypeEnumerator
{
private readonly Query _query;
private readonly PooledList<Archetype> _archetypes;
private int _index;
public ArchetypeEnumerator(in Query query, PooledList<Archetype> archetypes)
public ArchetypeEnumerator(PooledList<Archetype> archetypes)
{
_query = query;
_archetypes = archetypes;
_index = _archetypes.Count;
}
@@ -38,7 +34,7 @@ internal struct ArchetypeEnumerator
while (--_index >= 0)
{
var archetype = Current;
if (archetype.Entities > 0)
if (archetype.EntityCount > 0)
{
return true;
}

View File

@@ -29,7 +29,7 @@ internal struct ArchChunkEnumerator
if (_archetypes.MoveNext())
{
_chunkIndex = _archetypes.Current.Size;
_chunkIndex = _archetypes.Current.ChunkCount;
}
}
@@ -45,7 +45,7 @@ internal struct ArchChunkEnumerator
return false;
}
_chunkIndex = _archetypes.Current.Size - 1;
_chunkIndex = _archetypes.Current.ChunkCount - 1;
return true;
}
}
@@ -54,7 +54,7 @@ internal static partial class QueryExtensions
{
internal static ArchChunkIterator ChunkIterator(this in Query query, World world)
{
var archetypeEnumerator = new ArchetypeEnumerator(in query, query.Matches);
var archetypeEnumerator = new ArchetypeEnumerator(query.Matches);
return new ArchChunkIterator(in archetypeEnumerator);
}
}

View File

@@ -222,7 +222,9 @@ namespace Robust.Shared.GameObjects
AddComponentInternal(uid, component, reg, skipInit, metadata);
}
internal void AddComponentInternal<T>(EntityUid uid, T component, ComponentRegistration reg, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent
// TODO: Clean up this mess.
internal void AddComponentInternalOnly<T>(EntityUid uid, T component, ComponentRegistration reg, MetaDataComponent? metadata = null) where T : IComponent
{
// We can't use typeof(T) here in case T is just Component
DebugTools.Assert(component is MetaDataComponent ||
@@ -231,7 +233,7 @@ namespace Robust.Shared.GameObjects
// We can't use typeof(T) here in case T is just Component
DebugTools.Assert(component is MetaDataComponent ||
(metadata ?? MetaQuery.GetComponent(uid)).EntityLifeStage < EntityLifeStage.Terminating,
(metadata ?? MetaQuery.GetComponent(uid)).EntityLifeStage < EntityLifeStage.Terminating,
$"Attempted to add a {reg.Name} component to an entity ({ToPrettyString(uid)}) while it is terminating");
// TODO optimize this
@@ -257,7 +259,11 @@ namespace Robust.Shared.GameObjects
{
component.Networked = false;
}
}
internal void AddComponentEvents<T>(EntityUid uid, T component, ComponentRegistration reg, bool skipInit,
MetaDataComponent? metadata = null) where T : IComponent
{
var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg);
ComponentAdded?.Invoke(eventArgs);
_eventBus.OnComponentAdded(eventArgs);
@@ -285,6 +291,12 @@ namespace Robust.Shared.GameObjects
EventBus.RaiseComponentEvent(component, MapInitEventInstance);
}
internal void AddComponentInternal<T>(EntityUid uid, T component, ComponentRegistration reg, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent
{
AddComponentInternalOnly(uid, component, reg, metadata);
AddComponentEvents(uid, component, reg, skipInit, metadata);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool RemoveComponent<T>(EntityUid uid, MetaDataComponent? meta = null)
@@ -724,6 +736,7 @@ namespace Robust.Shared.GameObjects
{
if (IsAlive(uid) && _world.TryGet(uid, out component))
{
DebugTools.Assert(component != null);
if (!component!.Deleted)
{
return true;

View File

@@ -115,7 +115,10 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public override int GetHashCode()
{
return Id;
unchecked
{
return Id.GetHashCode() * 397 ^ Version.GetHashCode();
}
}
/// <summary>

View File

@@ -688,9 +688,8 @@ public abstract partial class SharedMapSystem
public IEnumerable<TileRef> GetAllTiles(EntityUid uid, MapGridComponent grid, bool ignoreEmpty = true)
{
foreach (var kvChunk in grid.Chunks)
foreach (var chunk in grid.Chunks.Values)
{
var chunk = kvChunk.Value;
for (ushort x = 0; x < grid.ChunkSize; x++)
{
for (ushort y = 0; y < grid.ChunkSize; y++)
@@ -771,11 +770,54 @@ public abstract partial class SharedMapSystem
RegenerateCollision(uid, grid, modified);
}
public TilesEnumerator GetLocalTilesEnumerator(EntityUid uid, MapGridComponent grid, Box2 aabb,
bool ignoreEmpty = true,
Predicate<TileRef>? predicate = null)
{
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, aabb);
return enumerator;
}
public TilesEnumerator GetTilesEnumerator(EntityUid uid, MapGridComponent grid, Box2 aabb, bool ignoreEmpty = true,
Predicate<TileRef>? predicate = null)
{
var invMatrix = _transform.GetInvWorldMatrix(uid);
var localAABB = invMatrix.TransformBox(aabb);
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
return enumerator;
}
public TilesEnumerator GetTilesEnumerator(EntityUid uid, MapGridComponent grid, Box2Rotated bounds, bool ignoreEmpty = true,
Predicate<TileRef>? predicate = null)
{
var invMatrix = _transform.GetInvWorldMatrix(uid);
var localAABB = invMatrix.TransformBox(bounds);
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
return enumerator;
}
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2 localAABB, bool ignoreEmpty = true,
Predicate<TileRef>? predicate = null)
{
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
while (enumerator.MoveNext(out var tileRef))
{
yield return tileRef;
}
}
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated localArea, bool ignoreEmpty = true,
Predicate<TileRef>? predicate = null)
{
var localAABB = localArea.CalcBoundingBox();
return GetLocalTilesIntersecting(uid, grid, localAABB, ignoreEmpty, predicate);
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localAABB);
while (enumerator.MoveNext(out var tileRef))
{
yield return tileRef;
}
}
public IEnumerable<TileRef> GetTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated worldArea, bool ignoreEmpty = true,
@@ -784,9 +826,11 @@ public abstract partial class SharedMapSystem
var matrix = _transform.GetInvWorldMatrix(uid);
var localArea = matrix.TransformBox(worldArea);
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localArea);
while (enumerator.MoveNext(out var tileRef))
{
yield return tile;
yield return tileRef;
}
}
@@ -796,46 +840,11 @@ public abstract partial class SharedMapSystem
var matrix = _transform.GetInvWorldMatrix(uid);
var localArea = matrix.TransformBox(worldArea);
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
var enumerator = new TilesEnumerator(this, ignoreEmpty, predicate, uid, grid, localArea);
while (enumerator.MoveNext(out var tileRef))
{
yield return tile;
}
}
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2 localArea, bool ignoreEmpty = true,
Predicate<TileRef>? predicate = null)
{
// TODO: Should move the intersecting calls onto mapmanager system and then allow people to pass in xform / xformquery
// that way we can avoid the GetComp here.
var gridTileLb = new Vector2i((int)Math.Floor(localArea.Left), (int)Math.Floor(localArea.Bottom));
// If we have 20.1 we want to include that tile but if we have 20 then we don't.
var gridTileRt = new Vector2i((int)Math.Ceiling(localArea.Right), (int)Math.Ceiling(localArea.Top));
for (var x = gridTileLb.X; x < gridTileRt.X; x++)
{
for (var y = gridTileLb.Y; y < gridTileRt.Y; y++)
{
var gridChunk = GridTileToChunkIndices(uid, grid, new Vector2i(x, y));
if (grid.Chunks.TryGetValue(gridChunk, out var chunk))
{
var chunkTile = chunk.GridTileToChunkTile(new Vector2i(x, y));
var tile = GetTileRef(uid, grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
if (ignoreEmpty && tile.Tile.IsEmpty)
continue;
if (predicate == null || predicate(tile))
yield return tile;
}
else if (!ignoreEmpty)
{
var tile = new TileRef(uid, x, y, Tile.Empty);
if (predicate == null || predicate(tile))
yield return tile;
}
}
yield return tileRef;
}
}
@@ -1020,22 +1029,32 @@ public abstract partial class SharedMapSystem
public IEnumerable<EntityUid> GetLocalAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 localAABB)
{
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localAABB, true, null))
var enumerator = new TilesEnumerator(this, true, null, uid, grid, localAABB);
while (enumerator.MoveNext(out var tileRef))
{
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
var anchoredEnumerator = GetAnchoredEntitiesEnumerator(uid, grid, tileRef.GridIndices);
while (anchoredEnumerator.MoveNext(out var ent))
{
yield return ent;
yield return ent.Value;
}
}
}
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 worldAABB)
{
foreach (var tile in GetTilesIntersecting(uid, grid, worldAABB))
var invWorldMatrix = _transform.GetInvWorldMatrix(uid);
var localAABB = invWorldMatrix.TransformBox(worldAABB);
var enumerator = new TilesEnumerator(this, true, null, uid, grid, localAABB);
while (enumerator.MoveNext(out var tileRef))
{
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
var anchoredEnumerator = GetAnchoredEntitiesEnumerator(uid, grid, tileRef.GridIndices);
while (anchoredEnumerator.MoveNext(out var ent))
{
yield return ent;
yield return ent.Value;
}
}
}
@@ -1422,4 +1441,98 @@ public abstract partial class SharedMapSystem
RegenerateCollision(uid, grid, mapChunk);
}
}
/// <summary>
/// Iterates the local tiles of the specified data.
/// </summary>
public struct TilesEnumerator
{
private readonly SharedMapSystem _mapSystem;
private readonly EntityUid _uid;
private readonly MapGridComponent _grid;
private readonly bool _ignoreEmpty;
private readonly Predicate<TileRef>? _predicate;
private readonly int _lowerY;
private readonly int _upperX;
private readonly int _upperY;
private int _x;
private int _y;
public TilesEnumerator(
SharedMapSystem mapSystem,
bool ignoreEmpty,
Predicate<TileRef>? predicate,
EntityUid uid,
MapGridComponent grid,
Box2 aabb)
{
_mapSystem = mapSystem;
_uid = uid;
_grid = grid;
_ignoreEmpty = ignoreEmpty;
_predicate = predicate;
// TODO: Should move the intersecting calls onto mapmanager system and then allow people to pass in xform / xformquery
// that way we can avoid the GetComp here.
var gridTileLb = new Vector2i((int)Math.Floor(aabb.Left), (int)Math.Floor(aabb.Bottom));
// If we have 20.1 we want to include that tile but if we have 20 then we don't.
var gridTileRt = new Vector2i((int)Math.Ceiling(aabb.Right), (int)Math.Ceiling(aabb.Top));
_x = gridTileLb.X;
_y = gridTileLb.Y;
_lowerY = gridTileLb.Y;
_upperX = gridTileRt.X;
_upperY = gridTileRt.Y;
}
public bool MoveNext(out TileRef tile)
{
if (_x >= _upperX)
{
tile = TileRef.Zero;
return false;
}
var gridTile = new Vector2i(_x, _y);
_y++;
if (_y >= _upperY)
{
_x++;
_y = _lowerY;
}
var gridChunk = _mapSystem.GridTileToChunkIndices(_uid, _grid, gridTile);
if (_grid.Chunks.TryGetValue(gridChunk, out var chunk))
{
var chunkTile = chunk.GridTileToChunkTile(gridTile);
tile = _mapSystem.GetTileRef(_uid, _grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
if (_ignoreEmpty && tile.Tile.IsEmpty)
return MoveNext(out tile);
if (_predicate == null || _predicate(tile))
{
return true;
}
}
else if (!_ignoreEmpty)
{
tile = new TileRef(_uid, gridTile.X, gridTile.Y, Tile.Empty);
if (_predicate == null || _predicate(tile))
{
return true;
}
}
return MoveNext(out tile);
}
}
}