Documentation
¶
Overview ¶
Package pecs provides a Player Entity Component System for Dragonfly servers.
PECS is an architectural layer built on top of Dragonfly that provides:
- Session abstraction for persistent player identity
- Component-based data storage per player
- Declarative dependency injection via struct tags
- Type-safe relations between sessions
- Transaction-safe handlers, loops, and tasks
- Multi-instance support for running multiple servers in one process
Quick Start ¶
Initialize PECS in your server setup:
bundle := pecs.NewBundle("MyGame").
Handler(&MyHandler{}).
Loop(&MyLoop{}, time.Second, pecs.Default)
mngr := pecs.NewBuilder().
Bundle(bundle).
Init()
for p := range srv.Accept() {
sess, err := mngr.NewSession(p)
if err != nil {
p.Disconnect("failed to initialize session")
continue
}
pecs.Add(sess, &Health{Current: 100, Max: 100})
p.Handle(pecs.NewHandler(sess, p))
}
Components ¶
Components are plain Go structs attached to sessions:
type Health struct {
Current int
Max int
}
pecs.Add(sess, &Health{100, 100})
health := pecs.Get[Health](sess)
pecs.Remove[Health](sess)
Systems ¶
Systems declare dependencies via struct tags:
type MyHandler struct {
Session *pecs.Session
Manager *pecs.Manager // Optional: for broadcasting, lookups
Health *Health // Required
Shield *Shield `pecs:"opt"` // Optional
Config *Config `pecs:"res"` // Resource
_ pecs.Without[Spectator] // Skip if Spectator exists
}
func (h *MyHandler) HandleHurt(ev *pecs.EventHurt) {
// Handle hurt event using ev.Damage, ev.Source, etc.
}
Tag Reference ¶
(none) Required read-only component pecs:"mut" Required mutable component pecs:"opt" Optional (nil if missing) pecs:"opt,mut" Optional mutable pecs:"rel" Relation traversal pecs:"res" Resource pecs:"res,mut" Mutable resource pecs:"peer" Peer[T] resolution (remote player data) pecs:"shared" Shared[T] resolution (shared entity data)
Index ¶
- Constants
- func Add[T any](s *Session, component *T)
- func AddFor[T any](s *Session, component *T, duration time.Duration)
- func AddUntil[T any](s *Session, component *T, expireAt time.Time)
- func Expired[T any](s *Session) bool
- func ExpiresAt[T any](s *Session) time.Time
- func ExpiresIn[T any](s *Session) time.Duration
- func Get[T any](s *Session) *T
- func GetOrAdd[T any](s *Session, defaultVal *T) *T
- func Has[T any](s *Session) bool
- func ManagerResource[T any](m *Manager) *T
- func NewHandler(s *Session, p *player.Player) player.Handler
- func Remove[T any](s *Session)
- func Resource[T any](s *Session) *T
- type AccessMeta
- type ActorConfig
- type Attachable
- type Bitmask
- func (m Bitmask) And(other Bitmask) Bitmask
- func (m Bitmask) AndNot(other Bitmask) Bitmask
- func (m *Bitmask) Clear(id ComponentID)
- func (m Bitmask) Clone() Bitmask
- func (m *Bitmask) ContainsAll(other Bitmask) bool
- func (m *Bitmask) ContainsAny(other Bitmask) bool
- func (m *Bitmask) Count() int
- func (m *Bitmask) Equals(other Bitmask) bool
- func (m *Bitmask) Has(id ComponentID) bool
- func (m *Bitmask) IsDisjoint(other Bitmask) bool
- func (m *Bitmask) IsZero() bool
- func (m Bitmask) Or(other Bitmask) Bitmask
- func (m *Bitmask) Set(id ComponentID)
- type Builder
- func (b *Builder) Bundle(callback func(*Manager) *Bundle) *Builder
- func (b *Builder) Init(ws ...*world.World) *Manager
- func (b *Builder) PeerProvider(p PeerProvider, opts ...ProviderOption) *Builder
- func (b *Builder) Resource(res any) *Builder
- func (b *Builder) SharedProvider(p SharedProvider, opts ...ProviderOption) *Builder
- type Bundle
- func (b *Bundle) Build() func(*Manager) *Bundle
- func (b *Bundle) Command(command cmd.Command) *Bundle
- func (b *Bundle) Handler(h any) *Bundle
- func (b *Bundle) Loop(sys Runnable, interval time.Duration, stage Stage) *Bundle
- func (b *Bundle) Name() string
- func (b *Bundle) PeerProvider(p PeerProvider, opts ...ProviderOption) *Bundle
- func (b *Bundle) PostInit(hook func(*Manager)) *Bundle
- func (b *Bundle) Resource(res any) *Bundle
- func (b *Bundle) SharedProvider(p SharedProvider, opts ...ProviderOption) *Bundle
- func (b *Bundle) Task(sys Runnable, stage Stage) *Bundle
- type ComponentAttachEvent
- type ComponentDetachEvent
- type ComponentID
- type Detachable
- type EntityMarker
- type EventAttackEntity
- type EventBlockBreak
- type EventBlockPick
- type EventBlockPlace
- type EventChangeWorld
- type EventChat
- type EventCommandExecution
- type EventDeath
- type EventDiagnostics
- type EventExperienceGain
- type EventFireExtinguish
- type EventFoodLoss
- type EventHeal
- type EventHeldSlotChange
- type EventHurt
- type EventItemConsume
- type EventItemDamage
- type EventItemDrop
- type EventItemPickup
- type EventItemRelease
- type EventItemUse
- type EventItemUseOnBlock
- type EventItemUseOnEntity
- type EventJoin
- type EventJump
- type EventLecternPageTurn
- type EventMove
- type EventPunchAir
- type EventQuit
- type EventRespawn
- type EventSignEdit
- type EventSkinChange
- type EventSleep
- type EventStartBreak
- type EventTeleport
- type EventToggleSneak
- type EventToggleSprint
- type EventTransfer
- type FakeMarker
- type FieldKind
- type FieldMeta
- type Manager
- func (m *Manager) AllSessions() []*Session
- func (m *Manager) AllSessionsInWorld(w *world.World) []*Session
- func (m *Manager) Emit(event any)
- func (m *Manager) EmitExcept(event any, exclude ...*Session)
- func (m *Manager) EmitGlobal(event any)
- func (m *Manager) GetSession(p *player.Player) *Session
- func (m *Manager) GetSessionByHandle(h *world.EntityHandle) *Session
- func (m *Manager) GetSessionByID(id string) *Session
- func (m *Manager) GetSessionByName(name string) *Session
- func (m *Manager) GetSessionByUUID(id uuid.UUID) *Session
- func (m *Manager) MoveSession(s *Session, from, to *world.World)
- func (m *Manager) NewSession(p *player.Player) (*Session, error)
- func (m *Manager) RegisterPeerProvider(p PeerProvider, opts ...ProviderOption)
- func (m *Manager) RegisterSharedProvider(p SharedProvider, opts ...ProviderOption)
- func (m *Manager) ResolvePeer(playerID string, componentType reflect.Type) unsafe.Pointer
- func (m *Manager) ResolvePeers(playerIDs []string, componentType reflect.Type) []unsafe.Pointer
- func (m *Manager) ResolveShared(entityID string, dataType reflect.Type) unsafe.Pointer
- func (m *Manager) ResolveSharedMany(entityIDs []string, dataType reflect.Type) []unsafe.Pointer
- func (m *Manager) SessionCount() int
- func (m *Manager) Shutdown()
- func (m *Manager) SpawnEntity(tx *world.Tx, cfg ActorConfig) (*player.Player, *Session)
- func (m *Manager) SpawnFake(tx *world.Tx, cfg ActorConfig, fakeID string) (*player.Player, *Session)
- func (m *Manager) Start()
- func (m *Manager) TickNumber() uint64
- type Peer
- type PeerProvider
- type PeerSet
- func (ps *PeerSet[T]) Add(playerID string)
- func (ps *PeerSet[T]) Clear()
- func (ps *PeerSet[T]) IDs() []string
- func (ps *PeerSet[T]) Len() int
- func (ps *PeerSet[T]) Remove(playerID string)
- func (ps *PeerSet[T]) Resolve(m *Manager) []*T
- func (ps *PeerSet[T]) Set(playerIDs []string)
- func (ps *PeerSet[T]) TargetType() reflect.Type
- type PhantomTypeInfo
- type PlayerUpdate
- type Provider
- type ProviderOption
- type ProviderOptions
- type Relation
- type RelationSet
- func (rs *RelationSet[T]) Add(target *Session)
- func (rs *RelationSet[T]) All() []*Session
- func (rs *RelationSet[T]) Clear()
- func (rs *RelationSet[T]) Has(target *Session) bool
- func (rs *RelationSet[T]) Len() int
- func (rs *RelationSet[T]) Remove(target *Session)
- func (rs *RelationSet[T]) Resolve() []Resolved[T]
- func (rs *RelationSet[T]) TargetType() reflect.Type
- type RepeatingTaskHandle
- type Resolved
- type Runnable
- type Scheduler
- type Session
- func (s *Session) Closed() bool
- func (s *Session) Emit(event any)
- func (s *Session) Exec(fn func(tx *world.Tx, p *player.Player)) bool
- func (s *Session) Handle() *world.EntityHandle
- func (s *Session) ID() string
- func (s *Session) IsActor() bool
- func (s *Session) IsEntity() bool
- func (s *Session) IsFake() bool
- func (s *Session) Manager() *Manager
- func (s *Session) Mask() Bitmask
- func (s *Session) Name() string
- func (s *Session) Player(tx *world.Tx) (*player.Player, bool)
- func (s *Session) String() string
- func (s *Session) UUID() uuid.UUID
- func (s *Session) World() *world.World
- func (s *Session) XUID() string
- type SessionHandler
- func (h *SessionHandler) HandleAttackEntity(ctx *player.Context, e world.Entity, force, height *float64, critical *bool)
- func (h *SessionHandler) HandleBlockBreak(ctx *player.Context, pos cube.Pos, drops *[]item.Stack, xp *int)
- func (h *SessionHandler) HandleBlockPick(ctx *player.Context, pos cube.Pos, b world.Block)
- func (h *SessionHandler) HandleBlockPlace(ctx *player.Context, pos cube.Pos, b world.Block)
- func (h *SessionHandler) HandleChangeWorld(p *player.Player, before, after *world.World)
- func (h *SessionHandler) HandleChat(ctx *player.Context, message *string)
- func (h *SessionHandler) HandleCommandExecution(ctx *player.Context, command cmd.Command, args []string)
- func (h *SessionHandler) HandleDeath(p *player.Player, src world.DamageSource, keepInv *bool)
- func (h *SessionHandler) HandleDiagnostics(p *player.Player, d session.Diagnostics)
- func (h *SessionHandler) HandleExperienceGain(ctx *player.Context, amount *int)
- func (h *SessionHandler) HandleFireExtinguish(ctx *player.Context, pos cube.Pos)
- func (h *SessionHandler) HandleFoodLoss(ctx *player.Context, from int, to *int)
- func (h *SessionHandler) HandleHeal(ctx *player.Context, health *float64, src world.HealingSource)
- func (h *SessionHandler) HandleHeldSlotChange(ctx *player.Context, from, to int)
- func (h *SessionHandler) HandleHurt(ctx *player.Context, damage *float64, immune bool, ...)
- func (h *SessionHandler) HandleItemConsume(ctx *player.Context, it item.Stack)
- func (h *SessionHandler) HandleItemDamage(ctx *player.Context, it item.Stack, damage *int)
- func (h *SessionHandler) HandleItemDrop(ctx *player.Context, it item.Stack)
- func (h *SessionHandler) HandleItemPickup(ctx *player.Context, it *item.Stack)
- func (h *SessionHandler) HandleItemRelease(ctx *player.Context, it item.Stack, dur time.Duration)
- func (h *SessionHandler) HandleItemUse(ctx *player.Context)
- func (h *SessionHandler) HandleItemUseOnBlock(ctx *player.Context, pos cube.Pos, face cube.Face, clickPos mgl64.Vec3)
- func (h *SessionHandler) HandleItemUseOnEntity(ctx *player.Context, e world.Entity)
- func (h *SessionHandler) HandleJump(p *player.Player)
- func (h *SessionHandler) HandleLecternPageTurn(ctx *player.Context, pos cube.Pos, oldPage int, newPage *int)
- func (h *SessionHandler) HandleMove(ctx *player.Context, newPos mgl64.Vec3, newRot cube.Rotation)
- func (h *SessionHandler) HandlePunchAir(ctx *player.Context)
- func (h *SessionHandler) HandleQuit(p *player.Player)
- func (h *SessionHandler) HandleRespawn(p *player.Player, pos *mgl64.Vec3, w **world.World)
- func (h *SessionHandler) HandleSignEdit(ctx *player.Context, pos cube.Pos, frontSide bool, oldText, newText string)
- func (h *SessionHandler) HandleSkinChange(ctx *player.Context, sk *skin.Skin)
- func (h *SessionHandler) HandleSleep(ctx *player.Context, sendReminder *bool)
- func (h *SessionHandler) HandleStartBreak(ctx *player.Context, pos cube.Pos)
- func (h *SessionHandler) HandleTeleport(ctx *player.Context, pos mgl64.Vec3)
- func (h *SessionHandler) HandleToggleSneak(ctx *player.Context, after bool)
- func (h *SessionHandler) HandleToggleSprint(ctx *player.Context, after bool)
- func (h *SessionHandler) HandleTransfer(ctx *player.Context, addr *net.UDPAddr)
- func (h *SessionHandler) Session() *Session
- type Shared
- type SharedProvider
- type SharedSet
- func (ss *SharedSet[T]) Add(entityID string)
- func (ss *SharedSet[T]) Clear()
- func (ss *SharedSet[T]) IDs() []string
- func (ss *SharedSet[T]) Len() int
- func (ss *SharedSet[T]) Remove(entityID string)
- func (ss *SharedSet[T]) Resolve(m *Manager) []*T
- func (ss *SharedSet[T]) Set(entityIDs []string)
- func (ss *SharedSet[T]) TargetType() reflect.Type
- type Stage
- type Subscription
- type SystemMeta
- type TagInfo
- type TaskHandle
- func Dispatch(s *Session, task Runnable) *TaskHandle
- func Dispatch2(s1, s2 *Session, task Runnable) *TaskHandle
- func DispatchGlobal(m *Manager, task Runnable) *TaskHandle
- func Schedule(s *Session, task Runnable, delay time.Duration) *TaskHandle
- func Schedule2(s1, s2 *Session, task Runnable, delay time.Duration) *TaskHandle
- func ScheduleAt(s *Session, task Runnable, at time.Time) *TaskHandle
- func ScheduleGlobal(m *Manager, task Runnable, delay time.Duration) *TaskHandle
- type WindowMeta
- type With
- type Without
Constants ¶
const MaxComponents = 255
MaxComponents is the maximum number of component types supported per manager.
const Version = "1.0.0"
Version is the PECS version.
Variables ¶
This section is empty.
Functions ¶
func Add ¶
Add attaches a component to the session. If a component of this type already exists, it is replaced. If the component implements Attachable, its Attach method is called.
Concurrency: This function is thread-safe.
func AddFor ¶
AddFor attaches a component to the session that will be automatically removed after the specified duration. Useful for buffs, debuffs, and temporary effects.
If a component of this type already exists, it is replaced and the expiration is reset. If the component implements Attachable, its Attach method is called. When the component expires, if it implements Detachable, its Detach method is called.
Example:
pecs.AddFor(sess, &SpeedBoost{Multiplier: 2.0}, 10*time.Second)
func AddUntil ¶
AddUntil attaches a component to the session that will be automatically removed at the specified time. Useful for buffs, debuffs, and temporary effects.
If a component of this type already exists, it is replaced and the expiration is reset. If the component implements Attachable, its Attach method is called. When the component expires, if it implements Detachable, its Detach method is called.
Example:
pecs.AddUntil(sess, &EventBuff{}, eventEndTime)
func Expired ¶
Expired returns true if the component has an expiration set AND that time has passed. Returns false if the component has no expiration or hasn't expired yet. Note: The component may still exist briefly after expiring until the next scheduler tick removes it.
Example:
if pecs.Expired[SpeedBoost](sess) {
// Buff has expired (or will be removed very soon)
}
func ExpiresAt ¶
ExpiresAt returns the time when a component will expire. Returns zero time if the component doesn't exist or has no expiration set.
Example:
expireTime := pecs.ExpiresAt[SpeedBoost](sess)
if !expireTime.IsZero() {
fmt.Printf("Speed boost expires at %v\n", expireTime)
}
func ExpiresIn ¶
ExpiresIn returns the remaining duration until a component expires. Returns 0 if the component doesn't exist or has no expiration set. Returns negative duration if the component has already expired (but not yet removed).
Example:
remaining := pecs.ExpiresIn[SpeedBoost](sess)
if remaining > 0 {
fmt.Printf("Speed boost expires in %v\n", remaining)
}
func Get ¶
Get retrieves a component from the session. Returns nil if the component is not present.
Concurrency: This function is thread-safe.
func GetOrAdd ¶
GetOrAdd retrieves a component from the session, or adds the default if missing. Returns the existing component if present, otherwise adds defaultVal and returns it.
Concurrency: This function is thread-safe.
func Has ¶
Has checks if a component type is present on the session.
Concurrency: This function is fully thread-safe and can be called from any goroutine.
func ManagerResource ¶
ManagerResource retrieves a global resource from the manager. Returns nil if the resource is not found.
func NewHandler ¶
NewHandler creates a new Dragonfly handler for the given session. This should be passed to player.Handle().
Types ¶
type AccessMeta ¶
type AccessMeta struct {
Reads []reflect.Type
Writes []reflect.Type
ResReads []reflect.Type
ResWrites []reflect.Type
// contains filtered or unexported fields
}
AccessMeta describes what components/resources a system reads or writes. Used for conflict detection and parallel scheduling.
func (*AccessMeta) Conflicts ¶
func (a *AccessMeta) Conflicts(other *AccessMeta) bool
Conflicts returns true if this access pattern conflicts with another.
func (*AccessMeta) PrepareSets ¶
func (a *AccessMeta) PrepareSets()
PrepareSets precomputes lookup sets from the slice fields for faster conflict checks.
type ActorConfig ¶
type ActorConfig struct {
// Identity & Core Settings
Name string
Skin skin.Skin
GameMode world.GameMode
// Position & Physics
Position mgl64.Vec3
Velocity mgl64.Vec3
Rotation cube.Rotation
FallDistance float64
// Vitals: Health
Health float64
HealthMax float64
// Vitals: Hunger
Food int
FoodTick int
Saturation float64
Exhaustion float64
// Vitals: Breath
AirSupply int
AirSupplyMax int
// Inventory & Equipment
Inventory *inventory.Inventory
EnderChest *inventory.Inventory
OffHand *inventory.Inventory
Armour *inventory.Armour
HeldSlot int
// State, Effects & Progression
Experience int
EnchantmentSeed int64
FireTicks int64
Effects []effect.Effect
}
ActorConfig configures the initial state of a fake player or NPC entity. It is used with Manager.SpawnFake and Manager.SpawnEntity.
type Attachable ¶
type Attachable interface {
Attach(s *Session)
}
Attachable is implemented by components that need initialization logic when attached to a session.
type Bitmask ¶
type Bitmask [4]uint64
Bitmask is a 256-bit bitmask used for tracking component presence. It supports up to 256 unique component types.
func (*Bitmask) Clear ¶
func (m *Bitmask) Clear(id ComponentID)
Clear clears the bit at the given index.
func (*Bitmask) ContainsAll ¶
ContainsAll returns true if all bits set in other are also set in m. This is used to check if all required components are present. Optimized for the common case where most systems require only a few components that fit in the first 64-bit segment.
func (*Bitmask) ContainsAny ¶
ContainsAny returns true if any bit set in other is also set in m. This is used to check if any excluded components (Without[T]) are present. Optimized similarly to ContainsAll for early exit.
func (*Bitmask) Has ¶
func (m *Bitmask) Has(id ComponentID) bool
Has returns true if the bit at the given index is set.
func (*Bitmask) IsDisjoint ¶
IsDisjoint returns true if no bits are set in both m and other.
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder configures PECS before initialization. Use NewBuilder() to create a builder and chain configuration methods.
func (*Builder) Init ¶
Init initializes PECS with the configured settings. Returns the Manager instance which should be stored and used to create sessions. Multiple Manager instances can coexist for running multiple isolated servers.
func (*Builder) PeerProvider ¶
func (b *Builder) PeerProvider(p PeerProvider, opts ...ProviderOption) *Builder
PeerProvider registers a provider for Peer[T] resolution. PeerProviders fetch and sync data for remote players.
Example:
builder.PeerProvider(&StatusProvider{...}, pecs.WithFetchTimeout(2000))
func (*Builder) SharedProvider ¶
func (b *Builder) SharedProvider(p SharedProvider, opts ...ProviderOption) *Builder
SharedProvider registers a provider for Shared[T] resolution. SharedProviders fetch and sync data for shared entities (parties, matches, etc.).
Example:
builder.SharedProvider(&PartyProvider{...})
type Bundle ¶
type Bundle struct {
// contains filtered or unexported fields
}
Bundle groups related systems, handlers, and resources together. Bundles are registered with the PECS builder and provide isolation between different gameplay features.
func (*Bundle) Build ¶
Build returns a callback function that returns this bundle. This allows for cleaner inline bundle initialization:
bund := pecs.NewBundle("gameplay").
Handler(&ExampleHandler{}).
Build()
mngr := pecs.NewBuilder().
Bundle(bund).
Init()
func (*Bundle) Command ¶
Command registers a Dragonfly command for this bundle. Commands are automatically registered with Dragonfly's command system when the bundle is built.
func (*Bundle) Handler ¶
Handler registers a handler for this bundle. Handlers are structs that implement event methods like HandleHurt(*EventHurt).
func (*Bundle) Loop ¶
Loop registers a loop system that runs at fixed intervals. Interval of 0 means the loop runs every tick.
func (*Bundle) PeerProvider ¶
func (b *Bundle) PeerProvider(p PeerProvider, opts ...ProviderOption) *Bundle
PeerProvider registers a provider for Peer[T] resolution.
func (*Bundle) Resource ¶
Resource registers a bundle-level resource. These are available to all systems via global manager.
func (*Bundle) SharedProvider ¶
func (b *Bundle) SharedProvider(p SharedProvider, opts ...ProviderOption) *Bundle
SharedProvider registers a provider for Shared[T] resolution.
type ComponentAttachEvent ¶
ComponentAttachEvent is emitted when a component is added to a session.
type ComponentDetachEvent ¶
ComponentDetachEvent is emitted when a component is removed from a session.
type ComponentID ¶
type ComponentID uint8
ComponentID is a unique identifier for a component type. Valid IDs range from 0 to 255 per manager.
type Detachable ¶
type Detachable interface {
Detach(s *Session)
}
Detachable is implemented by components that need cleanup logic when detached from a session or when the session closes.
type EntityMarker ¶
type EntityMarker struct{}
EntityMarker marks a session as an NPC entity. Entities do not participate in cross-server provider lookups.
type EventAttackEntity ¶
type EventAttackEntity struct {
Ctx *player.Context
Entity world.Entity
Force *float64
Height *float64
Critical *bool
}
EventAttackEntity is emitted when a player attacks an entity.
func (*EventAttackEntity) Cancel ¶
func (e *EventAttackEntity) Cancel()
func (*EventAttackEntity) Tx ¶
func (e *EventAttackEntity) Tx() *world.Tx
func (*EventAttackEntity) Val ¶
func (e *EventAttackEntity) Val() *player.Player
type EventBlockBreak ¶
type EventBlockBreak struct {
Ctx *player.Context
Position cube.Pos
Drops *[]item.Stack
Experience *int
}
EventBlockBreak is emitted when a player breaks a block.
func (*EventBlockBreak) Cancel ¶
func (e *EventBlockBreak) Cancel()
func (*EventBlockBreak) Tx ¶
func (e *EventBlockBreak) Tx() *world.Tx
func (*EventBlockBreak) Val ¶
func (e *EventBlockBreak) Val() *player.Player
type EventBlockPick ¶
EventBlockPick is emitted when a player picks a block.
func (*EventBlockPick) Cancel ¶
func (e *EventBlockPick) Cancel()
func (*EventBlockPick) Tx ¶
func (e *EventBlockPick) Tx() *world.Tx
func (*EventBlockPick) Val ¶
func (e *EventBlockPick) Val() *player.Player
type EventBlockPlace ¶
EventBlockPlace is emitted when a player places a block.
func (*EventBlockPlace) Cancel ¶
func (e *EventBlockPlace) Cancel()
func (*EventBlockPlace) Tx ¶
func (e *EventBlockPlace) Tx() *world.Tx
func (*EventBlockPlace) Val ¶
func (e *EventBlockPlace) Val() *player.Player
type EventChangeWorld ¶
EventChangeWorld is emitted when a player changes worlds.
type EventCommandExecution ¶
EventCommandExecution is emitted when a player executes a command.
func (*EventCommandExecution) Cancel ¶
func (e *EventCommandExecution) Cancel()
func (*EventCommandExecution) Tx ¶
func (e *EventCommandExecution) Tx() *world.Tx
func (*EventCommandExecution) Val ¶
func (e *EventCommandExecution) Val() *player.Player
type EventDeath ¶
type EventDeath struct {
Player *player.Player
Source world.DamageSource
KeepInventory *bool
}
EventDeath is emitted when a player dies.
type EventDiagnostics ¶
type EventDiagnostics struct {
Player *player.Player
Diagnostics session.Diagnostics
}
EventDiagnostics is emitted for diagnostics data.
type EventExperienceGain ¶
EventExperienceGain is emitted when a player gains experience.
func (*EventExperienceGain) Cancel ¶
func (e *EventExperienceGain) Cancel()
func (*EventExperienceGain) Tx ¶
func (e *EventExperienceGain) Tx() *world.Tx
func (*EventExperienceGain) Val ¶
func (e *EventExperienceGain) Val() *player.Player
type EventFireExtinguish ¶
EventFireExtinguish is emitted when a player extinguishes fire.
func (*EventFireExtinguish) Cancel ¶
func (e *EventFireExtinguish) Cancel()
func (*EventFireExtinguish) Tx ¶
func (e *EventFireExtinguish) Tx() *world.Tx
func (*EventFireExtinguish) Val ¶
func (e *EventFireExtinguish) Val() *player.Player
type EventFoodLoss ¶
EventFoodLoss is emitted when a player loses food.
func (*EventFoodLoss) Cancel ¶
func (e *EventFoodLoss) Cancel()
func (*EventFoodLoss) Tx ¶
func (e *EventFoodLoss) Tx() *world.Tx
func (*EventFoodLoss) Val ¶
func (e *EventFoodLoss) Val() *player.Player
type EventHeal ¶
type EventHeal struct {
Ctx *player.Context
Health *float64
Source world.HealingSource
}
EventHeal is emitted when a player is healed.
type EventHeldSlotChange ¶
EventHeldSlotChange is emitted when a player changes their held slot.
func (*EventHeldSlotChange) Cancel ¶
func (e *EventHeldSlotChange) Cancel()
func (*EventHeldSlotChange) Tx ¶
func (e *EventHeldSlotChange) Tx() *world.Tx
func (*EventHeldSlotChange) Val ¶
func (e *EventHeldSlotChange) Val() *player.Player
type EventHurt ¶
type EventHurt struct {
Ctx *player.Context
Damage *float64
Immune bool
Immunity *time.Duration
Source world.DamageSource
}
EventHurt is emitted when a player is hurt.
type EventItemConsume ¶
EventItemConsume is emitted when a player consumes an item.
func (*EventItemConsume) Cancel ¶
func (e *EventItemConsume) Cancel()
func (*EventItemConsume) Tx ¶
func (e *EventItemConsume) Tx() *world.Tx
func (*EventItemConsume) Val ¶
func (e *EventItemConsume) Val() *player.Player
type EventItemDamage ¶
EventItemDamage is emitted when an item takes damage.
func (*EventItemDamage) Cancel ¶
func (e *EventItemDamage) Cancel()
func (*EventItemDamage) Tx ¶
func (e *EventItemDamage) Tx() *world.Tx
func (*EventItemDamage) Val ¶
func (e *EventItemDamage) Val() *player.Player
type EventItemDrop ¶
EventItemDrop is emitted when a player drops an item.
func (*EventItemDrop) Cancel ¶
func (e *EventItemDrop) Cancel()
func (*EventItemDrop) Tx ¶
func (e *EventItemDrop) Tx() *world.Tx
func (*EventItemDrop) Val ¶
func (e *EventItemDrop) Val() *player.Player
type EventItemPickup ¶
EventItemPickup is emitted when a player picks up an item.
func (*EventItemPickup) Cancel ¶
func (e *EventItemPickup) Cancel()
func (*EventItemPickup) Tx ¶
func (e *EventItemPickup) Tx() *world.Tx
func (*EventItemPickup) Val ¶
func (e *EventItemPickup) Val() *player.Player
type EventItemRelease ¶
EventItemRelease is emitted when a player releases a charged item.
func (*EventItemRelease) Cancel ¶
func (e *EventItemRelease) Cancel()
func (*EventItemRelease) Tx ¶
func (e *EventItemRelease) Tx() *world.Tx
func (*EventItemRelease) Val ¶
func (e *EventItemRelease) Val() *player.Player
type EventItemUse ¶
EventItemUse is emitted when a player uses an item.
func (*EventItemUse) Cancel ¶
func (e *EventItemUse) Cancel()
func (*EventItemUse) Tx ¶
func (e *EventItemUse) Tx() *world.Tx
func (*EventItemUse) Val ¶
func (e *EventItemUse) Val() *player.Player
type EventItemUseOnBlock ¶
type EventItemUseOnBlock struct {
Ctx *player.Context
Position cube.Pos
Face cube.Face
ClickPos mgl64.Vec3
}
EventItemUseOnBlock is emitted when a player uses an item on a block.
func (*EventItemUseOnBlock) Cancel ¶
func (e *EventItemUseOnBlock) Cancel()
func (*EventItemUseOnBlock) Tx ¶
func (e *EventItemUseOnBlock) Tx() *world.Tx
func (*EventItemUseOnBlock) Val ¶
func (e *EventItemUseOnBlock) Val() *player.Player
type EventItemUseOnEntity ¶
EventItemUseOnEntity is emitted when a player uses an item on an entity.
func (*EventItemUseOnEntity) Cancel ¶
func (e *EventItemUseOnEntity) Cancel()
func (*EventItemUseOnEntity) Tx ¶
func (e *EventItemUseOnEntity) Tx() *world.Tx
func (*EventItemUseOnEntity) Val ¶
func (e *EventItemUseOnEntity) Val() *player.Player
type EventLecternPageTurn ¶
EventLecternPageTurn is emitted when a player turns a lectern page.
func (*EventLecternPageTurn) Cancel ¶
func (e *EventLecternPageTurn) Cancel()
func (*EventLecternPageTurn) Tx ¶
func (e *EventLecternPageTurn) Tx() *world.Tx
func (*EventLecternPageTurn) Val ¶
func (e *EventLecternPageTurn) Val() *player.Player
type EventPunchAir ¶
EventPunchAir is emitted when a player punches air.
func (*EventPunchAir) Cancel ¶
func (e *EventPunchAir) Cancel()
func (*EventPunchAir) Tx ¶
func (e *EventPunchAir) Tx() *world.Tx
func (*EventPunchAir) Val ¶
func (e *EventPunchAir) Val() *player.Player
type EventRespawn ¶
EventRespawn is emitted when a player respawns.
type EventSignEdit ¶
type EventSignEdit struct {
Ctx *player.Context
Position cube.Pos
FrontSide bool
OldText string
NewText string
}
EventSignEdit is emitted when a player edits a sign.
func (*EventSignEdit) Cancel ¶
func (e *EventSignEdit) Cancel()
func (*EventSignEdit) Tx ¶
func (e *EventSignEdit) Tx() *world.Tx
func (*EventSignEdit) Val ¶
func (e *EventSignEdit) Val() *player.Player
type EventSkinChange ¶
EventSkinChange is emitted when a player changes their skin.
func (*EventSkinChange) Cancel ¶
func (e *EventSkinChange) Cancel()
func (*EventSkinChange) Tx ¶
func (e *EventSkinChange) Tx() *world.Tx
func (*EventSkinChange) Val ¶
func (e *EventSkinChange) Val() *player.Player
type EventSleep ¶
EventSleep is emitted when a player sleeps.
func (*EventSleep) Cancel ¶
func (e *EventSleep) Cancel()
func (*EventSleep) Tx ¶
func (e *EventSleep) Tx() *world.Tx
func (*EventSleep) Val ¶
func (e *EventSleep) Val() *player.Player
type EventStartBreak ¶
EventStartBreak is emitted when a player starts breaking a block.
func (*EventStartBreak) Cancel ¶
func (e *EventStartBreak) Cancel()
func (*EventStartBreak) Tx ¶
func (e *EventStartBreak) Tx() *world.Tx
func (*EventStartBreak) Val ¶
func (e *EventStartBreak) Val() *player.Player
type EventTeleport ¶
EventTeleport is emitted when a player is teleported.
func (*EventTeleport) Cancel ¶
func (e *EventTeleport) Cancel()
func (*EventTeleport) Tx ¶
func (e *EventTeleport) Tx() *world.Tx
func (*EventTeleport) Val ¶
func (e *EventTeleport) Val() *player.Player
type EventToggleSneak ¶
EventToggleSneak is emitted when a player toggles sneaking.
func (*EventToggleSneak) Cancel ¶
func (e *EventToggleSneak) Cancel()
func (*EventToggleSneak) Tx ¶
func (e *EventToggleSneak) Tx() *world.Tx
func (*EventToggleSneak) Val ¶
func (e *EventToggleSneak) Val() *player.Player
type EventToggleSprint ¶
EventToggleSprint is emitted when a player toggles sprinting.
func (*EventToggleSprint) Cancel ¶
func (e *EventToggleSprint) Cancel()
func (*EventToggleSprint) Tx ¶
func (e *EventToggleSprint) Tx() *world.Tx
func (*EventToggleSprint) Val ¶
func (e *EventToggleSprint) Val() *player.Player
type EventTransfer ¶
EventTransfer is emitted when a player is transferred to another server.
func (*EventTransfer) Cancel ¶
func (e *EventTransfer) Cancel()
func (*EventTransfer) Tx ¶
func (e *EventTransfer) Tx() *world.Tx
func (*EventTransfer) Val ¶
func (e *EventTransfer) Val() *player.Player
type FakeMarker ¶
type FakeMarker struct{}
FakeMarker marks a session as a fake player (testing bot). The federated ID is stored on Session.fakeID, not in the marker.
type FieldKind ¶
type FieldKind int
FieldKind represents the type of field for injection.
const ( // KindSession indicates a *Session field KindSession FieldKind = iota // KindManager indicates a *Manager field KindManager // KindComponent indicates a component field KindComponent // KindRelation indicates a relation traversal field KindRelation // KindRelationSlice indicates a relation set traversal field (slice) KindRelationSlice // KindResource indicates a resource field KindResource // KindPhantomWith indicates a With[T] phantom type KindPhantomWith // KindPhantomWithout indicates a Without[T] phantom type KindPhantomWithout // KindPayload indicates a non-injected payload field KindPayload // KindPeer indicates a Peer[T] resolution field (single remote player) KindPeer // KindPeerSlice indicates a PeerSet[T] resolution field (multiple remote players) KindPeerSlice KindShared KindSharedSlice // KindPeerSource indicates a Peer[T] field in a component (source for resolution) KindPeerSource // KindPeerSetSource indicates a PeerSet[T] field in a component (source for resolution) KindPeerSetSource KindSharedSource KindSharedSetSource )
type FieldMeta ¶
type FieldMeta struct {
// Offset is the field offset in the struct for unsafe injection
Offset uintptr
// Size is the field size in bytes (for payload field zeroing)
Size uintptr
// Name is the field name for debugging
Name string
// Kind is the type of field (component, resource, etc.)
Kind FieldKind
// ComponentID is the ID of the component type (for component fields)
ComponentID ComponentID
// ComponentType is the reflect.Type of the component.
// For payload fields, this stores the type of the field itself.
ComponentType reflect.Type
// Optional indicates the field can be nil
Optional bool
// Mutable indicates the field has write access
Mutable bool
// WindowIndex indicates which session window this field belongs to
WindowIndex int
// RelationSourceField is the name of the field containing the relation
// (for relation traversal fields)
RelationSourceField string
// RelationSourceIndex is the index of the source field in Fields
RelationSourceIndex int
// RelationDataOffset is the offset of the Relation/RelationSet field in the source component
RelationDataOffset uintptr
// IsSlice indicates this is a slice field (for RelationSet resolution)
IsSlice bool
// PeerSourceIndex is the index of the source component field for Peer/PeerSet resolution
PeerSourceIndex int
// PeerSourceOffset is the offset of the Peer[T]/PeerSet[T] field in the source component
PeerSourceOffset uintptr
SharedSourceIndex int
SharedSourceOffset uintptr
}
FieldMeta holds metadata about a single injectable field.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager is the central PECS coordinator. It manages sessions, bundles, and the scheduler. Multiple Manager instances can coexist in the same process for running multiple isolated servers.
func (*Manager) AllSessions ¶
AllSessions returns a slice of all active sessions.
func (*Manager) AllSessionsInWorld ¶
AllSessionsInWorld returns all active sessions in the specified world.
func (*Manager) Emit ¶
Emit sends an event to all active sessions. Global handlers are invoked first, then session-scoped handlers for each session.
func (*Manager) EmitExcept ¶
EmitExcept sends an event to all active sessions except the specified ones. Global handlers are invoked first, then session-scoped handlers for each non-excluded session.
func (*Manager) EmitGlobal ¶
EmitGlobal sends an event to global handlers only.
func (*Manager) GetSession ¶
GetSession retrieves the session for a player.
func (*Manager) GetSessionByHandle ¶
func (m *Manager) GetSessionByHandle(h *world.EntityHandle) *Session
GetSessionByHandle retrieves a session by entity handle.
func (*Manager) GetSessionByID ¶
GetSessionByID retrieves a session by its federated ID. This works for both real players (using XUID) and fake players (using FakeMarker.ID). Returns nil for entities (which have no federated ID).
func (*Manager) GetSessionByName ¶
GetSessionByName retrieves a session by player Name.
func (*Manager) GetSessionByUUID ¶
GetSessionByUUID retrieves a session by UUID.
func (*Manager) MoveSession ¶
MoveSession updates the session's world in the index.
func (*Manager) NewSession ¶
NewSession creates a new session for a player. This should be called when a player joins and the returned session should be passed to player.Handle() wrapped with NewHandler(). Automatically fetches data from registered PeerProviders and subscribes to updates.
func (*Manager) RegisterPeerProvider ¶
func (m *Manager) RegisterPeerProvider(p PeerProvider, opts ...ProviderOption)
RegisterPeerProvider registers a provider for Peer[T] resolution.
func (*Manager) RegisterSharedProvider ¶
func (m *Manager) RegisterSharedProvider(p SharedProvider, opts ...ProviderOption)
RegisterSharedProvider registers a provider for Shared[T] resolution.
func (*Manager) ResolvePeer ¶
ResolvePeer resolves a Peer[T] reference to the target's component. If the player is local, returns their component directly. If remote, fetches and caches via the registered PeerProvider.
func (*Manager) ResolvePeers ¶
ResolvePeers resolves multiple Peer[T] references. Uses batch fetching for efficiency when multiple players are remote.
func (*Manager) ResolveShared ¶
ResolveShared resolves a Shared[T] reference to the entity's data.
func (*Manager) ResolveSharedMany ¶
ResolveSharedMany resolves multiple Shared[T] references.
func (*Manager) SessionCount ¶
SessionCount returns the number of active sessions.
func (*Manager) SpawnEntity ¶
SpawnEntity creates an NPC entity and registers it as a PECS session. Entities do not participate in cross-server Peer[T] lookups. Returns the session for adding components.
func (*Manager) SpawnFake ¶
func (m *Manager) SpawnFake(tx *world.Tx, cfg ActorConfig, fakeID string) (*player.Player, *Session)
SpawnFake creates a fake player (testing bot) and registers it as a PECS session. The fakeID is used as the federated identifier for Peer[T] resolution. Returns the session for adding components.
func (*Manager) TickNumber ¶
TickNumber returns the current scheduler tick number.
type Peer ¶
type Peer[T any] struct { // contains filtered or unexported fields }
Peer[T] references another player's component data by their persistent ID.
When resolved:
- If the player is on the same server, their local Session component is used directly.
- If the player is remote, PECS fetches and syncs their data via the registered PeerProvider.
Usage:
type SocialData struct {
BestFriend Peer[FriendProfile]
}
// In a system - PECS injects the resolved data
type ShowFriendSystem struct {
Session *Session
Social *SocialData
FriendInfo *FriendProfile `pecs:"peer"` // Resolved from Social.BestFriend
}
// In a command - manual resolution
func (c ShowFriendCommand) Run(src cmd.Source, out *cmd.Output, tx *world.Tx) {
p, sess := pecs.Command(src)
if sess == nil {
return
}
social := pecs.Get[SocialData](sess)
if friend, ok := social.BestFriend.Resolve(sess.Manager()); ok {
out.Printf("Best friend: %s", friend.Username)
}
}
func (*Peer[T]) Resolve ¶
Resolve fetches the target player's component. If the player is local, returns their component directly. If remote, fetches via the registered PeerProvider. Returns (nil, false) if the peer is not set, player doesn't exist, or the component is not available.
func (*Peer[T]) TargetType ¶
TargetType returns the reflect.Type of the component T.
type PeerProvider ¶
type PeerProvider interface {
Provider
// PlayerComponents returns the component types this provider handles.
// PECS will only call this provider for Peer[T] where T is in this list.
PlayerComponents() []reflect.Type
// FetchPlayer retrieves components for a single player.
// Returns nil (not error) if the player doesn't exist.
FetchPlayer(ctx context.Context, playerID string) ([]any, error)
// FetchPlayers batch-fetches components for multiple players.
// Returns a map of playerID -> components.
// Missing players should be omitted from the map (not nil values).
FetchPlayers(ctx context.Context, playerIDs []string) (map[string][]any, error)
// SubscribePlayer starts receiving real-time updates for a player.
// Updates should be sent to the channel until the subscription is closed.
// Return an error if the player doesn't exist or subscription fails.
SubscribePlayer(ctx context.Context, playerID string, updates chan<- PlayerUpdate) (Subscription, error)
}
PeerProvider fetches and syncs per-player data for Peer[T] resolution. Implement this interface to enable cross-server player data access.
type PeerSet ¶
type PeerSet[T any] struct { // contains filtered or unexported fields }
PeerSet[T] references multiple players' component data.
Usage:
type PartyData struct {
Members PeerSet[MemberInfo]
}
type PartyDisplaySystem struct {
Session *Session
Party *PartyData
Members []*MemberInfo `pecs:"peer"` // Resolved from Party.Members
}
func (*PeerSet[T]) Resolve ¶
Resolve fetches all target players' components. Returns only successfully resolved components (nil entries are filtered out).
func (*PeerSet[T]) TargetType ¶
TargetType returns the reflect.Type of the component T.
type PhantomTypeInfo ¶
PhantomTypeInfo provides component type information for phantom types.
type PlayerUpdate ¶
type PlayerUpdate struct {
// ComponentType is the type of component being updated.
ComponentType reflect.Type
// Data is a pointer to the new component data.
// If nil, the component should be removed.
Data any
}
PlayerUpdate represents an update to a player's component from a provider.
type Provider ¶
type Provider interface {
// Name returns a unique identifier for this provider (for logging/debugging).
Name() string
}
Provider is the base interface for all data providers. Providers bridge PECS with external data sources (gRPC services, databases, etc.).
type ProviderOption ¶
type ProviderOption func(*ProviderOptions)
ProviderOption configures a provider.
func WithFetchTimeout ¶
func WithFetchTimeout(d time.Duration) ProviderOption
WithFetchTimeout sets the fetch timeout.
func WithGracePeriod ¶
func WithGracePeriod(d time.Duration) ProviderOption
WithGracePeriod sets the grace period.
func WithRequired ¶
func WithRequired(required bool) ProviderOption
WithRequired marks the provider as required for session creation. If a required provider fails during NewSession, the session creation fails.
func WithStaleTimeout ¶
func WithStaleTimeout(d time.Duration) ProviderOption
WithStaleTimeout sets the stale timeout.
type ProviderOptions ¶
type ProviderOptions struct {
// FetchTimeout is the maximum time to wait for Fetch calls.
// Default: 5 seconds.
FetchTimeout time.Duration
// GracePeriod is how long to keep cached data after the last reference is released.
// This prevents thrashing when players rapidly reference/dereference the same target.
// Default: 30 seconds.
GracePeriod time.Duration
// StaleTimeout defines when cached data is considered too old to use.
// If a subscription fails and data is older than this, resolution fails.
// Default: 5 minutes.
StaleTimeout time.Duration
// Required indicates this provider must succeed for session creation.
// If true, NewSession returns an error if this provider fails.
// If false, provider failures are logged but session creation continues.
// Default: false.
Required bool
}
ProviderOptions configures provider behavior.
type Relation ¶
type Relation[T any] struct { // contains filtered or unexported fields }
Relation represents a reference from one session to another. The type parameter T indicates what component the target session must have. This provides type-safe references between players.
Usage:
type Following struct {
Target pecs.Relation[Health] // Target must have Health component
}
func (*Relation[T]) Resolve ¶
Resolve retrieves the target session and its component of type T. Returns (nil, nil, false) if the relation is unset, the target is closed, or the component is missing.
func (*Relation[T]) Set ¶
Set sets the target session for this relation. The target should have a component of type T, though this is validated at system execution time, not at set time.
func (*Relation[T]) TargetType ¶
TargetType returns the reflect.Type of the component the target must have.
type RelationSet ¶
type RelationSet[T any] struct { // contains filtered or unexported fields }
RelationSet represents a set of references to other sessions. The type parameter T indicates what component target sessions should have.
Usage:
type PartyLeader struct {
Members pecs.RelationSet[PartyMember]
}
func (*RelationSet[T]) Add ¶
func (rs *RelationSet[T]) Add(target *Session)
Add adds a session to the relation set.
func (*RelationSet[T]) All ¶
func (rs *RelationSet[T]) All() []*Session
All returns all non-closed target sessions. Closed sessions are lazily removed on subsequent calls.
func (*RelationSet[T]) Clear ¶
func (rs *RelationSet[T]) Clear()
Clear removes all sessions from the relation set.
func (*RelationSet[T]) Has ¶
func (rs *RelationSet[T]) Has(target *Session) bool
Has checks if a session is in the relation set.
func (*RelationSet[T]) Len ¶
func (rs *RelationSet[T]) Len() int
Len returns the number of sessions in the relation set.
func (*RelationSet[T]) Remove ¶
func (rs *RelationSet[T]) Remove(target *Session)
Remove removes a session from the relation set.
func (*RelationSet[T]) Resolve ¶
func (rs *RelationSet[T]) Resolve() []Resolved[T]
Resolve returns all valid sessions with their components. A session is valid if it's not closed and has the required component T. Closed sessions are lazily removed on subsequent calls.
func (*RelationSet[T]) TargetType ¶
func (rs *RelationSet[T]) TargetType() reflect.Type
TargetType returns the reflect.Type of the component targets must have.
type RepeatingTaskHandle ¶
type RepeatingTaskHandle struct {
// contains filtered or unexported fields
}
RepeatingTaskHandle allows cancelling a repeating scheduled task.
func ScheduleRepeating ¶
func ScheduleRepeating(s *Session, task Runnable, interval time.Duration, times int) *RepeatingTaskHandle
ScheduleRepeating schedules a task to run repeatedly at the given interval. If times is -1, the task repeats indefinitely until cancelled. If times is > 0, the task runs exactly that many times. Returns a RepeatingTaskHandle that can be used to cancel future executions.
func (*RepeatingTaskHandle) Cancel ¶
func (h *RepeatingTaskHandle) Cancel()
Cancel cancels the repeating task, preventing future executions.
type Runnable ¶
Runnable is the interface implemented by loops and tasks. The Run method contains the system's logic and is called when the system executes. The tx parameter is the active world transaction - use it instead of opening new transactions to avoid deadlocks. All sessions in a system are guaranteed to be in this transaction's world.
type Scheduler ¶
type Scheduler struct {
// contains filtered or unexported fields
}
Scheduler manages the execution of loops and tasks. It supports parallel execution of non-conflicting systems.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session represents a player's session in PECS. It wraps the player's EntityHandle (which is persistent across transactions) and stores all components attached to the player.
Sessions are created when players join and destroyed when they leave. They implement pecs.Handler to intercept all player events.
func Command ¶
Command extracts the player and session from a command source. Returns (nil, nil) if the source is not a player or has no session.
Usage:
func (c MyCommand) Run(src cmd.Source, out *cmd.Output, tx *world.Tx) {
p, sess := pecs.Command(src)
if sess == nil {
out.Error("Player-only command")
return
}
// Use p and sess...
}
Concurrency: Commands are executed synchronously with the player, just like handlers. It is safe to access and modify components directly.
func Form ¶
Form extracts the player and session from a form submitter. Returns (nil, nil) if the submitter is not a player or has no session.
Usage:
func (f MyForm) Submit(sub form.Submitter, tx *world.Tx) {
p, sess := pecs.Form(sub)
if sess == nil {
return
}
// Use p and sess...
}
Concurrency: Form submissions are executed synchronously with the player, just like handlers. It is safe to access and modify components directly.
func Item ¶
Item extracts the player and session from an item user. Returns (nil, nil) if the user is not a player or has no session.
Usage:
func (i MyItem) Use(tx *world.Tx, user item.User, ctx *item.UseContext) bool {
p, sess := pecs.Item(user)
if sess == nil {
return
}
// Use p and sess...
}
Concurrency: Item uses are executed synchronously with the player, just like handlers. It is safe to access and modify components directly.
func (*Session) Emit ¶
Emit sends an event to all registered handlers that listen for it. Handlers listen for events by implementing a method with the signature:
func (h *MyHandler) HandleMyEvent(ev *MyEventType)
The method name does not matter, only the signature (one argument).
func (*Session) Exec ¶
Exec runs a function within the session's world transaction. Returns false if the player is offline or the session is closed.
func (*Session) Handle ¶
func (s *Session) Handle() *world.EntityHandle
Handle returns the underlying EntityHandle.
func (*Session) ID ¶
ID returns the federated identifier for this session. For real players, this is their XUID. For fake players, this is their FakeMarker.ID. For entities, this returns an empty string (no federated ID). This is used for Peer[T] resolution to identify players across servers.
func (*Session) IsActor ¶
IsActor returns true if this session is an actor (fake player or entity). Bots have session.Nop as their network session.
func (*Session) Mask ¶
Mask returns a copy of the session's component bitmask. This is primarily for debugging and testing.
func (*Session) Player ¶
Player retrieves the *player.Player instance associated with this session within the given transaction. It returns (nil, false) if the player entity is not present in the transaction (e.g. offline or in another world).
Usage:
if p, ok := s.Player(tx); ok {
p.Message("Hello!")
}
type SessionHandler ¶
type SessionHandler struct {
// contains filtered or unexported fields
}
SessionHandler wraps a PECS session to implement Dragonfly's player.Handler. It receives Dragonfly events and emits them as pooled PECS events.
Concurrency: Handlers are executed synchronously by Dragonfly (typically within the world's tick loop or packet processing). This means they are serialized with respect to the world state and generally do not race with PECS Loops or Tasks, as those are also executed within the world's transaction context via the Scheduler.
It is safe for Handlers to read/write Components, as they effectively have exclusive access during execution relative to the specific world.
func (*SessionHandler) HandleAttackEntity ¶
func (h *SessionHandler) HandleAttackEntity(ctx *player.Context, e world.Entity, force, height *float64, critical *bool)
HandleAttackEntity handles attacking an entity.
func (*SessionHandler) HandleBlockBreak ¶
func (h *SessionHandler) HandleBlockBreak(ctx *player.Context, pos cube.Pos, drops *[]item.Stack, xp *int)
HandleBlockBreak handles block breaking.
func (*SessionHandler) HandleBlockPick ¶
HandleBlockPick handles picking a block.
func (*SessionHandler) HandleBlockPlace ¶
HandleBlockPlace handles block placement.
func (*SessionHandler) HandleChangeWorld ¶
func (h *SessionHandler) HandleChangeWorld(p *player.Player, before, after *world.World)
HandleChangeWorld handles the player changing worlds.
func (*SessionHandler) HandleChat ¶
func (h *SessionHandler) HandleChat(ctx *player.Context, message *string)
HandleChat handles the player sending a chat message.
func (*SessionHandler) HandleCommandExecution ¶
func (h *SessionHandler) HandleCommandExecution(ctx *player.Context, command cmd.Command, args []string)
HandleCommandExecution handles executing a command.
func (*SessionHandler) HandleDeath ¶
func (h *SessionHandler) HandleDeath(p *player.Player, src world.DamageSource, keepInv *bool)
HandleDeath handles the player dying.
func (*SessionHandler) HandleDiagnostics ¶
func (h *SessionHandler) HandleDiagnostics(p *player.Player, d session.Diagnostics)
HandleDiagnostics handles a diagnostics request.
func (*SessionHandler) HandleExperienceGain ¶
func (h *SessionHandler) HandleExperienceGain(ctx *player.Context, amount *int)
HandleExperienceGain handles XP gain.
func (*SessionHandler) HandleFireExtinguish ¶
func (h *SessionHandler) HandleFireExtinguish(ctx *player.Context, pos cube.Pos)
HandleFireExtinguish handles the player extinguishing fire.
func (*SessionHandler) HandleFoodLoss ¶
func (h *SessionHandler) HandleFoodLoss(ctx *player.Context, from int, to *int)
HandleFoodLoss handles the player losing food.
func (*SessionHandler) HandleHeal ¶
func (h *SessionHandler) HandleHeal(ctx *player.Context, health *float64, src world.HealingSource)
HandleHeal handles the player being healed.
func (*SessionHandler) HandleHeldSlotChange ¶
func (h *SessionHandler) HandleHeldSlotChange(ctx *player.Context, from, to int)
HandleHeldSlotChange handles held hotbar slot change.
func (*SessionHandler) HandleHurt ¶
func (h *SessionHandler) HandleHurt(ctx *player.Context, damage *float64, immune bool, attackImmunity *time.Duration, src world.DamageSource)
HandleHurt handles the player being hurt.
func (*SessionHandler) HandleItemConsume ¶
func (h *SessionHandler) HandleItemConsume(ctx *player.Context, it item.Stack)
HandleItemConsume handles consuming an item.
func (*SessionHandler) HandleItemDamage ¶
HandleItemDamage handles damaging an item.
func (*SessionHandler) HandleItemDrop ¶
func (h *SessionHandler) HandleItemDrop(ctx *player.Context, it item.Stack)
HandleItemDrop handles dropping an item.
func (*SessionHandler) HandleItemPickup ¶
func (h *SessionHandler) HandleItemPickup(ctx *player.Context, it *item.Stack)
HandleItemPickup handles picking up an item.
func (*SessionHandler) HandleItemRelease ¶
HandleItemRelease handles releasing a charged-use item.
func (*SessionHandler) HandleItemUse ¶
func (h *SessionHandler) HandleItemUse(ctx *player.Context)
HandleItemUse handles general item use.
func (*SessionHandler) HandleItemUseOnBlock ¶
func (h *SessionHandler) HandleItemUseOnBlock(ctx *player.Context, pos cube.Pos, face cube.Face, clickPos mgl64.Vec3)
HandleItemUseOnBlock handles using an item on a block.
func (*SessionHandler) HandleItemUseOnEntity ¶
func (h *SessionHandler) HandleItemUseOnEntity(ctx *player.Context, e world.Entity)
HandleItemUseOnEntity handles using an item on an entity.
func (*SessionHandler) HandleJump ¶
func (h *SessionHandler) HandleJump(p *player.Player)
HandleJump handles the player jumping.
func (*SessionHandler) HandleLecternPageTurn ¶
func (h *SessionHandler) HandleLecternPageTurn(ctx *player.Context, pos cube.Pos, oldPage int, newPage *int)
HandleLecternPageTurn handles page turning on lecterns.
func (*SessionHandler) HandleMove ¶
HandleMove handles the player moving.
func (*SessionHandler) HandlePunchAir ¶
func (h *SessionHandler) HandlePunchAir(ctx *player.Context)
HandlePunchAir handles punching air.
func (*SessionHandler) HandleQuit ¶
func (h *SessionHandler) HandleQuit(p *player.Player)
HandleQuit handles a player quitting the server.
func (*SessionHandler) HandleRespawn ¶
HandleRespawn handles the player respawning.
func (*SessionHandler) HandleSignEdit ¶
func (h *SessionHandler) HandleSignEdit(ctx *player.Context, pos cube.Pos, frontSide bool, oldText, newText string)
HandleSignEdit handles sign text editing.
func (*SessionHandler) HandleSkinChange ¶
func (h *SessionHandler) HandleSkinChange(ctx *player.Context, sk *skin.Skin)
HandleSkinChange handles the player changing their skin.
func (*SessionHandler) HandleSleep ¶
func (h *SessionHandler) HandleSleep(ctx *player.Context, sendReminder *bool)
HandleSleep handles sleeping in a bed.
func (*SessionHandler) HandleStartBreak ¶
func (h *SessionHandler) HandleStartBreak(ctx *player.Context, pos cube.Pos)
HandleStartBreak handles the player starting to break a block.
func (*SessionHandler) HandleTeleport ¶
func (h *SessionHandler) HandleTeleport(ctx *player.Context, pos mgl64.Vec3)
HandleTeleport handles the player being teleported.
func (*SessionHandler) HandleToggleSneak ¶
func (h *SessionHandler) HandleToggleSneak(ctx *player.Context, after bool)
HandleToggleSneak handles the player toggling sneak.
func (*SessionHandler) HandleToggleSprint ¶
func (h *SessionHandler) HandleToggleSprint(ctx *player.Context, after bool)
HandleToggleSprint handles the player toggling sprint.
func (*SessionHandler) HandleTransfer ¶
func (h *SessionHandler) HandleTransfer(ctx *player.Context, addr *net.UDPAddr)
HandleTransfer handles server transfer.
func (*SessionHandler) Session ¶
func (h *SessionHandler) Session() *Session
Session returns the session associated with this handler.
type Shared ¶
type Shared[T any] struct { // contains filtered or unexported fields }
Shared[T] references a shared entity's data by its ID.
Unlike Peer[T] which references player data, Shared[T] references entities that are shared across multiple players (parties, matches, guilds, etc.). The data is cached globally and all references point to the same instance.
Usage:
type MatchmakingData struct {
CurrentParty Shared[PartyInfo]
ActiveMatch Shared[MatchInfo]
}
// In a system - PECS injects the resolved data
type PartyDisplaySystem struct {
Session *Session
MMData *MatchmakingData
Party *PartyInfo `pecs:"shared"` // Resolved from MMData.CurrentParty
}
// In a command - manual resolution
func (c PartyInfoCommand) Run(src cmd.Source, out *cmd.Output, tx *world.Tx) {
p, sess := pecs.Command(src)
if sess == nil {
return
}
mmData := pecs.Get[MatchmakingData](sess)
if party, ok := mmData.CurrentParty.Resolve(sess.Manager()); ok {
out.Printf("Party: %s (%d members)", party.Name, len(party.Members))
}
}
func (*Shared[T]) Resolve ¶
Resolve fetches the shared entity's data. Returns (nil, false) if the entity is not set, doesn't exist, or the data is not available.
func (*Shared[T]) TargetType ¶
TargetType returns the reflect.Type of the data type T.
type SharedProvider ¶
type SharedProvider interface {
Provider
// PECS will only call this provider for Shared[T] where T is in this list.
EntityComponents() []reflect.Type
// Returns nil (not error) if the entity doesn't exist.
FetchEntity(ctx context.Context, entityID string) (any, error)
// Returns a map of entityID -> component.
// Missing entities should be omitted from the map.
FetchEntities(ctx context.Context, entityIDs []string) (map[string]any, error)
// Updates should be sent to the channel until the subscription is closed.
// Return an error if the entity doesn't exist or subscription fails.
SubscribeEntity(ctx context.Context, entityID string, updates chan<- any) (Subscription, error)
}
SharedProvider fetches and syncs shared entity data for Shared[T] resolution. Implement this interface for data shared across multiple players (parties, matches, etc.).
type SharedSet ¶
type SharedSet[T any] struct { // contains filtered or unexported fields }
SharedSet[T] references multiple shared entities.
Usage:
type GuildData struct {
ActiveWars SharedSet[WarInfo]
}
type WarDisplaySystem struct {
Session *Session
Guild *GuildData
Wars []*WarInfo `pecs:"shared"` // Resolved from Guild.ActiveWars
}
func (*SharedSet[T]) Resolve ¶
Resolve fetches all shared entities' data. Returns only successfully resolved data (nil entries are filtered out).
func (*SharedSet[T]) TargetType ¶
TargetType returns the reflect.Type of the data type T.
type Stage ¶
type Stage int
Stage represents a scheduling stage for system execution. Systems are executed in stage order: Before → Default → After.
const ( // Before stage runs first. Use for pre-processing, input handling, // and setup logic that other systems depend on. Before Stage = iota // Default stage runs second. Use for main game logic including // combat, movement, abilities, and most gameplay systems. Default // After stage runs last. Use for cleanup, synchronization, // statistics logging, and network state updates. After )
type Subscription ¶
type Subscription interface {
Close() error
}
Subscription represents an active subscription to updates. Call Close() to stop receiving updates and release resources.
type SystemMeta ¶
type SystemMeta struct {
// Type is the reflect.Type of the system struct
Type reflect.Type
// Name is the type name for debugging
Name string
// RequireMask is the bitmask of required components
RequireMask Bitmask
// ExcludeMask is the bitmask of excluded components (Without[T])
ExcludeMask Bitmask
// Fields holds injection metadata for each field
Fields []FieldMeta
// Windows defines session windows for multi-session systems
Windows []WindowMeta
// Stage is the execution stage
Stage Stage
// IsMultiSession indicates this is a multi-session system
IsMultiSession bool
// IsGlobal indicates this is a global system that runs once, not per-session.
IsGlobal bool
// Pool is the sync.Pool for this system type
Pool *sync.Pool
// Bundle is the bundle this system belongs to
Bundle *Bundle
// AccessMeta for conflict detection
Access AccessMeta
}
SystemMeta holds pre-computed metadata about a system type. This is computed once at registration time and reused for all executions.
type TagInfo ¶
type TagInfo struct {
Mutable bool // pecs:"mut"
Optional bool // pecs:"opt"
Relation bool // pecs:"rel"
Resource bool // pecs:"res"
Peer bool // pecs:"peer"
}
TagInfo holds parsed tag information.
type TaskHandle ¶
type TaskHandle struct {
// contains filtered or unexported fields
}
TaskHandle allows cancelling a scheduled task.
func Dispatch ¶
func Dispatch(s *Session, task Runnable) *TaskHandle
Dispatch immediately executes a task in the next tick. Returns a TaskHandle that can be used to cancel the task.
func Dispatch2 ¶
func Dispatch2(s1, s2 *Session, task Runnable) *TaskHandle
Dispatch2 immediately executes a multi-session task in the next tick. Returns a TaskHandle that can be used to cancel the task.
func DispatchGlobal ¶
func DispatchGlobal(m *Manager, task Runnable) *TaskHandle
DispatchGlobal schedules a global task for execution immediately. The task runs once in the manager's default world and is not tied to any session. Returns a TaskHandle that can be used to cancel the task.
func Schedule ¶
func Schedule(s *Session, task Runnable, delay time.Duration) *TaskHandle
Schedule schedules a task for execution after the given delay. The task will only run if the session passes the bitmask check at execution time. Returns a TaskHandle that can be used to cancel the task.
func Schedule2 ¶
func Schedule2(s1, s2 *Session, task Runnable, delay time.Duration) *TaskHandle
Schedule2 schedules a multi-session task for execution after the given delay. Both sessions must be in the same world at execution time and belong to the same manager. Returns a TaskHandle that can be used to cancel the task.
func ScheduleAt ¶
func ScheduleAt(s *Session, task Runnable, at time.Time) *TaskHandle
ScheduleAt schedules a task for execution at a specific time. If the time is in the past, the task will execute on the next tick. Returns a TaskHandle that can be used to cancel the task.
func ScheduleGlobal ¶
func ScheduleGlobal(m *Manager, task Runnable, delay time.Duration) *TaskHandle
ScheduleGlobal schedules a global task for execution after a given delay. The task runs once in the manager's default world and is not tied to any session. Returns a TaskHandle that can be used to cancel the task.
type WindowMeta ¶
type WindowMeta struct {
// SessionFieldIndex is the index of the *Session field in Fields
SessionFieldIndex int
// StartFieldIndex is the first field index for this window
StartFieldIndex int
// EndFieldIndex is one past the last field index for this window
EndFieldIndex int
// RequireMask is the bitmask of required components for this window
RequireMask Bitmask
// ExcludeMask is the bitmask of excluded components for this window
ExcludeMask Bitmask
}
WindowMeta defines a session window in a multi-session system.
type With ¶
type With[T any] struct{}
With is a phantom type that indicates a component must exist for the system to run. The component is not injected into the field - it's only used for filtering.
Usage:
type MySystem struct {
Session *pecs.Session
_ pecs.With[VampireTag] // Only run if VampireTag exists
}
func (With[T]) ComponentType ¶
ComponentType implements PhantomTypeInfo for With[T].
type Without ¶
type Without[T any] struct{}
Without is a phantom type that indicates a component must NOT exist for the system to run. The system will be skipped if the component is present on the session.
Usage:
type MySystem struct {
Session *pecs.Session
_ pecs.Without[Spectator] // Skip if Spectator exists
}
func (Without[T]) ComponentType ¶
ComponentType implements PhantomTypeInfo for Without[T].