Misc grid state changes (#5597)

This commit is contained in:
Leon Friedrich
2025-01-18 03:07:09 +11:00
committed by GitHub
parent 5d84be9c78
commit 32d8a1cba9
8 changed files with 80 additions and 111 deletions

View File

@@ -26,6 +26,8 @@ namespace Robust.Client.GameObjects
protected override void OnAppearanceGetState(EntityUid uid, AppearanceComponent component, ref ComponentGetState args)
{
// TODO Game State
// Force the client to serialize & de-serialize implicitly generated component states.
var clone = CloneAppearanceData(component.AppearanceData);
args.State = new AppearanceComponentState(clone);
}

View File

@@ -153,7 +153,6 @@ namespace Robust.Shared.GameObjects
newFixtures.Add(($"grid_chunk-{bounds.Left}-{bounds.Bottom}", newFixture));
}
var toRemove = new ValueList<(string Id, Fixture Fixture)>();
// Check if we even need to issue an eventbus event
var updated = false;
@@ -167,6 +166,13 @@ namespace Robust.Shared.GameObjects
for (var i = newFixtures.Count - 1; i >= 0; i--)
{
var fixture = newFixtures[i].Fixture;
// TODO GRIDS
// Fix this
// This **only** works if we assume the density is always the default (PhysicsConstants.DefaultDensity).
// Hence, this always fails in SS14 because ShuttleSystem.OnGridFixtureChange changes the density.
// So it constantly creats & destroys fixtures unnecessarily
// AAAAA
if (!oldFixture.Equals(fixture))
continue;
@@ -175,21 +181,16 @@ namespace Robust.Shared.GameObjects
break;
}
if (existing)
continue;
// Doesn't align with any new fixtures so delete
if (existing) continue;
toRemove.Add((oldId, oldFixture));
chunk.Fixtures.Remove(oldId);
_fixtures.DestroyFixture(uid, oldId, oldFixture, false, body: body, manager: manager, xform: xform);
updated = true;
}
foreach (var (id, fixture) in toRemove.Span)
{
// TODO add a DestroyFixture() override that takes in a list.
// reduced broadphase lookups
chunk.Fixtures.Remove(id);
_fixtures.DestroyFixture(uid, id, fixture, false, body: body, manager: manager, xform: xform);
}
if (newFixtures.Count > 0 || toRemove.Count > 0)
if (newFixtures.Count > 0)
{
updated = true;
}
@@ -200,10 +201,11 @@ namespace Robust.Shared.GameObjects
chunk.Fixtures.Add(id);
var existingFixture = _fixtures.GetFixtureOrNull(uid, id, manager: manager);
// Check if it's the same (otherwise remove anyway).
// TODO GRIDS
// wasn't this already checked?
if (existingFixture?.Shape is PolygonShape poly &&
poly.EqualsApprox((PolygonShape) fixture.Shape))
{
continue;
}

View File

@@ -245,13 +245,10 @@ public abstract partial class SharedMapSystem
private void OnGridHandleState(EntityUid uid, MapGridComponent component, ref ComponentHandleState args)
{
HashSet<MapChunk> modifiedChunks;
switch (args.Current)
{
case MapGridComponentDeltaState delta:
{
modifiedChunks = new();
DebugTools.Assert(component.ChunkSize == delta.ChunkSize || component.Chunks.Count == 0,
"Can't modify chunk size of an existing grid.");
@@ -261,7 +258,7 @@ public abstract partial class SharedMapSystem
foreach (var (index, chunkData) in delta.ChunkData)
{
ApplyChunkData(uid, component, index, chunkData, modifiedChunks);
ApplyChunkData(uid, component, index, chunkData);
}
component.LastTileModifiedTick = delta.LastTileModifiedTick;
@@ -269,7 +266,6 @@ public abstract partial class SharedMapSystem
}
case MapGridComponentState state:
{
modifiedChunks = new();
DebugTools.Assert(component.ChunkSize == state.ChunkSize || component.Chunks.Count == 0,
"Can't modify chunk size of an existing grid.");
@@ -279,12 +275,13 @@ public abstract partial class SharedMapSystem
foreach (var index in component.Chunks.Keys)
{
if (!state.FullGridData.ContainsKey(index))
ApplyChunkData(uid, component, index, ChunkDatum.Empty, modifiedChunks);
ApplyChunkData(uid, component, index, ChunkDatum.Empty);
}
foreach (var (index, data) in state.FullGridData)
{
ApplyChunkData(uid, component, index, new(data), modifiedChunks);
DebugTools.Assert(!data.IsDeleted());
ApplyChunkData(uid, component, index, data);
}
break;
@@ -309,10 +306,8 @@ public abstract partial class SharedMapSystem
EntityUid uid,
MapGridComponent component,
Vector2i index,
ChunkDatum data,
HashSet<MapChunk> modifiedChunks)
ChunkDatum data)
{
bool shapeChanged = false;
var counter = 0;
if (data.IsDeleted())
@@ -326,7 +321,7 @@ public abstract partial class SharedMapSystem
{
for (ushort y = 0; y < component.ChunkSize; y++)
{
if (!deletedChunk.TrySetTile(x, y, Tile.Empty, out var oldTile, out var chunkShapeChanged))
if (!deletedChunk.TrySetTile(x, y, Tile.Empty, out var oldTile, out _))
continue;
var gridIndices = deletedChunk.ChunkTileToGridTile((x, y));
@@ -336,9 +331,6 @@ public abstract partial class SharedMapSystem
}
component.Chunks.Remove(index);
// TODO is this required?
modifiedChunks.Add(deletedChunk);
return;
}
@@ -351,30 +343,26 @@ public abstract partial class SharedMapSystem
for (ushort y = 0; y < component.ChunkSize; y++)
{
var tile = data.TileData[counter++];
if (!chunk.TrySetTile(x, y, tile, out var oldTile, out var tileShapeChanged))
if (!chunk.TrySetTile(x, y, tile, out var oldTile, out _))
continue;
shapeChanged |= tileShapeChanged;
var gridIndices = chunk.ChunkTileToGridTile((x, y));
var newTileRef = new TileRef(uid, gridIndices, tile);
_mapInternal.RaiseOnTileChanged(newTileRef, oldTile, index);
}
}
if (data.Fixtures != null && !chunk.Fixtures.SetEquals(data.Fixtures))
// These should never refer to the same object
DebugTools.AssertNotEqual(chunk.Fixtures, data.Fixtures);
if (!chunk.Fixtures.SetEquals(data.Fixtures))
{
chunk.Fixtures.Clear();
if (data.Fixtures != null)
chunk.Fixtures.UnionWith(data.Fixtures);
chunk.Fixtures.UnionWith(data.Fixtures);
}
chunk.CachedBounds = data.CachedBounds!.Value;
chunk.SuppressCollisionRegeneration = false;
if (shapeChanged)
{
modifiedChunks.Add(chunk);
}
}
private void OnGridGetState(EntityUid uid, MapGridComponent component, ref ComponentGetState args)
@@ -429,7 +417,15 @@ public abstract partial class SharedMapSystem
tileBuffer[x * component.ChunkSize + y] = chunk.GetTile((ushort)x, (ushort)y);
}
}
chunkData.Add(index, ChunkDatum.CreateModified(tileBuffer, chunk.Fixtures, chunk.CachedBounds));
// The client needs to clone the fixture set instead of storing a reference.
// TODO Game State
// Force the client to serialize & de-serialize implicitly generated component states.
var fixtures = chunk.Fixtures;
if (_netManager.IsClient)
fixtures = new(fixtures);
chunkData.Add(index, ChunkDatum.CreateModified(tileBuffer, fixtures, chunk.CachedBounds));
}
}
@@ -466,7 +462,15 @@ public abstract partial class SharedMapSystem
tileBuffer[x * component.ChunkSize + y] = chunk.GetTile((ushort)x, (ushort)y);
}
}
chunkData.Add(index, ChunkDatum.CreateModified(tileBuffer, chunk.Fixtures, chunk.CachedBounds));
// The client needs to clone the fixture set instead of storing a reference.
// TODO Game State
// Force the client to serialize & de-serialize implicitly generated component states.
var fixtures = chunk.Fixtures;
if (_netManager.IsClient)
fixtures = new(fixtures);
chunkData.Add(index, ChunkDatum.CreateModified(tileBuffer, fixtures, chunk.CachedBounds));
}
args.State = new MapGridComponentState(component.ChunkSize, chunkData, component.LastTileModifiedTick);
@@ -644,10 +648,12 @@ public abstract partial class SharedMapSystem
foreach (var id in mapChunk.Fixtures)
{
mapChunk.Fixtures.Remove(id);
_fixtures.DestroyFixture(uid, id, false, manager: manager, body: body, xform: xform);
}
RemoveChunk(uid, grid, mapChunk.Indices);
DebugTools.AssertEqual(mapChunk.Fixtures.Count, 0);
removedChunks.Add(mapChunk);
}
}

View File

@@ -322,12 +322,14 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
}
case 1 << 2:
{
var state = new UserInterfaceStatesDeltaState()
{
States = new Dictionary<Enum, BoundUserInterfaceState>(ent.Comp.States),
};
var states = ent.Comp.States;
args.State = state;
// TODO Game State
// Force the client to serialize & de-serialize implicitly generated component states.
if (_netManager.IsClient)
states = new(states);
args.State = new UserInterfaceStatesDeltaState {States = states};
return;
}
}
@@ -336,6 +338,8 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
var actors = new Dictionary<Enum, List<NetEntity>>();
var dataCopy = new Dictionary<Enum, InterfaceData>(ent.Comp.Interfaces.Count);
// TODO Game State
// Force the client to serialize & de-serialize implicitly generated component states.
foreach (var (weh, a) in ent.Comp.Interfaces)
{
dataCopy[weh] = new InterfaceData(a);

View File

@@ -10,7 +10,7 @@ namespace Robust.Shared.GameStates
[Serializable, NetSerializable]
public readonly struct ChunkDatum
{
public static readonly ChunkDatum Empty = new ChunkDatum();
public static readonly ChunkDatum Empty = new();
public readonly HashSet<string>? Fixtures;
@@ -21,28 +21,12 @@ namespace Robust.Shared.GameStates
public readonly Box2i? CachedBounds;
[MemberNotNullWhen(false, nameof(TileData))]
[MemberNotNullWhen(false, nameof(TileData), nameof(Fixtures))]
public bool IsDeleted()
{
return TileData == null;
}
internal ChunkDatum(ChunkDatum data)
{
if (data.TileData != null)
{
TileData = new Tile[data.TileData.Length];
data.TileData.CopyTo(TileData, 0);
}
if (data.Fixtures != null)
{
Fixtures = new HashSet<string>(data.Fixtures);
}
CachedBounds = data.CachedBounds;
}
private ChunkDatum(Tile[] tileData, HashSet<string> fixtures, Box2i cachedBounds)
{
TileData = tileData;

View File

@@ -319,7 +319,7 @@ namespace Robust.Shared.Map.Components
if (data.IsDeleted())
state.FullGridData.Remove(index);
else
state.FullGridData[index] = new(data);
state.FullGridData[index] = data;
}
state.LastTileModifiedTick = LastTileModifiedTick;
@@ -327,14 +327,10 @@ namespace Robust.Shared.Map.Components
public MapGridComponentState CreateNewFullState(MapGridComponentState state)
{
var fullGridData = new Dictionary<Vector2i, ChunkDatum>(state.FullGridData.Count);
if (ChunkData == null)
return new(ChunkSize, state.FullGridData, state.LastTileModifiedTick);
foreach (var (key, value) in state.FullGridData)
{
fullGridData[key] = new(value);
}
var newState = new MapGridComponentState(ChunkSize, fullGridData, LastTileModifiedTick);
var newState = new MapGridComponentState(ChunkSize, state.FullGridData.ShallowClone(), LastTileModifiedTick);
ApplyToFullState(newState);
return newState;
}

View File

@@ -5,11 +5,13 @@ using Robust.Shared.Collections;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Events;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Shared.Physics.Systems
@@ -21,6 +23,7 @@ namespace Robust.Shared.Physics.Systems
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private EntityQuery<PhysicsMapComponent> _mapQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<FixturesComponent> _fixtureQuery;
@@ -180,6 +183,18 @@ namespace Robust.Shared.Physics.Systems
return;
}
// Temporary debug block for trying to help catch a bug where grid fixtures disappear without the chunk's
// fixture set being updated
#if DEBUG
if (TryComp(uid, out MapGridComponent? grid) && !_timing.ApplyingState)
{
foreach (var chunk in grid.Chunks.Values)
{
DebugTools.Assert(!chunk.Fixtures.Contains(fixtureId), $"A grid fixture is being deleted without first removing it from the chunk. Please report this bug.");
}
}
#endif
foreach (var contact in fixture.Contacts.Values.ToArray())
{
_physics.DestroyContact(contact);

View File

@@ -1,40 +0,0 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Physics.Components;
namespace Robust.Shared.Physics.Systems;
public abstract partial class SharedPhysicsSystem
{
private void InitializeFixturesChange()
{
SubscribeLocalEvent<FixturesChangeComponent, ComponentStartup>(OnChangeStartup);
SubscribeLocalEvent<FixturesChangeComponent, ComponentShutdown>(OnChangeShutdown);
}
private void OnChangeStartup(Entity<FixturesChangeComponent> ent, ref ComponentStartup args)
{
foreach (var (id, fixture) in ent.Comp.Fixtures)
{
_fixtures.TryCreateFixture(ent.Owner,
fixture.Shape,
id,
fixture.Density,
fixture.Hard,
fixture.CollisionLayer,
fixture.CollisionMask,
fixture.Friction,
fixture.Restitution);
}
// TODO: Fixture creation should be handling this.
WakeBody(ent.Owner);
}
private void OnChangeShutdown(Entity<FixturesChangeComponent> ent, ref ComponentShutdown args)
{
foreach (var id in ent.Comp.Fixtures.Keys)
{
_fixtures.DestroyFixture(ent.Owner, id);
}
}
}