using System; using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Robust.Shared.GameObjects; public abstract partial class MetaDataSystem : EntitySystem { [Dependency] private IGameTiming _timing = default!; private EntityPausedEvent _pausedEvent; private EntityQuery _metaQuery; public override void Initialize() { base.Initialize(); _metaQuery = GetEntityQuery(); SubscribeLocalEvent(OnMetaDataHandle); SubscribeLocalEvent(OnMetaDataGetState); } private void OnMetaDataGetState(EntityUid uid, MetaDataComponent component, ref ComponentGetState args) { args.State = new MetaDataComponentState(component._entityName, component._entityDescription, component._entityPrototype?.ID, component.PauseTime); } private void OnMetaDataHandle(EntityUid uid, MetaDataComponent component, ref ComponentHandleState args) { if (args.Current is not MetaDataComponentState state) return; component._entityName = state.Name; component._entityDescription = state.Description; if(state.PrototypeId != null && state.PrototypeId != component._entityPrototype?.ID) component._entityPrototype = ProtoMan.Index(state.PrototypeId); component.PauseTime = state.PauseTime; } public void SetEntityName(EntityUid uid, string value, MetaDataComponent? metadata = null, bool raiseEvents = true) { if (!_metaQuery.Resolve(uid, ref metadata) || value.Equals(metadata.EntityName)) return; var oldName = metadata.EntityName; metadata._entityName = value; if (raiseEvents) { var ev = new EntityRenamedEvent(uid, oldName, value); RaiseLocalEvent(uid, ref ev, true); } Dirty(uid, metadata, metadata); } public void SetEntityDescription(EntityUid uid, string value, MetaDataComponent? metadata = null) { if (!_metaQuery.Resolve(uid, ref metadata) || value.Equals(metadata.EntityDescription)) return; metadata._entityDescription = value; Dirty(uid, metadata, metadata); } internal void SetEntityPrototype(EntityUid uid, EntityPrototype? value, MetaDataComponent? metadata = null) { if (!_metaQuery.Resolve(uid, ref metadata) || value?.Equals(metadata._entityPrototype) == true) return; // The ID string should never change after an entity has been created. // Otherwise this breaks networking in multiplayer games. DebugTools.Assert(value?.ID == metadata._entityPrototype?.ID); metadata._entityPrototype = value; } public bool EntityPaused(EntityUid uid, MetaDataComponent? metadata = null) { if (!_metaQuery.Resolve(uid, ref metadata)) return true; return metadata.EntityPaused; } public void SetEntityPaused(EntityUid uid, bool value, MetaDataComponent? metadata = null) { if (!_metaQuery.Resolve(uid, ref metadata)) return; if (metadata.EntityPaused == value) return; if (value) { DebugTools.Assert(metadata.PauseTime == null); metadata.PauseTime = _timing.CurTime; RaiseLocalEvent(uid, ref _pausedEvent); } else { DebugTools.Assert(metadata.PauseTime != null); var ev = new EntityUnpausedEvent(_timing.CurTime - metadata.PauseTime!.Value); metadata.PauseTime = null; RaiseLocalEvent(uid, ref ev); } Dirty(uid, metadata, metadata); } /// /// Gets how long this entity has been paused. /// public TimeSpan GetPauseTime(EntityUid uid, MetaDataComponent? metadata = null) { if (!_metaQuery.Resolve(uid, ref metadata)) return TimeSpan.Zero; return (_timing.CurTime - metadata.PauseTime) ?? TimeSpan.Zero; } /// /// Offsets the specified time by how long the entity has been paused. /// public void PauseOffset(EntityUid uid, ref TimeSpan time, MetaDataComponent? metadata = null) { var paused = GetPauseTime(uid, metadata); time += paused; } public void SetFlag(Entity entity, MetaDataFlags flags, bool enabled) { if (!_metaQuery.Resolve(entity, ref entity.Comp)) return; if (enabled) entity.Comp.Flags |= flags; else RemoveFlag(entity, flags, entity.Comp); } public void AddFlag(EntityUid uid, MetaDataFlags flags, MetaDataComponent? comp = null) => SetFlag((uid, comp), flags, true); /// /// Attempts to remove the specific flag from metadata. /// Other systems can choose not to allow the removal if it's still relevant. /// public void RemoveFlag(EntityUid uid, MetaDataFlags flags, MetaDataComponent? component = null) { if (!_metaQuery.Resolve(uid, ref component)) return; var toRemove = component.Flags & flags; if (toRemove == 0x0) return; // TODO PERF // does this need to be a broadcast event? var ev = new MetaFlagRemoveAttemptEvent(toRemove); RaiseLocalEvent(uid, ref ev, true); component.Flags &= ~ev.ToRemove; } } /// /// Raised if is trying to remove a particular flag. /// [ByRefEvent] public struct MetaFlagRemoveAttemptEvent { public MetaDataFlags ToRemove; public MetaFlagRemoveAttemptEvent(MetaDataFlags toRemove) { ToRemove = toRemove; } }