Stormworks の数値チャンネルの制約
Stormworks のコンポジット数値チャンネルは、値がワイヤ上で IEEE-754 単精度浮動小数点数 (float32) として往復します。Lua の数値自体は倍精度 (float64) ですが、チャンネルを経由すると float32 の精度に丸められます。 float32 の仮数部は 24 bit ぶんの精度を持つため、2^24 (16,777,216) 以下の整数であれば float64 → float32 → float64 と変換しても値が変わりません。Number Codec Compiler はこの性質を利用して、パッキングした状態数がこの範囲に収まるかどうかで、そのまま送るか・ビットを再解釈して送るかを切り替えています。
4 種類のパッキング方式
フィールドをチャンネルへ割り当てたあと、そのチャンネルに詰め込まれた状態数の組み合わせ数に応じて、次の 4 方式のいずれかが自動選択されます。
float32 の
+Infinity のビットパターンは 0x7F800000 (= 2,139,095,040) で、raw の上限 2,139,095,041 はその 1 つ先の値です。2,139,095,040 未満の整数であれば、ビット再解釈しても常に有限かつ NaN でない float32 になります。stable 方式はこの上限を超えても、Infinity/NaN のビットパターン域 (合計で約 839 万状態ぶん) を避けて残りの空間を使うことで、送信中の値が Infinity/NaN にならないようにしています。
backend を "raw32" にすると、この Infinity/NaN 回避を行わない代わりに 2^32 までのフル範囲を 1 チャンネルで使えます。backend の切り替えは UI 上の操作としては用意されておらず、プロジェクト JSON を直接編集するか、HTTP API 経由で指定する必要があります。
フィールドをチャンネルへ割り当てる 2 つの戦略
候補となるチャンネル数k ごとに、次の 2 つの戦略を両方試し、生成される Lua コードの合計文字数が小さい方を採用します。
- フィールド単位のグループ化: 各フィールドをまるごと
k個のチャンネルのいずれかに割り当てます。同じチャンネルに割り当てられたフィールドは、それぞれの状態数を基数とした多基数 (mixed-radix) の掛け算で 1 つの整数にまとめられ、受信側で逆順に%///で取り出されます。フィールド数が 10 以下なら全探索、それを超えると貪欲法で探索します。 - 多基数リム分割:
backendが"pure"で、かつすべてのフィールドの状態数がraw32の上限未満に収まる場合にのみ試されます。パケット全体の状態数 (全フィールドの状態数の積) を 1 つの巨大な整数とみなし、固定の基数 (PURE_CAP = 4,286,578,690) でk個のstable方式チャンネルに均等に分割します。この積は float64 の安全な整数範囲 (2^53) を超えることがあるため、ネイティブの Lua 演算ではなく、桁ごとに計算する手製の乗除算ヘルパーで処理されます。
k は、候補一覧に表示されません。