Async tasks and ownership
Bounded background work, cancellation и server-owned применение state changes.
В этом разделе
Другие страницы раздела:
Правила
Pulse core сам владеет background work. Плагины могут запрашивать долгие операции, но не должны запускать goroutine, которые напрямую меняют player, world, entity, inventory, session, storage или runtime state.
- Быстрые handle-методы могут выглядеть синхронно, но mutation всё равно проходит через owning service.
- Долгая работа идет через bounded core task runners с context cancellation и queue backpressure.
- Background job готовит данные; финальное изменение state применяет server-owned service.
- Rejected work возвращает typed error, например
task.ErrQueueFullилиtask.ErrClosed. - Plugin scheduler нужен для tick-timed gameplay callbacks, а не для неконтролируемого background IO.
Core task runner
core/task.Runner - низкоуровневый bounded worker pool. Submit(ctx, name, fn) принимает named task или отклоняет ее, если queue full/closed.
- Task handle дает
ID,Name,Done,CancelиAwait. Resultхранит value, error, start time и finish time.Runner.Stats()показывает queue depth/capacity, running tasks, completions, failures, cancellations и rejects.plugin.ManagerConfigдает host-level настройкиTaskWorkersиTaskQueue; host может читатьplugin.Manager.TaskStats().
Managed timers
core/task.TimerScheduler используется plugin scheduler и заменяет one-goroutine-per-delay scheduling на один owner-managed loop.
Later(ctx, delay, fn)выполняется один раз, если не отменен.Repeat(ctx, period, fn)выполняется до отмены.Close()отменяет pending timers и останавливает loop.ctx.Scheduler().Later/Repeatпо-прежнему отменяется при plugin unload.
Переведенные services
ctx.WorldTemplates().Copy(...) теперь идет через core task runner. Completion callback возвращается через plugin scheduler.
world-template-task.go
taskHandle, err := ctx.WorldTemplates().CopyTask(ctx, plugin.WorldTemplateCopyRequest{ TemplatePath: "worlds/templates/duel", TargetPath: "worlds/arenas/duel-1", WorldID: "duel-1",})if err != nil { return err}result := taskHandle.Await(ctx)if result.Err != nil { return result.Err}world-lifecycle-task.go
preload, err := worldHandle.PreloadChunksTask(ctx, world.Pos{X: 0, Y: 64, Z: 0}, 8)if err != nil { return err}if result := preload.Await(ctx); result.Err != nil { return result.Err}save, err := worldHandle.SaveTask(ctx)if err != nil { return err}_ = saveWorldHandleдаетPreloadChunksTaskиSaveTaskдля первых world lifecycle операций.PlayerDataProviderдаетOfflineTask,ByNameTask,ByXUIDTask,ByUUIDTaskдля async persisted-data reads.ctx.ResourcePacks()даетRegisterFileTask,ChunkTaskиPacks()для task-backed file IO.- World generation queue saturation возвращает
world.ErrGenerationQueueFullи увеличивает saturation stats, а не создает ожидающую goroutine.
resource-pack-task.go
packTask, err := ctx.ResourcePacks().RegisterFileTask(ctx, "packs/hub.mcpack", plugin.ResourcePackOptions{})if err != nil { return err}packResult := packTask.Await(ctx)if packResult.Err != nil { return packResult.Err}