Entities and Components
In SampSharp, an entity is a unique object in the game world, such as a player, vehicle, or object. Components are data containers that are attached to entities to define their properties or state. Components should not contain logic or behavior; instead, logic is implemented in systems that operate on entities with specific components. An entity only exists in the ECS world when it has at least one component attached.
Entities and components are the building blocks of the ECS architecture:
- Entities are identified by an
EntityId(EntityId) - Components are subclasses of Component
- Systems operate on entities by handling events; the dispatcher resolves the relevant components from the involved entities and passes them to the handler
Creating Entities
Entities are created implicitly when you add the first component to a new EntityId. You can generate a new entity identifier using:
var entityId = EntityId.NewEntityId();
To add a component and thus create the entity, use the IEntityManager interface. For example, with a custom component:
var myComponent = entityManager.AddComponent<MyFirstComponent>(entityId);
- The entity is created when the first component is added.
- You can add more components to the same entity using the same
entityId.
Note
Built-in components such as Player, Pickup, and Vehicle are managed by SampSharp and should not be manually added to an entity.
Creating Custom Components
To create a custom component, simply inherit from Component:
public class BankAccount : Component
{
public decimal Balance { get; set; }
public void Deposit(decimal amount)
{
Balance += amount;
}
public void Withdraw(decimal amount)
{
if (amount <= Balance)
Balance -= amount;
}
}
You can then add your custom component to an entity:
var accountComponent = player.AddComponent<BankAccount>();
accountComponent.Deposit(100);
Entity Hierarchy and Nesting
Entities can be organized in a parent-child hierarchy. When you add a component to an entity, you can specify a parent entity:
var childId = EntityId.NewEntityId();
entityManager.AddComponent<BankAccount>(childId, parentId);
- Destroying a parent entity will recursively destroy all its children and their components.
Example: round-scoped entities
A common use case for entity hierarchies is lifetime grouping: tie a set of transient entities to a single parent, then destroy the parent to clean them all up at once. All IWorldService.Create* methods accept an optional parent parameter for exactly this.
For example, a deathmatch round that spawns weapon pickups and a control zone can parent everything to a Round entity. Destroying that entity at the end of the round removes the pickups and gang zone in one call:
graph TD
R(Round Entity) --> RC[Round]
R --> P1(Pickup Entity)
P1 --> P1C[Pickup]
R --> P2(Pickup Entity)
P2 --> P2C[Pickup]
R --> GZ(GangZone Entity)
GZ --> GZC[GangZone]
Destroying Entities and Components
Destroying an entity removes all its components and recursively destroys all child entities. You can destroy an entity using:
entityManager.Destroy(entityId);
Or from a component:
component.DestroyEntity();
You can also destroy a single component:
component.Destroy();
Component Liveness
Once a component is destroyed — directly via Destroy(), indirectly via DestroyEntity(), or when its underlying game object goes away (for example, when a player disconnects) — the C# object remains in memory until the garbage collector reclaims it, but calling methods or accessing properties that touch the underlying native handle will throw ObjectDisposedException.
If your code holds onto a component across a boundary where it could have been destroyed in the meantime — typically across await, a timer callback, or a captured closure — check liveness before using it:
[Event]
public async Task OnPlayerConnect(Player player)
{
await SomeLongRunningWorkAsync();
if (player)
player.SendClientMessage("Welcome back to your seat.");
}
Every component is implicitly truthy when alive and falsy when destroyed or null, so if (component) is enough. The underlying flag is also exposed as component.IsComponentAlive if you need to read it explicitly. Inside OnDestroyComponent, the related IsDestroying property is true — useful for distinguishing the destruction pass from normal operation.
Working with Components from a Component
From any Component, you can:
- Get another component on the same entity:
component.GetComponent<OtherComponent>() - Add a component to the same entity:
component.AddComponent<OtherComponent>() - Get components in children or parent entities:
component.GetComponentInChildren<T>(),component.GetComponentInParent<T>(), etc.
See Component for all available methods.
Provided Components in SampSharp
Below is a list of the most important components provided by SampSharp:
| Component | Description |
|---|---|
| Actor | Represents an actor (non-movable NPC) in the world |
| Class | Represents a player class |
| GangZone | Represents a gang zone |
| GlobalObject | Represents an object |
| Menu | Represents a menu |
| Npc | Represents a non-player character |
| Pickup | Represents a pickup item |
| Player | Represents a player |
| PlayerGangZone | Player-specific gang zone |
| PlayerObject | Player-specific object |
| PlayerPickup | Player-specific pickup |
| PlayerTextDraw | Player-specific textdraw |
| PlayerTextLabel | Player-specific text label |
| TextDraw | Represents a textdraw |
| TextLabel | Represents a text label |
| Vehicle | Represents a vehicle |