mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 18:31:17 +03:00
* Fix decal prediction Tried to do it the clever way in after state handler but didn't reset properly so we do it the stupid way. * a * also these fixes
152 lines
5.7 KiB
C#
152 lines
5.7 KiB
C#
using System.Numerics;
|
|
using Robust.Shared.GameStates;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Serialization;
|
|
|
|
namespace Content.Shared.Decals
|
|
{
|
|
public abstract partial class SharedDecalSystem : EntitySystem
|
|
{
|
|
[Dependency] protected ChunkEntitySystem ChunkEntities = default!;
|
|
[Dependency] protected EntityQuery<DecalChunkComponent> DecalChunkQuery = default!;
|
|
|
|
protected bool PvsEnabled;
|
|
|
|
// Legacy DecalGridComponent data was serialized in 32x32 chunks. Loading code must treat those keys as old
|
|
// storage buckets and re-chunk decals by coordinates before migrating them to chunk entities.
|
|
public const int LegacyChunkSize = 32;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<DecalGridComponent, ComponentGetState>(OnGetState);
|
|
SubscribeLocalEvent<DecalChunkComponent, ComponentStartup>(OnChunkStartup);
|
|
SubscribeAllEvent<RequestDecalPlacementEvent>(OnDecalPlacementRequest);
|
|
SubscribeAllEvent<RequestDecalRemovalEvent>(OnDecalRemovalRequest);
|
|
}
|
|
|
|
protected abstract void OnDecalPlacementRequest(RequestDecalPlacementEvent ev, EntitySessionEventArgs eventArgs);
|
|
|
|
protected abstract void OnDecalRemovalRequest(RequestDecalRemovalEvent ev, EntitySessionEventArgs eventArgs);
|
|
|
|
private void OnChunkStartup(Entity<DecalChunkComponent> ent, ref ComponentStartup args)
|
|
{
|
|
RebuildFreeDecalIds(ent.Comp);
|
|
}
|
|
|
|
private void OnGetState(EntityUid uid, DecalGridComponent component, ref ComponentGetState args)
|
|
{
|
|
if (PvsEnabled && !args.ReplayState)
|
|
return;
|
|
|
|
// Should this be a full component state or a delta-state?
|
|
if (args.FromTick <= component.CreationTick || args.FromTick <= component.ForceTick)
|
|
{
|
|
args.State = new DecalGridState(component.ChunkCollection.ChunkCollection);
|
|
return;
|
|
}
|
|
|
|
var data = new Dictionary<Vector2i, DecalGridComponent.DecalChunk>();
|
|
foreach (var (index, chunk) in component.ChunkCollection.ChunkCollection)
|
|
{
|
|
if (chunk.LastModified >= args.FromTick)
|
|
data[index] = chunk;
|
|
}
|
|
|
|
args.State = new DecalGridDeltaState(data, new(component.ChunkCollection.ChunkCollection.Keys));
|
|
}
|
|
|
|
public HashSet<(DecalIndex Index, Decal Decal)> GetDecalsInRange(EntityUid gridId, Vector2 position, float distance = 0.75f, Func<Decal, bool>? validDelegate = null)
|
|
{
|
|
var bounds = new Box2(position - new Vector2(distance + 1f), position + new Vector2(distance + 1f));
|
|
var decalIds = GetDecalsIntersecting(gridId, bounds);
|
|
|
|
decalIds.RemoveWhere(set =>
|
|
(position - set.Decal.Coordinates - new Vector2(0.5f, 0.5f)).Length() > distance ||
|
|
validDelegate != null && !validDelegate(set.Decal));
|
|
|
|
return decalIds;
|
|
}
|
|
|
|
public HashSet<(DecalIndex Index, Decal Decal)> GetDecalsIntersecting(EntityUid gridUid, Box2 bounds)
|
|
{
|
|
var decalIds = new HashSet<(DecalIndex, Decal)>();
|
|
|
|
foreach (var chunk in ChunkEntities.GetChunksIntersecting(gridUid, bounds, DecalChunkQuery))
|
|
{
|
|
foreach (var (id, decal) in chunk.Comp2.Decals)
|
|
{
|
|
if (!bounds.Contains(decal.Coordinates))
|
|
continue;
|
|
|
|
decalIds.Add((new DecalIndex(chunk.Comp1.Chunk, id), decal));
|
|
}
|
|
}
|
|
|
|
return decalIds;
|
|
}
|
|
|
|
public virtual bool RemoveDecal(EntityUid gridId, DecalIndex decal)
|
|
{
|
|
// NOOP on client atm.
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a decal.
|
|
/// </summary>
|
|
public abstract bool TryAddDecal(Decal decal, EntityCoordinates coordinates, out DecalIndex decalId);
|
|
|
|
private static void RebuildFreeDecalIds(DecalChunkComponent component)
|
|
{
|
|
component.FreeDecalIds.Clear();
|
|
|
|
// Saves only carry decal data and the highest allocated server id. The free list is a runtime cache.
|
|
foreach (var id in component.Decals.Keys)
|
|
{
|
|
if (id <= DecalChunkComponent.MaxServerDecalId)
|
|
component.MaxDecalId = Math.Max(component.MaxDecalId, id);
|
|
}
|
|
|
|
component.FreeDecalIds.EnsureCapacity(component.MaxDecalId + 1 - component.Decals.Count);
|
|
|
|
for (var id = 0; id <= component.MaxDecalId; id++)
|
|
{
|
|
var decalId = (ushort) id;
|
|
if (!component.Decals.ContainsKey(decalId))
|
|
component.FreeDecalIds.Add(decalId);
|
|
}
|
|
|
|
// Allocation pops from the end, so keep the lowest free id there for stable decal ids.
|
|
component.FreeDecalIds.Sort((x, y) => y.CompareTo(x));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sent by clients to request that a decal is placed on the server.
|
|
/// </summary>
|
|
[Serializable, NetSerializable]
|
|
public sealed class RequestDecalPlacementEvent : EntityEventArgs
|
|
{
|
|
public Decal Decal;
|
|
public NetCoordinates Coordinates;
|
|
|
|
public RequestDecalPlacementEvent(Decal decal, NetCoordinates coordinates)
|
|
{
|
|
Decal = decal;
|
|
Coordinates = coordinates;
|
|
}
|
|
}
|
|
|
|
[Serializable, NetSerializable]
|
|
public sealed class RequestDecalRemovalEvent : EntityEventArgs
|
|
{
|
|
public NetCoordinates Coordinates;
|
|
|
|
public RequestDecalRemovalEvent(NetCoordinates coordinates)
|
|
{
|
|
Coordinates = coordinates;
|
|
}
|
|
}
|
|
}
|