Skip to content

はじめに

対応 Minecraft Script API バージョン

Kairo は Minecraft の安定版 Script API を必要とします:

  • @minecraft/server 2.0.0 以降
  • @minecraft/server-ui 2.0.0 以降

2.0.0 より前のバージョンは初期化モデルが異なり(例: WorldLoad の代わりに WorldInitialize が使われていた)、サポートされません。

kairo のインストール

kairo ビヘイビアーパックをワールドに追加します。これが他のすべてのアドオン間の通信を仲介します。

kairo は GitHub Releases からダウンロードできます。

kairo はワールドごとの設定を必要としません。同じワールドに複数バージョンの kairo が存在していても、自動的に 1 つのホストが選ばれます。アドオン側のコードは @kairo-js/router だけに依存していればよく、どの kairo パックがホストになっているかを意識する必要はありません。

kairo-router を使う

自分のアドオンに kairo-router を追加することで、他のアドオンと通信できるようになります。

インストール

bash
pnpm add @kairo-js/router @kairo-js/properties

@kairo-js/router はアドオン間通信のための runtime API です。@kairo-js/propertiesrouter.init() に渡す AddonProperties 型と、manifest 生成に使いやすいメタデータ構造を提供します。

また、Kairo には @kairo-js/utils もあります。router を使い始めるだけなら必須ではありませんが、SemVer ユーティリティ、seeded random、JSON parse ヘルパー、TypeBox compile ヘルパーなどの共通処理に利用できます。

startup イベントで API を宣言する

API の提供・フックの登録はすべて router.beforeEvents.startup 内で行います。

typescript
import { router } from '@kairo-js/router'
import type { AddonProperties } from '@kairo-js/properties'
import { properties } from './properties'

router.beforeEvents.startup.subscribe((ev) => {
  // 自アドオンが提供する API を登録
  ev.addonApi.register<{ playerId: string }, { balance: number }>(
    'economy/getBalance',
    async ({ playerId }) => ({ balance: 100 }),
  )
})

router.init(properties)

Minecraft custom component を登録する

Minecraft custom component も router.beforeEvents.startup 内で登録します。startup event に公開されている native registry をそのまま使ってください。

typescript
router.beforeEvents.startup.subscribe((ev) => {
  ev.itemComponentRegistry.registerCustomComponent('my:item_component', {
    onUse(event) {
      console.log(event.source?.name)
    },
  })

  ev.blockComponentRegistry.registerCustomComponent('my:block_component', {
    onPlayerInteract(event) {
      console.log(event.player?.name)
    },
  })
})

API を呼び出す

typescript
// fire-and-forget(返答を待たない)
router.send('economy-addon', 'onTransaction', { amount: 50 })

// 結果を待つ
const result = await router.request<{ balance: number }>(
  'economy-addon',
  'getBalance',
  { playerId: '...' },
)

if ('canceled' in result) {
  console.log(result.reason)
} else {
  console.log(result.balance)
}

詳細は kairo-router API リファレンス を参照してください。

必須 dependencies

アドオンの properties.ts"kairo" を必須 dependency として宣言する必要があります。記述がない場合、router.init() は即座にエラーをスローします。

typescript
import type { AddonProperties } from '@kairo-js/properties'

export const properties: AddonProperties = {
  id: 'my-addon',
  // ...
  dependencies: {
    kairo: '^1.0.0',
    // optional: 'kairo-database': '^2.0.0' — router.save/load/delete/has に必要
  },
}

必須ではない連携先は optionalDependencies に記述します。たとえば router.save()router.load()router.delete()router.has() は、kairo-databasedependencies または optionalDependencies のどちらかに含まれている場合に利用できます。

カスタムコマンド

カスタムコマンドは Minecraft の native registry に直接登録するのではなく、router.beforeEvents.startup 内で ev.customCommandRegistry 経由で登録してください。

Minecraft 本来の仕様では、同じ id のカスタムコマンドを複数登録しようとすると拒否されるため、同じワールドに複数バージョンのアドオンがある場合、どれか 1 つのバージョンしかコマンドを登録できません。kairo-router はこの登録処理をラップし、重複した native 登録を安全にスキップしつつ、実行時には現在 active なアドオンバージョンへコマンドをルーティングします。これにより、ワールド内でバージョンを切り替えても同じコマンド id をスムーズに使えます。

コマンド id と引数の型の並びは互換性契約です。一度リリースしたコマンドについて、同じ command id のまま引数型の順序を変更しないでください。引数名はドキュメント上のメタデータなので変更できます。たとえば target: stringplayer: string に変えるのは互換です。一方で、型の順番を変える、既存引数の前に必須引数を追加する、stringentity に変える、といった変更は非互換です。

非互換なコマンドシグネチャが必要な場合は major バージョンを上げ、以前のシグネチャを持つ低いバージョンをアンインストールする必要があることをユーザーに明示してください。別の command id を新設し、古いコマンドを既存ワールド向けに残す方法もあります。

standalone モード

router.init(){ standalone: true } を渡すと standalone 起動が有効になります。kairo がインストールされていない場合、必須 dependencies が kairokairo-database のみであれば自動的に起動します。kairo のライフサイクル管理をオプションとして利用したい単体アドオンに便利です。

詳細は RouterInitOptions を参照してください。

初期化タイミング

ワールドロード後、kairo は Discovery・Registration・Activation という独自の初期化フローを実行してから addonActivate を発火します。インストールされているアドオン数にもよりますが、概ね 30〜50 tick の遅延が発生します。

この遅延は意図的なものです。Minecraft Script API 2.0.0+ では WorldLoad 完了前に world 系のメソッドを呼ぶとエラーになる仕様がありますが、addonActivate はその代わりとなる安全シグナルです。addonActivate 以降であれば、world 系メソッドの呼び出しやアドオン間 API の利用が可能です。

typescript
router.afterEvents.addonActivate.subscribe(() => {
  // ここから world 系メソッドの呼び出し・他アドオンの API 呼び出しが安全
})

TIP

バニラの Script API アドオンに慣れている方は、addonActivateWorldLoad の代わりとして捉えてください。追加の tick は kairo のハンドシェイクコストであり、無駄な待機ではありません。

Released under the MIT License.