mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Refactor map netcode. (#726)
Maps are now synchronized as part of game states. This instantly fixed all race conditions and the client can now live through a round restart with 0 problems (outside of Godot crashing yay).
This commit is contained in:
committed by
GitHub
parent
8d58ad304a
commit
58fb11a989
@@ -187,10 +187,6 @@ namespace SS14.Client
|
||||
_stateManager.RequestStateChange<GameScreen>();
|
||||
|
||||
OnPlayerJoinedGame(_playMan.LocalPlayer.Session);
|
||||
|
||||
// request entire map be sent to us
|
||||
var mapMsg = _net.CreateNetMessage<MsgMapReq>();
|
||||
_net.ClientSendMessage(mapMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace SS14.Client.GameObjects
|
||||
}
|
||||
|
||||
// After the first set of states comes in we do the startup.
|
||||
if (!EntitiesInitialized && MapsInitialized)
|
||||
if (!EntitiesInitialized)
|
||||
{
|
||||
InitializeEntities();
|
||||
}
|
||||
@@ -163,7 +163,6 @@ namespace SS14.Client.GameObjects
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
MapsInitialized = false;
|
||||
EntitiesInitialized = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using SS14.Shared.Network.Messages;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SS14.Client.Player;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
|
||||
namespace SS14.Client.GameStates
|
||||
{
|
||||
@@ -29,6 +30,8 @@ namespace SS14.Client.GameStates
|
||||
private readonly IClientNetManager netManager;
|
||||
[Dependency]
|
||||
private readonly IBaseClient baseClient;
|
||||
[Dependency]
|
||||
private readonly IMapManager _mapManager;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -73,8 +76,10 @@ namespace SS14.Client.GameStates
|
||||
|
||||
private void ApplyGameState(GameState gameState)
|
||||
{
|
||||
_mapManager.ApplyGameStatePre(gameState.MapData);
|
||||
entityManager.ApplyEntityStates(gameState.EntityStates, gameState.EntityDeletions, gameState.GameTime);
|
||||
playerManager.ApplyPlayerStates(gameState.PlayerStates);
|
||||
_mapManager.ApplyGameStatePost(gameState.MapData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ namespace SS14.Client.Map
|
||||
private void UpdateOnGridModified(object sender, GridChangedEventArgs args)
|
||||
{
|
||||
var tilemap = RenderTileMaps[args.Grid.Index];
|
||||
foreach ((int x, int y, Tile tile) in args.Modified)
|
||||
foreach (var (index, tile) in args.Modified)
|
||||
{
|
||||
tilemap.SetCell(x, -y, tile.TileId);
|
||||
tilemap.SetCell(index.X, -index.Y, tile.TileId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,12 @@ using SS14.Server.Interfaces.GameState;
|
||||
using SS14.Server.Interfaces.Player;
|
||||
using SS14.Shared.Enums;
|
||||
using SS14.Shared.GameStates;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Network.Messages;
|
||||
using SS14.Shared.Utility;
|
||||
|
||||
namespace SS14.Server.GameStates
|
||||
{
|
||||
@@ -31,6 +33,8 @@ namespace SS14.Server.GameStates
|
||||
[Dependency]
|
||||
private IPlayerManager _playerManager;
|
||||
|
||||
[Dependency] private IMapManager _mapManager;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgState>(MsgState.NAME);
|
||||
@@ -44,16 +48,18 @@ namespace SS14.Server.GameStates
|
||||
|
||||
public void SendGameStateUpdate()
|
||||
{
|
||||
DebugTools.Assert(_networkManager.IsServer);
|
||||
|
||||
if (!_networkManager.IsConnected)
|
||||
{
|
||||
// Prevent deletions piling up if we have no clients.
|
||||
_entityManager.CullDeletionHistory(uint.MaxValue);
|
||||
_mapManager.CullDeletionHistory(uint.MaxValue);
|
||||
return;
|
||||
}
|
||||
|
||||
var connections = _networkManager.Channels;
|
||||
uint oldestAck = uint.MaxValue;
|
||||
foreach (var connection in connections)
|
||||
var oldestAck = uint.MaxValue;
|
||||
foreach (var connection in _networkManager.Channels)
|
||||
{
|
||||
if (!ackedStates.TryGetValue(connection.ConnectionId, out var ack))
|
||||
{
|
||||
@@ -74,10 +80,11 @@ namespace SS14.Server.GameStates
|
||||
var entities = _entityManager.GetEntityStates(oldestAck);
|
||||
var players = _playerManager.GetPlayerStates();
|
||||
var deletions = _entityManager.GetDeletedEntities(oldestAck);
|
||||
var mapData = _mapManager.GetStateData(oldestAck);
|
||||
|
||||
var state = new GameState(oldestAck, _gameTiming.CurTick, entities, players, deletions);
|
||||
var state = new GameState(oldestAck, _gameTiming.CurTick, entities, players, deletions, mapData);
|
||||
|
||||
foreach (var c in connections)
|
||||
foreach (var c in _networkManager.Channels)
|
||||
{
|
||||
var session = _playerManager.GetSessionByChannel(c);
|
||||
|
||||
|
||||
@@ -9,17 +9,6 @@
|
||||
RequestEntRemove,
|
||||
}
|
||||
|
||||
public enum MapMessage
|
||||
{
|
||||
TurfUpdate = 0,
|
||||
TurfClick,
|
||||
SendTileMap,
|
||||
SendMapInfo,
|
||||
|
||||
CreateMap,
|
||||
DeleteMap
|
||||
}
|
||||
|
||||
public enum SessionStatus : byte
|
||||
{
|
||||
Zombie = 0,
|
||||
|
||||
@@ -69,7 +69,6 @@ namespace SS14.Shared.GameObjects
|
||||
protected bool EntitiesInitialized;
|
||||
|
||||
public bool Started { get; protected set; }
|
||||
public bool MapsInitialized { get; set; } = false;
|
||||
|
||||
#region IEntityManager Members
|
||||
|
||||
@@ -93,25 +92,14 @@ namespace SS14.Shared.GameObjects
|
||||
public virtual void Update(float frameTime)
|
||||
{
|
||||
ProcessMsgBuffer();
|
||||
|
||||
// for some 'special' reason entities require the map they are on to exist before they can
|
||||
// be initialized. This should stop the EntitySystems from updating over the uninitialized entities.
|
||||
if(MapsInitialized || _network.IsServer)
|
||||
{
|
||||
EntitySystemManager.Update(frameTime);
|
||||
|
||||
// dispatching events to systems will usually cause them to interact
|
||||
// with entities, we don't want that to happen if the maps are not initialized.
|
||||
ProcessEventQueue();
|
||||
}
|
||||
|
||||
EntitySystemManager.Update(frameTime);
|
||||
ProcessEventQueue();
|
||||
CullDeletedEntities();
|
||||
}
|
||||
|
||||
public virtual void FrameUpdate(float frameTime)
|
||||
{
|
||||
if (MapsInitialized || _network.IsServer)
|
||||
EntitySystemManager.FrameUpdate(frameTime);
|
||||
EntitySystemManager.FrameUpdate(frameTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -19,14 +19,14 @@ namespace SS14.Shared.GameStates
|
||||
/// <summary>
|
||||
/// Constructor!
|
||||
/// </summary>
|
||||
/// <param name="sequence"></param>
|
||||
public GameState(uint fromSequence, uint toSequence, List<EntityState> entities, List<PlayerState> players, List<EntityUid> deletions)
|
||||
public GameState(uint fromSequence, uint toSequence, List<EntityState> entities, List<PlayerState> players, List<EntityUid> deletions, GameStateMapData mapData)
|
||||
{
|
||||
FromSequence = fromSequence;
|
||||
ToSequence = toSequence;
|
||||
EntityStates = entities;
|
||||
PlayerStates = players;
|
||||
EntityDeletions = deletions;
|
||||
MapData = mapData;
|
||||
}
|
||||
|
||||
public readonly uint FromSequence;
|
||||
@@ -35,5 +35,6 @@ namespace SS14.Shared.GameStates
|
||||
public readonly List<EntityState> EntityStates;
|
||||
public readonly List<PlayerState> PlayerStates;
|
||||
public readonly List<EntityUid> EntityDeletions;
|
||||
public readonly GameStateMapData MapData;
|
||||
}
|
||||
}
|
||||
|
||||
73
SS14.Shared/GameStates/GameStateMapData.cs
Normal file
73
SS14.Shared/GameStates/GameStateMapData.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Maths;
|
||||
using SS14.Shared.Serialization;
|
||||
|
||||
namespace SS14.Shared.GameStates
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public class GameStateMapData
|
||||
{
|
||||
// Dict of the new maps along with which grids are their defaults.
|
||||
public readonly Dictionary<MapId, GridId> CreatedMaps;
|
||||
public readonly Dictionary<GridId, GridCreationDatum> CreatedGrids;
|
||||
public readonly Dictionary<GridId, GridDatum> GridData;
|
||||
public readonly List<GridId> DeletedGrids;
|
||||
public readonly List<MapId> DeletedMaps;
|
||||
|
||||
public GameStateMapData(Dictionary<GridId, GridDatum> gridData, List<GridId> deletedGrids, List<MapId> deletedMaps, Dictionary<MapId, GridId> createdMaps, Dictionary<GridId, GridCreationDatum> createdGrids)
|
||||
{
|
||||
GridData = gridData;
|
||||
DeletedGrids = deletedGrids;
|
||||
DeletedMaps = deletedMaps;
|
||||
CreatedMaps = createdMaps;
|
||||
CreatedGrids = createdGrids;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct GridCreationDatum
|
||||
{
|
||||
public readonly ushort ChunkSize;
|
||||
public readonly float SnapSize;
|
||||
public readonly bool IsTheDefault;
|
||||
|
||||
public GridCreationDatum(ushort chunkSize, float snapSize, bool isTheDefault)
|
||||
{
|
||||
ChunkSize = chunkSize;
|
||||
SnapSize = snapSize;
|
||||
IsTheDefault = isTheDefault;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct GridDatum
|
||||
{
|
||||
public readonly MapCoordinates Coordinates;
|
||||
public readonly List<ChunkDatum> ChunkData;
|
||||
|
||||
public GridDatum(List<ChunkDatum> chunkData, MapCoordinates coordinates)
|
||||
{
|
||||
ChunkData = chunkData;
|
||||
Coordinates = coordinates;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct ChunkDatum
|
||||
{
|
||||
public readonly MapIndices Index;
|
||||
|
||||
// Definitely wasteful to send EVERY tile.
|
||||
// Optimize away future coder.
|
||||
// Also it's stored row-major.
|
||||
public readonly Tile[] TileData;
|
||||
|
||||
public ChunkDatum(MapIndices index, Tile[] tileData)
|
||||
{
|
||||
Index = index;
|
||||
TileData = tileData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,6 @@ namespace SS14.Shared.Interfaces.GameObjects
|
||||
/// </summary>
|
||||
void FrameUpdate(float frameTime);
|
||||
|
||||
bool MapsInitialized { get; set; }
|
||||
|
||||
IComponentManager ComponentManager { get; }
|
||||
IEntityNetworkManager EntityNetManager { get; }
|
||||
|
||||
|
||||
@@ -14,11 +14,6 @@ namespace SS14.Shared.Interfaces.Map
|
||||
/// </summary>
|
||||
ushort ChunkSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The supported version of the chunk format.
|
||||
/// </summary>
|
||||
uint Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The X index of this chunk.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SS14.Shared.GameStates;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
using SS14.Shared.Map;
|
||||
|
||||
@@ -67,8 +68,6 @@ namespace SS14.Shared.Interfaces.Map
|
||||
|
||||
bool TryGetMap(MapId mapID, out IMap map);
|
||||
|
||||
void SendMap(INetChannel channel);
|
||||
|
||||
void DeleteMap(MapId mapID);
|
||||
|
||||
IMapGrid CreateGrid(MapId currentMapID, GridId? gridID = null, ushort chunkSize = 16, float snapSize = 1);
|
||||
@@ -100,5 +99,13 @@ namespace SS14.Shared.Interfaces.Map
|
||||
/// An existing map has been destroyed.
|
||||
/// </summary>
|
||||
event EventHandler<MapEventArgs> MapDestroyed;
|
||||
|
||||
GameStateMapData GetStateData(uint fromTick);
|
||||
void CullDeletionHistory(uint 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);
|
||||
void ApplyGameStatePost(GameStateMapData data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ namespace SS14.Shared.Map
|
||||
/// </summary>
|
||||
internal class Chunk : IMapChunk
|
||||
{
|
||||
private const int ChunkVersion = 1;
|
||||
internal uint LastModifiedTick { get; private set; }
|
||||
private readonly MapGrid _grid;
|
||||
private readonly MapIndices _gridIndices;
|
||||
private readonly MapManager _mapManager;
|
||||
|
||||
private readonly Tile[,] _tiles;
|
||||
internal readonly Tile[,] _tiles;
|
||||
private readonly SnapGridCell[,] _snapGrid;
|
||||
|
||||
/// <summary>
|
||||
@@ -34,6 +34,7 @@ namespace SS14.Shared.Map
|
||||
public Chunk(MapManager manager, MapGrid grid, int x, int y, ushort chunkSize)
|
||||
{
|
||||
_mapManager = manager;
|
||||
LastModifiedTick = _mapManager._gameTiming.CurTick;
|
||||
ChunkSize = chunkSize;
|
||||
_grid = grid;
|
||||
_gridIndices = new MapIndices(x, y);
|
||||
@@ -45,9 +46,6 @@ namespace SS14.Shared.Map
|
||||
/// <inheritdoc />
|
||||
public ushort ChunkSize { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Version => ChunkVersion;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int X => _gridIndices.X;
|
||||
|
||||
@@ -107,6 +105,7 @@ namespace SS14.Shared.Map
|
||||
var gridTile = ChunkTileToGridTile(new MapIndices(xChunkTile, yChunkTile));
|
||||
var newTileRef = new TileRef(_grid.MapID, _grid.Index, gridTile.X, gridTile.Y, tile);
|
||||
var oldTile = _tiles[xChunkTile, yChunkTile];
|
||||
_grid.LastModifiedTick = LastModifiedTick = _mapManager._gameTiming.CurTick;
|
||||
_mapManager.RaiseOnTileChanged(newTileRef, oldTile);
|
||||
_grid.UpdateAABB(gridTile);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SS14.Shared.Map
|
||||
{
|
||||
class Map : IMap
|
||||
{
|
||||
public uint CreatedTick { get; }
|
||||
public IMapGrid DefaultGrid { get; set; }
|
||||
public MapId Index { get; }
|
||||
private readonly MapManager _mapManager;
|
||||
@@ -17,6 +18,7 @@ namespace SS14.Shared.Map
|
||||
{
|
||||
Index = mapID;
|
||||
_mapManager = mapManager;
|
||||
CreatedTick = _mapManager._gameTiming.CurTick;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -10,11 +10,14 @@ namespace SS14.Shared.Map
|
||||
{
|
||||
public class MapGrid : IMapGrid
|
||||
{
|
||||
public uint CreatedTick { get; }
|
||||
public uint LastModifiedTick { get; internal set; }
|
||||
public bool IsDefaultGrid => Map.DefaultGrid == this;
|
||||
public IMap Map => _mapManager.GetMap(MapID);
|
||||
public MapId MapID { get; private set; }
|
||||
private readonly MapManager _mapManager;
|
||||
private readonly Dictionary<MapIndices, Chunk> _chunks = new Dictionary<MapIndices, Chunk>();
|
||||
internal readonly Dictionary<MapIndices, Chunk> _chunks = new Dictionary<MapIndices, Chunk>();
|
||||
private Vector2 _worldPosition;
|
||||
|
||||
internal MapGrid(MapManager mapManager, GridId gridIndex, ushort chunkSize, float snapsize, MapId mapID)
|
||||
{
|
||||
@@ -23,6 +26,7 @@ namespace SS14.Shared.Map
|
||||
ChunkSize = chunkSize;
|
||||
SnapSize = snapsize;
|
||||
MapID = mapID;
|
||||
LastModifiedTick = CreatedTick = _mapManager._gameTiming.CurTick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,10 +51,18 @@ namespace SS14.Shared.Map
|
||||
/// <summary>
|
||||
/// The length of the side of a square tile in world units.
|
||||
/// </summary>
|
||||
public ushort TileSize { get; set; } = 1;
|
||||
public ushort TileSize { get; } = 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2 WorldPosition { get; set; }
|
||||
public Vector2 WorldPosition
|
||||
{
|
||||
get => _worldPosition;
|
||||
set
|
||||
{
|
||||
_worldPosition = value;
|
||||
LastModifiedTick = _mapManager._gameTiming.CurTick;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands the AABB for this grid when a new tile is added. If the tile is already inside the existing AABB,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using SS14.Shared.Enums;
|
||||
using SS14.Shared.GameStates;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
@@ -14,205 +16,155 @@ namespace SS14.Shared.Map
|
||||
{
|
||||
public partial class MapManager
|
||||
{
|
||||
[Dependency]
|
||||
private readonly INetManager _netManager;
|
||||
[Dependency] private readonly INetManager _netManager;
|
||||
|
||||
private int _gridsToReceive;
|
||||
private int _gridsReceived;
|
||||
|
||||
public void SendMap(INetChannel channel)
|
||||
public GameStateMapData GetStateData(uint fromTick)
|
||||
{
|
||||
if (_netManager.IsClient)
|
||||
return;
|
||||
|
||||
DebugTools.Assert(_netManager.IsServer, "Why is the client calling this?");
|
||||
|
||||
Logger.Info($"{channel.RemoteEndPoint}: Sending map");
|
||||
|
||||
var quantityGridsSent = 0;
|
||||
|
||||
foreach (var map in GetAllMaps())
|
||||
var gridDatums = new Dictionary<GridId, GameStateMapData.GridDatum>();
|
||||
foreach (var grid in _grids.Values)
|
||||
{
|
||||
foreach (var grid in map.GetAllGrids())
|
||||
if (grid.LastModifiedTick < fromTick)
|
||||
{
|
||||
quantityGridsSent++;
|
||||
var message = _netManager.CreateNetMessage<MsgMap>();
|
||||
message.MessageType = MapMessage.SendTileMap;
|
||||
message.MapIndex = map.Index;
|
||||
message.GridIndex = grid.Index;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Map chunks
|
||||
var gridSize = grid.ChunkSize;
|
||||
message.ChunkSize = gridSize;
|
||||
message.ChunkDefs = new MsgMap.ChunkDef[grid.ChunkCount];
|
||||
var defCounter = 0;
|
||||
foreach (var chunk in grid.GetMapChunks())
|
||||
var chunkData = new List<GameStateMapData.ChunkDatum>();
|
||||
foreach (var (index, chunk) in grid._chunks)
|
||||
{
|
||||
if (chunk.LastModifiedTick < fromTick)
|
||||
{
|
||||
var newChunk = new MsgMap.ChunkDef
|
||||
{
|
||||
X = chunk.X,
|
||||
Y = chunk.Y
|
||||
};
|
||||
|
||||
newChunk.Tiles = new uint[gridSize * gridSize];
|
||||
var counter = 0;
|
||||
foreach (var tile in chunk)
|
||||
{
|
||||
newChunk.Tiles[counter] = (uint)tile.Tile;
|
||||
counter++;
|
||||
}
|
||||
|
||||
message.ChunkDefs[defCounter++] = newChunk;
|
||||
continue;
|
||||
}
|
||||
|
||||
_netManager.ServerSendMessage(message, channel);
|
||||
}
|
||||
}
|
||||
var msg = _netManager.CreateNetMessage<MsgMap>();
|
||||
msg.MessageType = MapMessage.SendMapInfo;
|
||||
msg.MapGridsToSend = quantityGridsSent;
|
||||
_netManager.ServerSendMessage(msg, channel);
|
||||
}
|
||||
var tileBuffer = new Tile[grid.ChunkSize * (uint) grid.ChunkSize];
|
||||
|
||||
private void HandleNetworkMessage(MsgMap message)
|
||||
{
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case MapMessage.TurfClick:
|
||||
HandleTurfClick(message);
|
||||
break;
|
||||
case MapMessage.TurfUpdate:
|
||||
HandleTileUpdate(message);
|
||||
break;
|
||||
case MapMessage.SendTileMap:
|
||||
HandleTileMap(message);
|
||||
break;
|
||||
case MapMessage.SendMapInfo:
|
||||
CollectMapInfo(message);
|
||||
break;
|
||||
case MapMessage.CreateMap:
|
||||
CreateMap(message.MapIndex);
|
||||
break;
|
||||
case MapMessage.DeleteMap:
|
||||
DeleteMap(message.MapIndex);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(message));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleTurfClick(MsgMap message)
|
||||
{
|
||||
/*
|
||||
// Who clicked and on what tile.
|
||||
Atom.Atom clicker = SS13Server.Singleton.playerManager.GetSessionByConnection(message.SenderConnection).attachedAtom;
|
||||
short x = message.ReadInt16();
|
||||
short y = message.ReadInt16();
|
||||
if (Vector2.Distance(clicker.position, new Vector2(x * tileSpacing + (tileSpacing / 2), y * tileSpacing + (tileSpacing / 2))) > 96)
|
||||
{
|
||||
return; // They were too far away to click us!
|
||||
}
|
||||
bool Update = false;
|
||||
if (IsSaneArrayPosition(x, y))
|
||||
{
|
||||
Update = tileArray[x, y].ClickedBy(clicker);
|
||||
if (Update)
|
||||
{
|
||||
if (tileArray[x, y].tileState == TileState.Dead)
|
||||
// Flatten the tile array.
|
||||
// NetSerializer doesn't do multi-dimensional arrays.
|
||||
// This is probably really expensive.
|
||||
for (var x = 0; x < grid.ChunkSize; x++)
|
||||
for (var y = 0; y < grid.ChunkSize; y++)
|
||||
{
|
||||
Tiles.Atmos.GasCell g = tileArray[x, y].gasCell;
|
||||
Tiles.Tile t = GenerateNewTile(x, y, tileArray[x, y].tileType);
|
||||
tileArray[x, y] = t;
|
||||
tileArray[x, y].gasCell = g;
|
||||
tileBuffer[x * grid.ChunkSize + y] = chunk._tiles[x, y];
|
||||
}
|
||||
NetworkUpdateTile(x, y);
|
||||
|
||||
chunkData.Add(new GameStateMapData.ChunkDatum(index, tileBuffer));
|
||||
}
|
||||
|
||||
var gridDatum =
|
||||
new GameStateMapData.GridDatum(chunkData, new MapCoordinates(grid.WorldPosition, grid.MapID));
|
||||
|
||||
gridDatums.Add(grid.Index, gridDatum);
|
||||
}
|
||||
*/
|
||||
|
||||
var mapDeletionsData = _mapDeletionHistory.Where(d => d.tick >= fromTick).Select(d => d.mapId).ToList();
|
||||
var gridDeletionsData = _gridDeletionHistory.Where(d => d.tick >= fromTick).Select(d => d.gridId).ToList();
|
||||
var mapCreations = _maps.Values.Where(m => m.CreatedTick >= fromTick)
|
||||
.ToDictionary(m => m.Index, m => m.DefaultGrid.Index);
|
||||
var gridCreations = _grids.Values.Where(g => g.CreatedTick >= fromTick).ToDictionary(g => g.Index,
|
||||
grid => new GameStateMapData.GridCreationDatum(grid.ChunkSize, grid.SnapSize,
|
||||
grid.IsDefaultGrid));
|
||||
|
||||
return new GameStateMapData(gridDatums, gridDeletionsData, mapDeletionsData, mapCreations, gridCreations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an IMapManager and ITileDefinitionManager from a properly formatted NetMessage.
|
||||
/// </summary>
|
||||
/// <param name="message">The message containing a serialized map and tileDefines.</param>
|
||||
private void HandleTileMap(MsgMap message)
|
||||
public void CullDeletionHistory(uint uptoTick)
|
||||
{
|
||||
DebugTools.Assert(_netManager.IsClient, "Why is the server calling this?");
|
||||
_mapDeletionHistory.RemoveAll(t => t.tick < uptoTick);
|
||||
_gridDeletionHistory.RemoveAll(t => t.tick < uptoTick);
|
||||
}
|
||||
|
||||
_gridsReceived++;
|
||||
public void ApplyGameStatePre(GameStateMapData data)
|
||||
{
|
||||
DebugTools.Assert(_netManager.IsClient, "Only the client should call this.");
|
||||
|
||||
var mapIndex = message.MapIndex;
|
||||
var gridIndex = message.GridIndex;
|
||||
// First we need to figure out all the NEW MAPS.
|
||||
// And make their default grids too.
|
||||
foreach (var (mapId, gridId) in data.CreatedMaps)
|
||||
{
|
||||
if (_maps.ContainsKey(mapId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var gridCreation = data.CreatedGrids[gridId];
|
||||
DebugTools.Assert(gridCreation.IsTheDefault);
|
||||
|
||||
var chunkSize = message.ChunkSize;
|
||||
var chunkCount = message.ChunkDefs.Length;
|
||||
var newMap = new Map(this, mapId);
|
||||
_maps.Add(mapId, newMap);
|
||||
MapCreated?.Invoke(this, new MapEventArgs(newMap));
|
||||
newMap.DefaultGrid = CreateGrid(newMap.Index, gridId, gridCreation.ChunkSize, gridCreation.SnapSize);
|
||||
}
|
||||
|
||||
if (!TryGetMap(mapIndex, out var map))
|
||||
map = CreateMap(mapIndex);
|
||||
// Then make all the other grids.
|
||||
foreach (var (gridId, creationDatum) in data.CreatedGrids)
|
||||
{
|
||||
if (creationDatum.IsTheDefault || _grids.ContainsKey(gridId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!map.GridExists(gridIndex))
|
||||
GetMap(mapIndex).CreateGrid(gridIndex, chunkSize);
|
||||
|
||||
var grid = GetMap(mapIndex).GetGrid(gridIndex);
|
||||
CreateGrid(data.GridData[gridId].Coordinates.MapId, gridId, creationDatum.ChunkSize,
|
||||
creationDatum.SnapSize);
|
||||
}
|
||||
|
||||
SuppressOnTileChanged = true;
|
||||
var modified = new List<(int x, int y, Tile tile)>();
|
||||
|
||||
for (var i = 0; i < chunkCount; ++i)
|
||||
// Ok good all the grids and maps exist now.
|
||||
foreach (var (gridId, gridDatum) in data.GridData)
|
||||
{
|
||||
var chunkPos = new MapIndices(message.ChunkDefs[i].X, message.ChunkDefs[i].Y);
|
||||
var chunk = grid.GetChunk(chunkPos);
|
||||
|
||||
var counter = 0;
|
||||
for (ushort x = 0; x < chunk.ChunkSize; x++)
|
||||
var grid = _grids[gridId];
|
||||
if (grid.MapID != gridDatum.Coordinates.MapId)
|
||||
{
|
||||
for (ushort y = 0; y < chunk.ChunkSize; y++)
|
||||
throw new NotImplementedException("Moving grids between maps is not yet implemented");
|
||||
}
|
||||
|
||||
grid.WorldPosition = gridDatum.Coordinates.Position;
|
||||
|
||||
var modified = new List<(MapIndices position, Tile tile)>();
|
||||
foreach (var chunkData in gridDatum.ChunkData)
|
||||
{
|
||||
var chunk = grid.GetChunk(chunkData.Index);
|
||||
DebugTools.Assert(chunkData.TileData.Length == grid.ChunkSize * grid.ChunkSize);
|
||||
|
||||
var counter = 0;
|
||||
for (ushort x = 0; x < grid.ChunkSize; x++)
|
||||
for (ushort y = 0; y < grid.ChunkSize; y++)
|
||||
{
|
||||
var tile = (Tile)message.ChunkDefs[i].Tiles[counter];
|
||||
var tile = chunkData.TileData[counter++];
|
||||
if (chunk.GetTile(x, y).Tile != tile)
|
||||
{
|
||||
chunk.SetTile(x, y, tile);
|
||||
modified.Add((x + chunk.X * chunk.ChunkSize, y + chunk.Y * chunk.ChunkSize, tile));
|
||||
modified.Add((new MapIndices(chunk.X * grid.ChunkSize + x, chunk.Y * grid.ChunkSize + y), tile));
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified.Count != 0)
|
||||
{
|
||||
GridChanged?.Invoke(this, new GridChangedEventArgs(grid, modified));
|
||||
}
|
||||
}
|
||||
|
||||
SuppressOnTileChanged = false;
|
||||
if (modified.Count != 0)
|
||||
}
|
||||
|
||||
public void ApplyGameStatePost(GameStateMapData data)
|
||||
{
|
||||
DebugTools.Assert(_netManager.IsClient, "Only the client should call this.");
|
||||
|
||||
foreach (var grid in data.DeletedGrids)
|
||||
{
|
||||
GridChanged?.Invoke(this, new GridChangedEventArgs(grid, modified));
|
||||
if (_grids.ContainsKey(grid))
|
||||
{
|
||||
DeleteGrid(grid);
|
||||
}
|
||||
}
|
||||
|
||||
if (_gridsReceived == _gridsToReceive)
|
||||
IoCManager.Resolve<IEntityManager>().MapsInitialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a single tile from the network message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message containing the info.</param>
|
||||
private void HandleTileUpdate(MsgMap message)
|
||||
{
|
||||
DebugTools.Assert(_netManager.IsClient, "Why is the server calling this?");
|
||||
|
||||
var x = message.SingleTurf.X;
|
||||
var y = message.SingleTurf.Y;
|
||||
var tile = (Tile)message.SingleTurf.Tile;
|
||||
|
||||
var pos = new GridCoordinates(x, y, message.GridIndex);
|
||||
pos.Grid.SetTile(pos, tile);
|
||||
}
|
||||
|
||||
private void CollectMapInfo(MsgMap message)
|
||||
{
|
||||
DebugTools.Assert(_netManager.IsClient, "Why is the server calling this?");
|
||||
|
||||
_gridsToReceive = message.MapGridsToSend;
|
||||
|
||||
if (_gridsReceived == _gridsToReceive)
|
||||
IoCManager.Resolve<IEntityManager>().MapsInitialized = true;
|
||||
foreach (var map in data.DeletedMaps)
|
||||
{
|
||||
if (_maps.ContainsKey(map))
|
||||
{
|
||||
DeleteMap(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SS14.Shared.Enums;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Log;
|
||||
using SS14.Shared.Maths;
|
||||
@@ -14,6 +15,8 @@ namespace SS14.Shared.Map
|
||||
/// <inheritdoc />
|
||||
public partial class MapManager : IMapManager, IPostInjectInit
|
||||
{
|
||||
[Dependency] private protected readonly IGameTiming _gameTiming;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IMap DefaultMap => GetMap(MapId.Nullspace);
|
||||
|
||||
@@ -50,6 +53,9 @@ namespace SS14.Shared.Map
|
||||
|
||||
private readonly Dictionary<GridId, MapGrid> _grids = new Dictionary<GridId, MapGrid>();
|
||||
|
||||
private readonly List<(uint tick, GridId gridId)> _gridDeletionHistory = new List<(uint, GridId)>();
|
||||
private readonly List<(uint tick, MapId mapId)> _mapDeletionHistory = new List<(uint, MapId)>();
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
CreateMap(MapId.Nullspace, GridId.Nullspace);
|
||||
@@ -58,14 +64,13 @@ namespace SS14.Shared.Map
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgMap>(MsgMap.NAME, HandleNetworkMessage);
|
||||
_netManager.RegisterNetMessage<MsgMapReq>(MsgMapReq.NAME, message => SendMap(message.MsgChannel));
|
||||
// So uh I removed the contents from this but I'm too lazy to remove the Initialize method.
|
||||
// Deal with it.
|
||||
}
|
||||
|
||||
public void Startup()
|
||||
{
|
||||
_gridsToReceive = -1;
|
||||
_gridsReceived = 0;
|
||||
// Ditto, removed contents but too lazy to remove method.
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
@@ -92,22 +97,6 @@ namespace SS14.Shared.Map
|
||||
return;
|
||||
|
||||
TileChanged?.Invoke(this, new TileChangedEventArgs(tileRef, oldTile));
|
||||
|
||||
if (_netManager.IsClient)
|
||||
return;
|
||||
|
||||
var message = _netManager.CreateNetMessage<MsgMap>();
|
||||
|
||||
message.MessageType = MapMessage.TurfUpdate;
|
||||
message.SingleTurf = new MsgMap.Turf
|
||||
{
|
||||
X = tileRef.X,
|
||||
Y = tileRef.Y,
|
||||
Tile = (uint)tileRef.Tile
|
||||
};
|
||||
message.GridIndex = tileRef.LocalPos.GridID;
|
||||
|
||||
_netManager.ServerSendToAll(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,12 +120,7 @@ namespace SS14.Shared.Map
|
||||
if (_netManager.IsClient)
|
||||
return;
|
||||
|
||||
var msg = _netManager.CreateNetMessage<MsgMap>();
|
||||
|
||||
msg.MessageType = MapMessage.DeleteMap;
|
||||
msg.MapIndex = mapID;
|
||||
|
||||
_netManager.ServerSendToAll(msg);
|
||||
_mapDeletionHistory.Add((_gameTiming.CurTick, mapID));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -171,16 +155,6 @@ namespace SS14.Shared.Map
|
||||
MapCreated?.Invoke(this, new MapEventArgs(newMap));
|
||||
newMap.DefaultGrid = CreateGrid(newMap.Index, defaultGridID);
|
||||
|
||||
if (_netManager.IsClient)
|
||||
return newMap;
|
||||
|
||||
var msg = _netManager.CreateNetMessage<MsgMap>();
|
||||
|
||||
msg.MessageType = MapMessage.CreateMap;
|
||||
msg.MapIndex = newMap.Index;
|
||||
|
||||
_netManager.ServerSendToAll(msg);
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
@@ -275,6 +249,12 @@ namespace SS14.Shared.Map
|
||||
_grids.Remove(grid.Index);
|
||||
|
||||
OnGridRemoved?.Invoke(gridID);
|
||||
|
||||
if (_netManager.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_gridDeletionHistory.Add((_gameTiming.CurTick, gridID));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,12 +312,12 @@ namespace SS14.Shared.Map
|
||||
/// </summary>
|
||||
public IMapGrid Grid { get; }
|
||||
|
||||
public IReadOnlyCollection<(int x, int y, Tile tile)> Modified { get; }
|
||||
public IReadOnlyCollection<(MapIndices position, Tile tile)> Modified { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of this class.
|
||||
/// </summary>
|
||||
public GridChangedEventArgs(IMapGrid grid, IReadOnlyCollection<(int x, int y, Tile tile)> modified)
|
||||
public GridChangedEventArgs(IMapGrid grid, IReadOnlyCollection<(MapIndices position, Tile tile)> modified)
|
||||
{
|
||||
Grid = grid;
|
||||
Modified = modified;
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
using Lidgren.Network;
|
||||
using SS14.Shared.Enums;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
using SS14.Shared.Map;
|
||||
|
||||
namespace SS14.Shared.Network.Messages
|
||||
{
|
||||
public class MsgMap : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public const MsgGroups Group = MsgGroups.Entity;
|
||||
public static readonly string NAME = nameof(MsgMap);
|
||||
public MsgMap(INetChannel channel) : base(NAME, Group) { }
|
||||
#endregion
|
||||
|
||||
public MapMessage MessageType { get; set; }
|
||||
public MapId MapIndex { get; set; }
|
||||
public GridId GridIndex { get; set; }
|
||||
|
||||
public Turf SingleTurf { get; set; }
|
||||
|
||||
public ChunkDef[] ChunkDefs { get; set; }
|
||||
|
||||
public ushort ChunkSize { get; set; }
|
||||
|
||||
public int MapGridsToSend { get; set; }
|
||||
|
||||
|
||||
public class Turf
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public uint Tile { get; set; }
|
||||
}
|
||||
|
||||
public class ChunkDef
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public uint[] Tiles { get; set; }
|
||||
}
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
MessageType = (MapMessage) buffer.ReadByte();
|
||||
switch (MessageType)
|
||||
{
|
||||
case MapMessage.TurfUpdate:
|
||||
MapIndex = new MapId(buffer.ReadInt32());
|
||||
GridIndex = new GridId(buffer.ReadInt32());
|
||||
SingleTurf = new Turf()
|
||||
{
|
||||
X = buffer.ReadInt32(),
|
||||
Y = buffer.ReadInt32(),
|
||||
Tile = buffer.ReadUInt32()
|
||||
};
|
||||
break;
|
||||
case MapMessage.SendTileMap:
|
||||
GridIndex = new GridId(buffer.ReadInt32());
|
||||
MapIndex = new MapId(buffer.ReadInt32());
|
||||
|
||||
// map chunks
|
||||
ChunkSize = buffer.ReadUInt16();
|
||||
var numChunks = buffer.ReadInt32();
|
||||
ChunkDefs = new ChunkDef[numChunks];
|
||||
|
||||
for (var i = 0; i < numChunks; i++)
|
||||
{
|
||||
var newChunk = new ChunkDef()
|
||||
{
|
||||
X = buffer.ReadInt32(),
|
||||
Y = buffer.ReadInt32()
|
||||
};
|
||||
|
||||
var chunkCount = ChunkSize * ChunkSize;
|
||||
var tiles = new uint[chunkCount];
|
||||
for (var j = 0; j < chunkCount; j++)
|
||||
{
|
||||
tiles[j] = buffer.ReadUInt32();
|
||||
}
|
||||
newChunk.Tiles = tiles;
|
||||
ChunkDefs[i] = newChunk;
|
||||
}
|
||||
break;
|
||||
case MapMessage.SendMapInfo:
|
||||
MapGridsToSend = buffer.ReadInt32();
|
||||
break;
|
||||
case MapMessage.CreateMap:
|
||||
case MapMessage.DeleteMap:
|
||||
MapIndex = new MapId(buffer.ReadInt32());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer)
|
||||
{
|
||||
buffer.Write((byte)MessageType);
|
||||
switch (MessageType)
|
||||
{
|
||||
case MapMessage.TurfUpdate:
|
||||
buffer.Write((int)MapIndex);
|
||||
buffer.Write((int)GridIndex);
|
||||
buffer.Write(SingleTurf.X);
|
||||
buffer.Write(SingleTurf.Y);
|
||||
buffer.Write(SingleTurf.Tile);
|
||||
break;
|
||||
case MapMessage.SendTileMap:
|
||||
buffer.Write((int)GridIndex);
|
||||
buffer.Write((int)MapIndex);
|
||||
|
||||
// Map chunks
|
||||
buffer.Write(ChunkSize);
|
||||
buffer.Write(ChunkDefs.Length);
|
||||
foreach (var chunk in ChunkDefs)
|
||||
{
|
||||
buffer.Write(chunk.X);
|
||||
buffer.Write(chunk.Y);
|
||||
|
||||
// ordered list
|
||||
foreach (var tile in chunk.Tiles)
|
||||
buffer.Write(tile);
|
||||
}
|
||||
|
||||
break;
|
||||
case MapMessage.SendMapInfo:
|
||||
buffer.Write(MapGridsToSend);
|
||||
break;
|
||||
case MapMessage.CreateMap:
|
||||
case MapMessage.DeleteMap:
|
||||
buffer.Write((int)MapIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Lidgren.Network;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
|
||||
namespace SS14.Shared.Network.Messages
|
||||
{
|
||||
public class MsgMapReq : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Entity;
|
||||
public static readonly string NAME = nameof(MsgMapReq);
|
||||
public MsgMapReq(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,7 @@
|
||||
<Compile Include="GameObjects\EntityUid.cs" />
|
||||
<Compile Include="GameObjects\IncomingEntityMessage.cs" />
|
||||
<Compile Include="GameObjects\NetComponent.cs" />
|
||||
<Compile Include="GameStates\GameStateMapData.cs" />
|
||||
<Compile Include="Input\InputCmdContext.cs" />
|
||||
<Compile Include="Input\CommandBindMapping.cs" />
|
||||
<Compile Include="Input\InputCmdMessage.cs" />
|
||||
@@ -210,8 +211,6 @@
|
||||
<Compile Include="Network\Messages\MsgConCmdAck.cs" />
|
||||
<Compile Include="Network\Messages\MsgConCmdReg.cs" />
|
||||
<Compile Include="Network\Messages\MsgEntity.cs" />
|
||||
<Compile Include="Network\Messages\MsgMap.cs" />
|
||||
<Compile Include="Network\Messages\MsgMapReq.cs" />
|
||||
<Compile Include="Network\Messages\MsgPlacement.cs" />
|
||||
<Compile Include="Network\Messages\MsgPlayerList.cs" />
|
||||
<Compile Include="Network\Messages\MsgPlayerListReq.cs" />
|
||||
|
||||
Reference in New Issue
Block a user