using System;
using System.Runtime.CompilerServices;
using Arch.Core;
using Arch.Core.Extensions.Dangerous;
using JetBrains.Annotations;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects
{
///
/// This type contains a local identification number of an entity.
/// This can be used by the EntityManager to access an entity
///
[CopyByRef]
public readonly struct EntityUid : IEquatable, IComparable, ISpanFormattable
{
public readonly int Id;
public readonly int Version;
///
/// An Invalid entity UID you can compare against.
///
public static readonly EntityUid Invalid = new(-1 + ArchUidOffset, -1 + ArchVersionOffset);
///
/// The first entity UID the entityManager should use when the manager is initialized.
///
public static readonly EntityUid FirstUid = new(0 + ArchUidOffset, 1 + ArchVersionOffset);
internal const int ArchUidOffset = 1;
internal const int ArchVersionOffset = 1;
public EntityUid()
{
Id = Invalid.Id;
Version = Invalid.Version;
}
internal EntityUid(EntityReference reference)
{
Id = reference.Entity.Id + ArchUidOffset;
Version = reference.Version + ArchVersionOffset;
}
///
/// Creates an instance of this structure, with the given network ID.
///
public EntityUid(int id, int version)
{
Id = id;
Version = version;
}
public bool Valid => IsValid();
///
/// Creates an entity UID by parsing a string number.
///
public static EntityUid Parse(ReadOnlySpan uid, ReadOnlySpan version)
{
return new EntityUid(int.Parse(uid), int.Parse(version));
}
public static bool TryParse(ReadOnlySpan uid, ReadOnlySpan version, out EntityUid entityUid)
{
try
{
entityUid = Parse(uid, version);
return true;
}
catch (FormatException)
{
entityUid = Invalid;
return false;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EntityUid FromArch(in World world, in Entity entity)
{
return new EntityUid(entity.Id + ArchUidOffset, world.Reference(entity).Version + ArchVersionOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetArchId() => Id - ArchUidOffset;
///
/// Checks if the ID value is valid. Does not check if it identifies
/// a valid Entity.
///
[Pure]
public bool IsValid()
{
return Id > Invalid.Id;
}
///
public bool Equals(EntityUid other)
{
return Id == other.Id && Version == other.Version;
}
///
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is EntityUid id && Equals(id);
}
///
public override int GetHashCode()
{
return Id;
}
///
/// Check for equality by value between two objects.
///
public static bool operator ==(EntityUid a, EntityUid b)
{
return a.Id == b.Id && a.Version == b.Version;
}
///
/// Check for inequality by value between two objects.
///
public static bool operator !=(EntityUid a, EntityUid b)
{
return !(a == b);
}
///
/// Explicit conversion of EntityId to int. This should only be used in special
/// cases like serialization. Do NOT use this in content.
///
public static explicit operator int(EntityUid self)
{
return self.Id;
}
public static implicit operator Entity(EntityUid self)
{
return DangerousEntityExtensions.CreateEntityStruct(self.Id - ArchUidOffset, 0);
}
public static implicit operator EntityReference(EntityUid other)
{
return DangerousEntityExtensions.CreateEntityReferenceStruct(other.Id - ArchUidOffset, other.Version - ArchVersionOffset, 0);
}
public static implicit operator EntityUid(EntityReference other)
{
return new EntityUid(other);
}
///
public override string ToString()
{
return Id.ToString();
}
public string ToString(string? format, IFormatProvider? formatProvider)
{
return ToString();
}
public bool TryFormat(
Span destination,
out int charsWritten,
ReadOnlySpan format,
IFormatProvider? provider)
{
return Id.TryFormat(destination, out charsWritten);
}
///
public int CompareTo(EntityUid other)
{
return Id.CompareTo(other.Id);
}
#region ViewVariables
[ViewVariables]
private string Representation => IoCManager.Resolve().ToPrettyString(this);
[ViewVariables(VVAccess.ReadWrite)]
private string Name
{
get => MetaData?.EntityName ?? string.Empty;
set
{
if (MetaData is {} metaData)
IoCManager.Resolve().System().SetEntityName(this, value, metaData);
}
}
[ViewVariables(VVAccess.ReadWrite)]
private string Description
{
get => MetaData?.EntityDescription ?? string.Empty;
set
{
if (MetaData is { } metaData)
{
var entManager = IoCManager.Resolve();
entManager.System().SetEntityDescription(this, value, metaData);
}
}
}
[ViewVariables]
private EntityPrototype? Prototype => MetaData?.EntityPrototype;
[ViewVariables]
private GameTick LastModifiedTick => MetaData?.EntityLastModifiedTick ?? GameTick.Zero;
[ViewVariables]
private bool Paused => MetaData?.EntityPaused ?? false;
[ViewVariables]
private EntityLifeStage LifeStage => MetaData?.EntityLifeStage ?? EntityLifeStage.Deleted;
[ViewVariables]
private MetaDataComponent? MetaData =>
IoCManager.Resolve().GetComponentOrNull(this);
[ViewVariables]
private TransformComponent? Transform =>
IoCManager.Resolve().GetComponentOrNull(this);
// This might seem useless, but it allows you to retrieve remote entities that don't exist on the client.
[ViewVariables]
private EntityUid _uid => this;
[ViewVariables]
private NetEntity _netId => IoCManager.Resolve().GetNetEntity(this);
#endregion
}
}