Events
Events are how SampSharp systems respond to game world changes. Built-in events like player connect/disconnect, entering vehicles, and damage trigger event handlers in your systems.
Handling Events with the [Event] Attribute
To handle an event, add the [Event] attribute to a method in your Systems. The method name determines which event is handled, unless you specify a custom name.
Multiple systems can handle the same event—each registered handler will be invoked when the event fires. If the event has a bool return value, handlers work together to produce a final result (see Event Return Values below).
using SampSharp.Entities;
using SampSharp.Entities.SAMP;
public class PlayerSystem : ISystem
{
[Event]
public void OnPlayerConnect(Player player)
{
player.SendClientMessage("Welcome to the server!");
}
[Event]
public void OnPlayerDisconnect(Player player)
{
Console.WriteLine($"{player.Name} disconnected.");
}
}
Custom Event Names
If your method name doesn't match the event name, use the Name parameter:
[Event(Name = "OnPlayerRequestClass")]
public void HandleClassSelection(Player player, Class klass)
{
player.SendClientMessage($"You selected class: {klass.Id}");
}
Dependency Injection in Event Handlers
Event handlers can accept services via dependency injection alongside event parameters:
[Event]
public void OnPlayerText(Player player, string message, IWorldService worldService)
{
// player and message come from the event
// worldService is injected
Console.WriteLine($"{player.Name}: {message}");
}
Built-in Game Events
SampSharp dispatches a wide range of built-in events covering player connections, spawning, vehicles, objects, text draws, NPCs, weapons, and more. For the full categorized list with signatures and return types, see Built-in Events Reference.
Event Return Values
Some events return bool to control behavior. The return value determines what happens next:
true— Event is considered "handled" or "accepted". The server continues with the action (e.g., allows a spawn, propagates a trailer update).false— Event is "not handled" or "rejected". The server may skip the action or skip default behavior (e.g., prevents a spawn, blocks a command).
If a handler doesn't explicitly return a value (returns void), the event uses a sensible default:
- Events that represent "can this happen?" queries default to
true(allow it). - Events that represent "did anyone handle this?" queries default to
false(not handled).
Examples
OnPlayerText— Returnfalseto suppress the message (it won't propagate to chat). Returntrueto allow the message to propagate normally.OnPlayerCommandText— Returntruemeans you handled the command. Returnfalsemeans command was not handled.OnPlayerRequestSpawn— Returnfalseto prevent the spawn. Returntrueto allow it.OnTrailerUpdate— Returntrueto propagate the update. Returnfalseto skip it.
Multiple Handlers
When multiple systems register handlers for the same event, all handlers are always called. For bool-returning events:
- If a handler returns the default value, it's ignored and the next handler is called.
- If a handler returns a non-default value, it becomes the current result.
- The last non-default return value is the final result.
- If all handlers return the default value (or there are no handlers), the default is used.
Dispatching Custom Events
For advanced scenarios, you can dispatch custom events using IEventDispatcher. When dispatching, pass entities as EntityId values, not as components.
The event dispatcher automatically converts entity IDs to the component types expected by event handlers. If an entity doesn't have the required component, the handler is not called.
How Event Handlers Resolve Parameters
When an event handler is invoked, SampSharp determines how to resolve each parameter based on its type:
Event Arguments (passed from event dispatcher):
- Value types (int, bool, float, etc.)
- Arrays
- Strings
- Components (e.g.,
Player, custom components) - Classes marked with the
[EventParameter]attribute
Services (resolved via dependency injection):
- Everything else is treated as a service and resolved from the DI container.
This allows handlers to combine event data with injected services flexibly. In the example above, the OnPlayerBan handler received a component (Player), custom event parameter (PlayerBanRequest), and an injected service (ILogger)—all resolved automatically.
using SampSharp.Entities;
using SampSharp.Entities.SAMP;
// Define custom event data types with [EventParameter]
[EventParameter]
public class PlayerBanRequest
{
public string Reason { get; set; }
public DateTime BanTime { get; set; }
}
public class BanSystem : ISystem
{
private readonly IEventDispatcher _dispatcher;
public BanSystem(IEventDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public void BanPlayer(EntityId playerEntity, string reason)
{
var banRequest = new PlayerBanRequest
{
Reason = reason,
BanTime = DateTime.UtcNow
};
// Dispatch custom event with entity and custom data
_dispatcher.Invoke("OnPlayerBan", playerEntity, banRequest);
}
}
// Handler in another system:
public class LoggingSystem : ISystem
{
[Event(Name = "OnPlayerBan")]
public void OnPlayerBan(
Player player, // Component from entity
PlayerBanRequest request, // Custom event parameter
ILogger<LoggingSystem> log) // Injected service
{
log.LogWarning($"Player {player.Name} banned at {request.BanTime}: {request.Reason}");
}
}
Event Middleware
Middleware allows you to intercept and modify event handling behavior. Middleware works like a pipeline where each middleware component can execute code before and after event handlers run, so you can inspect events, modify their context, or apply logic uniformly across many events.
You can use middleware to log all events, validate data before handlers run, check permissions, or apply other centralized logic across events. Configure middleware in your Startup class using the IEcsBuilder:
public class MyMiddleware
{
private readonly EventDelegate _next;
public MyMiddleware(EventDelegate next)
{
_next = next;
}
public object? Invoke(EventContext context)
{
// Do something before the event handlers
Console.WriteLine($"Event: {context.EventName}");
var result = _next(context);
// Do something after the event handlers
return result;
}
}
// In your Startup class:
public class Startup : IEcsStartup
{
public void Configure(IEcsBuilder builder)
{
builder.UseMiddleware<MyMiddleware>("OnPlayerText");
}
// ... other methods
}