mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Kill NetworkedMapManager (#3516)
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com> Co-authored-by: Paul <ritter.paul1@googlemail.com>
This commit is contained in:
@@ -604,11 +604,6 @@ namespace Robust.Client.GameStates
|
||||
_config.TickProcessMessages();
|
||||
}
|
||||
|
||||
using (_prof.Group("Map Pre"))
|
||||
{
|
||||
_mapManager.ApplyGameStatePre(curState.MapData, curState.EntityStates.Span);
|
||||
}
|
||||
|
||||
(IEnumerable<EntityUid> Created, List<EntityUid> Detached) output;
|
||||
using (_prof.Group("Entity"))
|
||||
{
|
||||
@@ -855,7 +850,7 @@ namespace Robust.Client.GameStates
|
||||
SharedTransformSystem xformSys)
|
||||
{
|
||||
// Processing deletions is non-trivial, because by default deletions will also delete all child entities.
|
||||
//
|
||||
//
|
||||
// Naively: easy, just apply server states to process any transform states before deleting, right? But now
|
||||
// that PVS detach messages are sent separately & processed over time, the entity may have left our view,
|
||||
// but not yet been moved to null-space. In that case, the server would not send us transform states, and
|
||||
@@ -1226,7 +1221,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
|
||||
/// Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
|
||||
/// </summary>
|
||||
private void ResetAllEnts(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -168,7 +169,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
public void _setChunkDirty(MapGridComponent grid, Vector2i chunk)
|
||||
{
|
||||
var data = _mapChunkData[grid.GridEntityId];
|
||||
var data = _mapChunkData.GetOrNew(grid.GridEntityId);
|
||||
if (data.TryGetValue(chunk, out var datum))
|
||||
{
|
||||
datum.Dirty = true;
|
||||
@@ -196,7 +197,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private void _updateOnGridCreated(GridStartupEvent ev)
|
||||
{
|
||||
var gridId = ev.EntityUid;
|
||||
_mapChunkData.Add(gridId, new Dictionary<Vector2i, MapChunkData>());
|
||||
_mapChunkData.GetOrNew(gridId);
|
||||
}
|
||||
|
||||
private void _updateOnGridRemoved(GridRemovalEvent ev)
|
||||
|
||||
@@ -265,7 +265,6 @@ namespace Robust.Server.GameStates
|
||||
playerChunks[sessionIndex], metadataQuery, transformQuery, viewerEntities[sessionIndex])
|
||||
: _pvs.GetAllEntityStates(session, lastAck, _gameTiming.CurTick);
|
||||
var playerStates = _playerManager.GetPlayerStates(lastAck);
|
||||
var mapData = _mapManager.GetStateData(lastAck);
|
||||
|
||||
// lastAck varies with each client based on lag and such, we can't just make 1 global state and send it to everyone
|
||||
var lastInputCommand = inputSystem.GetLastInputCommand(session);
|
||||
@@ -277,8 +276,7 @@ namespace Robust.Server.GameStates
|
||||
Math.Max(lastInputCommand, lastSystemMessage),
|
||||
entStates,
|
||||
playerStates,
|
||||
deletions,
|
||||
mapData);
|
||||
deletions);
|
||||
|
||||
InterlockedHelper.Min(ref oldestAckValue, lastAck.Value);
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
@@ -24,11 +28,103 @@ public abstract partial class SharedMapSystem
|
||||
return;
|
||||
|
||||
component.ChunkSize = state.ChunkSize;
|
||||
|
||||
if (state.ChunkData != null)
|
||||
{
|
||||
var modified = new List<(Vector2i position, Tile tile)>();
|
||||
MapManager.SuppressOnTileChanged = true;
|
||||
foreach (var chunkData in state.ChunkData)
|
||||
{
|
||||
if (chunkData.IsDeleted())
|
||||
continue;
|
||||
|
||||
var chunk = component.GetOrAddChunk(chunkData.Index);
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
DebugTools.Assert(chunkData.TileData.Length == component.ChunkSize * component.ChunkSize);
|
||||
|
||||
var counter = 0;
|
||||
for (ushort x = 0; x < component.ChunkSize; x++)
|
||||
{
|
||||
for (ushort y = 0; y < component.ChunkSize; y++)
|
||||
{
|
||||
var tile = chunkData.TileData[counter++];
|
||||
if (chunk.GetTile(x, y) == tile)
|
||||
continue;
|
||||
|
||||
chunk.SetTile(x, y, tile);
|
||||
modified.Add((new Vector2i(chunk.X * component.ChunkSize + x, chunk.Y * component.ChunkSize + y), tile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var chunkData in state.ChunkData)
|
||||
{
|
||||
if (chunkData.IsDeleted())
|
||||
{
|
||||
component.RemoveChunk(chunkData.Index);
|
||||
continue;
|
||||
}
|
||||
|
||||
var chunk = component.GetOrAddChunk(chunkData.Index);
|
||||
chunk.SuppressCollisionRegeneration = false;
|
||||
component.RegenerateCollision(chunk);
|
||||
}
|
||||
|
||||
MapManager.SuppressOnTileChanged = false;
|
||||
|
||||
if (modified.Count != 0)
|
||||
{
|
||||
RaiseLocalEvent(uid, new GridModifiedEvent(component, modified), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGridGetState(EntityUid uid, MapGridComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new MapGridComponentState(component.ChunkSize);
|
||||
// TODO: Actual deltas.
|
||||
List<ChunkDatum>? chunkData;
|
||||
var fromTick = args.FromTick;
|
||||
|
||||
if (component.LastTileModifiedTick < fromTick)
|
||||
{
|
||||
chunkData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunkData = new List<ChunkDatum>();
|
||||
var chunks = component.ChunkDeletionHistory;
|
||||
|
||||
foreach (var (tick, indices) in chunks)
|
||||
{
|
||||
if (tick < fromTick)
|
||||
continue;
|
||||
|
||||
chunkData.Add(ChunkDatum.CreateDeleted(indices));
|
||||
}
|
||||
|
||||
foreach (var (index, chunk) in component.GetMapChunks())
|
||||
{
|
||||
if (chunk.LastTileModifiedTick < fromTick)
|
||||
continue;
|
||||
|
||||
var tileBuffer = new Tile[component.ChunkSize * (uint) component.ChunkSize];
|
||||
|
||||
// Flatten the tile array.
|
||||
// NetSerializer doesn't do multi-dimensional arrays.
|
||||
// This is probably really expensive.
|
||||
for (var x = 0; x < component.ChunkSize; x++)
|
||||
{
|
||||
for (var y = 0; y < component.ChunkSize; y++)
|
||||
{
|
||||
tileBuffer[x * component.ChunkSize + y] = chunk.GetTile((ushort)x, (ushort)y);
|
||||
}
|
||||
}
|
||||
chunkData.Add(ChunkDatum.CreateModified(index, tileBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Mark it as delta proper
|
||||
args.State = new MapGridComponentState(component.ChunkSize, chunkData);
|
||||
}
|
||||
|
||||
private void OnGridAdd(EntityUid uid, MapGridComponent component, ComponentAdd args)
|
||||
|
||||
@@ -21,6 +21,13 @@ public abstract partial class SharedMapSystem
|
||||
return;
|
||||
|
||||
component.WorldMap = state.MapId;
|
||||
|
||||
if (!MapManager.MapExists(state.MapId))
|
||||
{
|
||||
var mapInternal = (IMapManagerInternal)MapManager;
|
||||
mapInternal.CreateMap(state.MapId, uid);
|
||||
}
|
||||
|
||||
component.LightingEnabled = state.LightingEnabled;
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using NetSerializer;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.GameStates
|
||||
{
|
||||
@@ -27,8 +26,7 @@ namespace Robust.Shared.GameStates
|
||||
uint lastInput,
|
||||
NetListAsArray<EntityState> entities,
|
||||
NetListAsArray<PlayerState> players,
|
||||
NetListAsArray<EntityUid> deletions,
|
||||
GameStateMapData? mapData)
|
||||
NetListAsArray<EntityUid> deletions)
|
||||
{
|
||||
FromSequence = fromSequence;
|
||||
ToSequence = toSequence;
|
||||
@@ -36,7 +34,6 @@ namespace Robust.Shared.GameStates
|
||||
EntityStates = entities;
|
||||
PlayerStates = players;
|
||||
EntityDeletions = deletions;
|
||||
MapData = mapData;
|
||||
}
|
||||
|
||||
public readonly GameTick FromSequence;
|
||||
@@ -47,6 +44,5 @@ namespace Robust.Shared.GameStates
|
||||
public readonly NetListAsArray<EntityState> EntityStates;
|
||||
public readonly NetListAsArray<PlayerState> PlayerStates;
|
||||
public readonly NetListAsArray<EntityUid> EntityDeletions;
|
||||
public readonly GameStateMapData? MapData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -8,63 +6,34 @@ using Robust.Shared.Serialization;
|
||||
namespace Robust.Shared.GameStates
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class GameStateMapData
|
||||
public readonly struct ChunkDatum
|
||||
{
|
||||
public readonly KeyValuePair<EntityUid, GridDatum>[]? GridData;
|
||||
public readonly Vector2i Index;
|
||||
|
||||
public GameStateMapData(KeyValuePair<EntityUid, GridDatum>[]? gridData)
|
||||
// Definitely wasteful to send EVERY tile.
|
||||
// Optimize away future coder.
|
||||
// Also it's stored row-major.
|
||||
public readonly Tile[] TileData;
|
||||
|
||||
public bool IsDeleted()
|
||||
{
|
||||
GridData = gridData;
|
||||
return TileData == default;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct GridDatum
|
||||
private ChunkDatum(Vector2i index, Tile[] tileData)
|
||||
{
|
||||
// TransformComponent State
|
||||
public readonly MapCoordinates Coordinates;
|
||||
public readonly Angle Angle;
|
||||
|
||||
// MapGridComponent State
|
||||
public readonly ChunkDatum[] ChunkData;
|
||||
|
||||
public GridDatum(ChunkDatum[] chunkData, MapCoordinates coordinates, Angle angle)
|
||||
{
|
||||
ChunkData = chunkData;
|
||||
Coordinates = coordinates;
|
||||
Angle = angle;
|
||||
}
|
||||
Index = index;
|
||||
TileData = tileData;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct ChunkDatum
|
||||
public static ChunkDatum CreateModified(Vector2i index, Tile[] tileData)
|
||||
{
|
||||
public readonly Vector2i Index;
|
||||
return new ChunkDatum(index, tileData);
|
||||
}
|
||||
|
||||
// Definitely wasteful to send EVERY tile.
|
||||
// Optimize away future coder.
|
||||
// Also it's stored row-major.
|
||||
public readonly Tile[] TileData;
|
||||
|
||||
public bool IsDeleted()
|
||||
{
|
||||
return TileData == default;
|
||||
}
|
||||
|
||||
private ChunkDatum(Vector2i index, Tile[] tileData)
|
||||
{
|
||||
Index = index;
|
||||
TileData = tileData;
|
||||
}
|
||||
|
||||
public static ChunkDatum CreateModified(Vector2i index, Tile[] tileData)
|
||||
{
|
||||
return new ChunkDatum(index, tileData);
|
||||
}
|
||||
|
||||
public static ChunkDatum CreateDeleted(Vector2i index)
|
||||
{
|
||||
return new ChunkDatum(index, default!);
|
||||
}
|
||||
public static ChunkDatum CreateDeleted(Vector2i index)
|
||||
{
|
||||
return new ChunkDatum(index, default!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,55 +82,6 @@ namespace Robust.Shared.Map.Components
|
||||
_entMan.EventBus.RaiseLocalEvent(Owner, new EmptyGridEvent { GridId = Owner }, true);
|
||||
}
|
||||
|
||||
internal static void ApplyMapGridState(NetworkedMapManager networkedMapManager, MapGridComponent gridComp,
|
||||
GameStateMapData.ChunkDatum[] chunkUpdates)
|
||||
{
|
||||
networkedMapManager.SuppressOnTileChanged = true;
|
||||
var modified = new List<(Vector2i position, Tile tile)>();
|
||||
|
||||
foreach (var chunkData in chunkUpdates)
|
||||
{
|
||||
if (chunkData.IsDeleted())
|
||||
continue;
|
||||
|
||||
var chunk = gridComp.GetOrAddChunk(chunkData.Index);
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
DebugTools.Assert(chunkData.TileData.Length == gridComp.ChunkSize * gridComp.ChunkSize);
|
||||
|
||||
var counter = 0;
|
||||
for (ushort x = 0; x < gridComp.ChunkSize; x++)
|
||||
{
|
||||
for (ushort y = 0; y < gridComp.ChunkSize; y++)
|
||||
{
|
||||
var tile = chunkData.TileData[counter++];
|
||||
if (chunk.GetTile(x, y) == tile)
|
||||
continue;
|
||||
|
||||
chunk.SetTile(x, y, tile);
|
||||
modified.Add((new Vector2i(chunk.X * gridComp.ChunkSize + x, chunk.Y * gridComp.ChunkSize + y), tile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modified.Count != 0)
|
||||
MapManager.InvokeGridChanged(networkedMapManager, gridComp, modified);
|
||||
|
||||
foreach (var chunkData in chunkUpdates)
|
||||
{
|
||||
if (chunkData.IsDeleted())
|
||||
{
|
||||
gridComp.RemoveChunk(chunkData.Index);
|
||||
continue;
|
||||
}
|
||||
|
||||
var chunk = gridComp.GetOrAddChunk(chunkData.Index);
|
||||
chunk.SuppressCollisionRegeneration = false;
|
||||
gridComp.RegenerateCollision(chunk);
|
||||
}
|
||||
|
||||
networkedMapManager.SuppressOnTileChanged = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerate collision for multiple chunks at once; faster than doing it individually.
|
||||
/// </summary>
|
||||
@@ -923,8 +874,9 @@ namespace Robust.Shared.Map.Components
|
||||
{
|
||||
// As the collision regeneration can potentially delete the chunk we'll notify of the tile changed first.
|
||||
var gridTile = mapChunk.ChunkTileToGridTile(tileIndices);
|
||||
mapChunk.LastTileModifiedTick = _timing.CurTick;
|
||||
LastTileModifiedTick = _timing.CurTick;
|
||||
mapChunk.LastTileModifiedTick = _mapManager.GameTiming.CurTick;
|
||||
LastTileModifiedTick = _mapManager.GameTiming.CurTick;
|
||||
_entMan.Dirty(this);
|
||||
|
||||
// The map serializer currently sets tiles of unbound grids as part of the deserialization process
|
||||
// It properly sets SuppressOnTileChanged so that the event isn't spammed for every tile on the grid.
|
||||
@@ -951,15 +903,20 @@ namespace Robust.Shared.Map.Components
|
||||
/// <summary>
|
||||
/// The size of the chunks in the map grid.
|
||||
/// </summary>
|
||||
public ushort ChunkSize { get; }
|
||||
public ushort ChunkSize;
|
||||
|
||||
/// <summary>
|
||||
/// Networked chunk data.
|
||||
/// </summary>
|
||||
public List<ChunkDatum>? ChunkData;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="MapGridComponentState"/>.
|
||||
/// </summary>
|
||||
/// <param name="chunkSize">The size of the chunks in the map grid.</param>
|
||||
public MapGridComponentState(ushort chunkSize)
|
||||
public MapGridComponentState(ushort chunkSize, List<ChunkDatum>? chunkData)
|
||||
{
|
||||
ChunkSize = chunkSize;
|
||||
ChunkData = chunkData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ namespace Robust.Shared.Map
|
||||
/// <param name="oldTile">The old tile that got replaced.</param>
|
||||
void RaiseOnTileChanged(TileRef tileRef, Tile oldTile);
|
||||
|
||||
MapId CreateMap(MapId? mapId, EntityUid euid);
|
||||
|
||||
void TrueDeleteMap(MapId mapId);
|
||||
void OnGridBoundsChange(EntityUid uid, MapGridComponent grid);
|
||||
}
|
||||
|
||||
@@ -159,13 +159,6 @@ internal partial class MapManager
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<TileChangedEventArgs>? TileChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Should the OnTileChanged event be suppressed? This is useful for initially loading the map
|
||||
/// so that you don't spam an event for each of the million station tiles.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<GridChangedEventArgs>? GridChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SuppressOnTileChanged { get; set; }
|
||||
|
||||
@@ -208,10 +201,4 @@ internal partial class MapManager
|
||||
EntityManager.StartComponents(gridEnt);
|
||||
return grid;
|
||||
}
|
||||
|
||||
protected internal static void InvokeGridChanged(MapManager mapManager, MapGridComponent mapGrid, IReadOnlyCollection<(Vector2i position, Tile tile)> changedTiles)
|
||||
{
|
||||
mapManager.GridChanged?.Invoke(mapManager, new GridChangedEventArgs(mapGrid, changedTiles));
|
||||
mapManager.EntityManager.EventBus.RaiseLocalEvent(mapGrid.GridEntityId, new GridModifiedEvent(mapGrid, changedTiles), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ internal partial class MapManager
|
||||
{
|
||||
var args = new MapEventArgs(mapId);
|
||||
OnMapDestroyedGridTree(args);
|
||||
MapDestroyed?.Invoke(this, args);
|
||||
_mapEntities.Remove(mapId);
|
||||
}
|
||||
|
||||
@@ -78,7 +77,7 @@ internal partial class MapManager
|
||||
/// <inheritdoc />
|
||||
public MapId CreateMap(MapId? mapId = null)
|
||||
{
|
||||
return CreateMap(mapId, default);
|
||||
return ((IMapManagerInternal) this).CreateMap(mapId, default);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -223,13 +222,7 @@ internal partial class MapManager
|
||||
return _highestMapId = new MapId(_highestMapId.Value + 1);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<MapEventArgs>? MapCreated;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<MapEventArgs>? MapDestroyed;
|
||||
|
||||
protected MapId CreateMap(MapId? mapId, EntityUid entityUid)
|
||||
MapId IMapManagerInternal.CreateMap(MapId? mapId, EntityUid entityUid)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
throw new InvalidOperationException("Attempted to create a null-space map.");
|
||||
@@ -283,8 +276,6 @@ internal partial class MapManager
|
||||
|
||||
var args = new MapEventArgs(actualId);
|
||||
OnMapCreatedGridTree(args);
|
||||
MapCreated?.Invoke(this, args);
|
||||
|
||||
return actualId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Map;
|
||||
|
||||
internal interface INetworkedMapManager : IMapManagerInternal
|
||||
{
|
||||
GameStateMapData? GetStateData(GameTick fromTick);
|
||||
void CullDeletionHistory(GameTick upToTick);
|
||||
|
||||
// Two methods here, so that new grids etc can be made BEFORE entities get states applied,
|
||||
// but old ones can be deleted after.
|
||||
void ApplyGameStatePre(GameStateMapData? data, ReadOnlySpan<EntityState> entityStates);
|
||||
}
|
||||
|
||||
internal sealed class NetworkedMapManager : MapManager, INetworkedMapManager
|
||||
{
|
||||
public GameStateMapData? GetStateData(GameTick fromTick)
|
||||
{
|
||||
var gridDatums = new Dictionary<EntityUid, GameStateMapData.GridDatum>();
|
||||
var enumerator = EntityManager.AllEntityQueryEnumerator<MapGridComponent>();
|
||||
|
||||
while (enumerator.MoveNext(out var iGrid))
|
||||
{
|
||||
if (iGrid.LastTileModifiedTick < fromTick)
|
||||
continue;
|
||||
|
||||
var chunkData = new List<GameStateMapData.ChunkDatum>();
|
||||
var chunks = iGrid.ChunkDeletionHistory;
|
||||
|
||||
foreach (var (tick, indices) in chunks)
|
||||
{
|
||||
if (tick < fromTick)
|
||||
continue;
|
||||
|
||||
chunkData.Add(GameStateMapData.ChunkDatum.CreateDeleted(indices));
|
||||
}
|
||||
|
||||
foreach (var (index, chunk) in iGrid.GetMapChunks())
|
||||
{
|
||||
if (chunk.LastTileModifiedTick < fromTick)
|
||||
continue;
|
||||
|
||||
var tileBuffer = new Tile[iGrid.ChunkSize * (uint) iGrid.ChunkSize];
|
||||
|
||||
// Flatten the tile array.
|
||||
// NetSerializer doesn't do multi-dimensional arrays.
|
||||
// This is probably really expensive.
|
||||
for (var x = 0; x < iGrid.ChunkSize; x++)
|
||||
{
|
||||
for (var y = 0; y < iGrid.ChunkSize; y++)
|
||||
{
|
||||
tileBuffer[x * iGrid.ChunkSize + y] = chunk.GetTile((ushort)x, (ushort)y);
|
||||
}
|
||||
}
|
||||
chunkData.Add(GameStateMapData.ChunkDatum.CreateModified(index, tileBuffer));
|
||||
}
|
||||
|
||||
var gridXform = EntityManager.GetComponent<TransformComponent>(iGrid.GridEntityId);
|
||||
var (worldPos, worldRot) = gridXform.GetWorldPositionRotation();
|
||||
|
||||
var gridDatum = new GameStateMapData.GridDatum(
|
||||
chunkData.ToArray(),
|
||||
new MapCoordinates(worldPos, gridXform.MapID),
|
||||
worldRot);
|
||||
|
||||
gridDatums.Add(iGrid.GridEntityId, gridDatum);
|
||||
}
|
||||
|
||||
// no point sending empty collections
|
||||
if (gridDatums.Count == 0)
|
||||
return default;
|
||||
|
||||
return new GameStateMapData(gridDatums.ToArray<KeyValuePair<EntityUid, GameStateMapData.GridDatum>>());
|
||||
}
|
||||
|
||||
public void CullDeletionHistory(GameTick upToTick)
|
||||
{
|
||||
var query = EntityManager.EntityQueryEnumerator<MapGridComponent>();
|
||||
var query = EntityManager.AllEntityQueryEnumerator<MapGridComponent>();
|
||||
|
||||
while (query.MoveNext(out var grid))
|
||||
{
|
||||
@@ -90,110 +20,4 @@ internal sealed class NetworkedMapManager : MapManager, INetworkedMapManager
|
||||
chunks.RemoveAll(t => t.tick < upToTick);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<(MapId mapId, EntityUid euid)> _newMaps = new();
|
||||
private List<(MapId mapId, EntityUid euid, ushort chunkSize)> _newGrids = new();
|
||||
|
||||
public void ApplyGameStatePre(GameStateMapData? data, ReadOnlySpan<EntityState> entityStates)
|
||||
{
|
||||
// Setup new maps and grids
|
||||
{
|
||||
// search for any newly created map components
|
||||
foreach (var entityState in entityStates)
|
||||
{
|
||||
foreach (var compChange in entityState.ComponentChanges.Span)
|
||||
{
|
||||
if (compChange.State is MapComponentState mapCompState)
|
||||
{
|
||||
var mapEuid = entityState.Uid;
|
||||
var mapId = mapCompState.MapId;
|
||||
|
||||
// map already exists from a previous state.
|
||||
if (MapExists(mapId))
|
||||
continue;
|
||||
|
||||
_newMaps.Add((mapId, mapEuid));
|
||||
}
|
||||
else if (data != null && data.GridData != null && compChange.State is MapGridComponentState gridCompState)
|
||||
{
|
||||
var gridEuid = entityState.Uid;
|
||||
var chunkSize = gridCompState.ChunkSize;
|
||||
|
||||
// grid already exists from a previous state
|
||||
if(GridExists(gridEuid))
|
||||
continue;
|
||||
|
||||
// Existing ent?
|
||||
// I love NetworkedMapManager
|
||||
if (EntityManager.EntityExists(gridEuid))
|
||||
{
|
||||
EntityManager.AddComponent<MapGridComponent>(gridEuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(chunkSize > 0, $"Invalid chunk size in entity state for new grid {gridEuid}.");
|
||||
|
||||
MapId gridMapId = default;
|
||||
foreach (var kvData in data.GridData)
|
||||
{
|
||||
if (kvData.Key != gridEuid)
|
||||
continue;
|
||||
|
||||
gridMapId = kvData.Value.Coordinates.MapId;
|
||||
break;
|
||||
}
|
||||
|
||||
DebugTools.Assert(gridMapId != default, $"Could not find corresponding gridData for new grid {gridEuid}.");
|
||||
|
||||
_newGrids.Add((gridMapId, gridEuid, chunkSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create all the new maps
|
||||
foreach (var (mapId, euid) in _newMaps)
|
||||
{
|
||||
CreateMap(mapId, euid);
|
||||
}
|
||||
_newMaps.Clear();
|
||||
|
||||
// create all the new grids
|
||||
foreach (var (mapId, euid, chunkSize) in _newGrids)
|
||||
{
|
||||
CreateGrid(mapId, chunkSize, euid);
|
||||
}
|
||||
_newGrids.Clear();
|
||||
}
|
||||
|
||||
// Process all grid updates.
|
||||
if (data != null && data.GridData != null)
|
||||
{
|
||||
// Ok good all the grids and maps exist now.
|
||||
foreach (var (gridId, gridDatum) in data.GridData)
|
||||
{
|
||||
var xformComp = EntityManager.GetComponent<TransformComponent>(gridId);
|
||||
ApplyTransformState(xformComp, gridDatum);
|
||||
|
||||
var gridComp = EntityManager.GetComponent<MapGridComponent>(gridId);
|
||||
MapGridComponent.ApplyMapGridState(this, gridComp, gridDatum.ChunkData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyTransformState(TransformComponent xformComp, GameStateMapData.GridDatum gridDatum)
|
||||
{
|
||||
if (xformComp.MapID != gridDatum.Coordinates.MapId)
|
||||
throw new NotImplementedException("Moving grids between maps is not yet implemented");
|
||||
|
||||
// TODO: SHITCODE ALERT -> When we get proper ECS we can delete this.
|
||||
if (xformComp.WorldPosition != gridDatum.Coordinates.Position)
|
||||
{
|
||||
xformComp.WorldPosition = gridDatum.Coordinates.Position;
|
||||
}
|
||||
|
||||
if (xformComp.WorldRotation != gridDatum.Angle)
|
||||
{
|
||||
xformComp.WorldRotation = gridDatum.Angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace Robust.UnitTesting.Client.GameStates
|
||||
/// </summary>
|
||||
private static GameState GameStateFactory(uint from, uint to)
|
||||
{
|
||||
return new(new GameTick(@from), new GameTick(to), 0, default, default, default, null);
|
||||
return new(new GameTick(@from), new GameTick(to), 0, default, default, default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
new EntityUid(512),
|
||||
new []
|
||||
{
|
||||
new ComponentChange(0, new MapGridComponentState(16), default)
|
||||
new ComponentChange(0, new MapGridComponentState(16, null), default)
|
||||
}, default);
|
||||
|
||||
serializer.Serialize(stream, payload);
|
||||
|
||||
Reference in New Issue
Block a user