minerender 2.1.0-alpha.116 → 2.1.0-alpha.118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1346,6 +1346,7 @@ interface MineRenderDataManifest {
1346
1346
  interface ResolvedMineRenderData {
1347
1347
  requestedVersion: string;
1348
1348
  version: string;
1349
+ /** Registry lookup root. Rootless requests still allow registered sources to override this URL. */
1349
1350
  root: string;
1350
1351
  manifest: MineRenderDataManifest;
1351
1352
  }
@@ -1355,7 +1356,8 @@ declare class MineRenderData {
1355
1356
  static get<K extends keyof MineRenderDatasets>(dataset: K, root?: string): Promise<MineRenderDatasets[K]>;
1356
1357
  /**
1357
1358
  * Uses the exact version when published. Otherwise, selects the nearest patch in the same
1358
- * release line (older on ties), then the latest earlier release. Unpublished snapshots reject.
1359
+ * release line (older on ties), then the latest earlier release or the oldest published release.
1360
+ * Unpublished snapshots reject.
1359
1361
  */
1360
1362
  static resolve(root?: string): Promise<ResolvedMineRenderData>;
1361
1363
  private static manifest;
@@ -1392,7 +1394,6 @@ declare class Models {
1392
1394
  static componentValue(components: Record<string, unknown>, id: string): unknown;
1393
1395
  private static snapshotContext;
1394
1396
  private static applyDefaults;
1395
- private static sourceCheck;
1396
1397
  private static contextKey;
1397
1398
  private static snapshotJson;
1398
1399
  private static propertyValue;
@@ -2922,11 +2923,12 @@ interface FlyControlsEventMap {
2922
2923
  type FlyAction = "forward" | "back" | "left" | "right" | "up" | "down" | "sprint";
2923
2924
  /**
2924
2925
  * Creative-flight camera controls: WASD moves horizontally along the view direction, space and shift
2925
- * move up and down, control sprints, and the mouse turns the camera (pointer lock, or drag).
2926
+ * move up and down, R sprints, and the mouse turns the camera (pointer lock, or drag).
2926
2927
  *
2927
2928
  * Call {@link FlyControls.update} with the elapsed seconds once per frame; the `change` event marks the
2928
2929
  * renderer dirty while keys are held. Speeds use scene units (16 per block). The element becomes focusable
2929
- * and keys apply while it has focus or the pointer is locked. The wheel changes the speed, as in vanilla
2930
+ * and keys apply while it has focus or the pointer is locked. A key pressed with Control, Alt, or Command
2931
+ * stays a browser shortcut unless that modifier is bound. The wheel changes the speed, as in vanilla
2930
2932
  * spectator mode. Touch input uses the left half of the element as a joystick and the right half for looking.
2931
2933
  */
2932
2934
  declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
@@ -2958,9 +2960,13 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
2958
2960
  private _position0;
2959
2961
  private _quaternion0;
2960
2962
  private _dragPointer?;
2963
+ private _dragX;
2964
+ private _dragY;
2961
2965
  private _dragMoved;
2966
+ private _dragTravel;
2962
2967
  private _joystick?;
2963
2968
  private _locked;
2969
+ private _madeFocusable;
2964
2970
  private _lastTime?;
2965
2971
  private readonly _onKeyDown;
2966
2972
  private readonly _onKeyUp;
@@ -2998,12 +3004,15 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
2998
3004
  /**
2999
3005
  * Advances movement by `delta` seconds (measured since the previous call when omitted)
3000
3006
  * and dispatches `change` when the camera moved.
3007
+ * A step longer than 0.1 s is applied as 0.1 s.
3001
3008
  */
3002
3009
  update(delta?: number): void;
3003
3010
  private move;
3004
3011
  private notifyChange;
3005
3012
  private releaseKeys;
3006
3013
  private handleKey;
3014
+ /** A key pressed with Control, Alt, or Command stays a browser shortcut unless that modifier is bound to an action. */
3015
+ private isBrowserShortcut;
3007
3016
  private actionFor;
3008
3017
  private handlePointerDown;
3009
3018
  private onJoystickSide;
@@ -3059,6 +3068,7 @@ declare class Renderer implements Disposable {
3059
3068
  /**
3060
3069
  * Replaces renderer-owned controls with the selected mode, keeping the camera where it is.
3061
3070
  * Switching to orbit controls targets the point the camera looks at, at the previous orbit distance.
3071
+ * The previous controls object is disposed, so its listeners and settings do not carry over.
3062
3072
  * Returns the new controls, or undefined when controls were disabled at construction or after disposal.
3063
3073
  */
3064
3074
  setControlsMode(mode: ControlsMode): Maybe<RendererControls>;
@@ -3622,7 +3632,6 @@ declare class AnvilParser {
3622
3632
  * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
3623
3633
  */
3624
3634
  static parseChunk(data: RegionInput, localX: number, localZ: number, options?: AnvilParseOptions): Promise<AnvilChunk | undefined>;
3625
- private static assetGuard;
3626
3635
  /**
3627
3636
  * Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
3628
3637
  * @param localX - Chunk x within the region, from 0 to 31.
@@ -1346,6 +1346,7 @@ interface MineRenderDataManifest {
1346
1346
  interface ResolvedMineRenderData {
1347
1347
  requestedVersion: string;
1348
1348
  version: string;
1349
+ /** Registry lookup root. Rootless requests still allow registered sources to override this URL. */
1349
1350
  root: string;
1350
1351
  manifest: MineRenderDataManifest;
1351
1352
  }
@@ -1355,7 +1356,8 @@ declare class MineRenderData {
1355
1356
  static get<K extends keyof MineRenderDatasets>(dataset: K, root?: string): Promise<MineRenderDatasets[K]>;
1356
1357
  /**
1357
1358
  * Uses the exact version when published. Otherwise, selects the nearest patch in the same
1358
- * release line (older on ties), then the latest earlier release. Unpublished snapshots reject.
1359
+ * release line (older on ties), then the latest earlier release or the oldest published release.
1360
+ * Unpublished snapshots reject.
1359
1361
  */
1360
1362
  static resolve(root?: string): Promise<ResolvedMineRenderData>;
1361
1363
  private static manifest;
@@ -1392,7 +1394,6 @@ declare class Models {
1392
1394
  static componentValue(components: Record<string, unknown>, id: string): unknown;
1393
1395
  private static snapshotContext;
1394
1396
  private static applyDefaults;
1395
- private static sourceCheck;
1396
1397
  private static contextKey;
1397
1398
  private static snapshotJson;
1398
1399
  private static propertyValue;
@@ -2922,11 +2923,12 @@ interface FlyControlsEventMap {
2922
2923
  type FlyAction = "forward" | "back" | "left" | "right" | "up" | "down" | "sprint";
2923
2924
  /**
2924
2925
  * Creative-flight camera controls: WASD moves horizontally along the view direction, space and shift
2925
- * move up and down, control sprints, and the mouse turns the camera (pointer lock, or drag).
2926
+ * move up and down, R sprints, and the mouse turns the camera (pointer lock, or drag).
2926
2927
  *
2927
2928
  * Call {@link FlyControls.update} with the elapsed seconds once per frame; the `change` event marks the
2928
2929
  * renderer dirty while keys are held. Speeds use scene units (16 per block). The element becomes focusable
2929
- * and keys apply while it has focus or the pointer is locked. The wheel changes the speed, as in vanilla
2930
+ * and keys apply while it has focus or the pointer is locked. A key pressed with Control, Alt, or Command
2931
+ * stays a browser shortcut unless that modifier is bound. The wheel changes the speed, as in vanilla
2930
2932
  * spectator mode. Touch input uses the left half of the element as a joystick and the right half for looking.
2931
2933
  */
2932
2934
  declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
@@ -2958,9 +2960,13 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
2958
2960
  private _position0;
2959
2961
  private _quaternion0;
2960
2962
  private _dragPointer?;
2963
+ private _dragX;
2964
+ private _dragY;
2961
2965
  private _dragMoved;
2966
+ private _dragTravel;
2962
2967
  private _joystick?;
2963
2968
  private _locked;
2969
+ private _madeFocusable;
2964
2970
  private _lastTime?;
2965
2971
  private readonly _onKeyDown;
2966
2972
  private readonly _onKeyUp;
@@ -2998,12 +3004,15 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
2998
3004
  /**
2999
3005
  * Advances movement by `delta` seconds (measured since the previous call when omitted)
3000
3006
  * and dispatches `change` when the camera moved.
3007
+ * A step longer than 0.1 s is applied as 0.1 s.
3001
3008
  */
3002
3009
  update(delta?: number): void;
3003
3010
  private move;
3004
3011
  private notifyChange;
3005
3012
  private releaseKeys;
3006
3013
  private handleKey;
3014
+ /** A key pressed with Control, Alt, or Command stays a browser shortcut unless that modifier is bound to an action. */
3015
+ private isBrowserShortcut;
3007
3016
  private actionFor;
3008
3017
  private handlePointerDown;
3009
3018
  private onJoystickSide;
@@ -3059,6 +3068,7 @@ declare class Renderer implements Disposable {
3059
3068
  /**
3060
3069
  * Replaces renderer-owned controls with the selected mode, keeping the camera where it is.
3061
3070
  * Switching to orbit controls targets the point the camera looks at, at the previous orbit distance.
3071
+ * The previous controls object is disposed, so its listeners and settings do not carry over.
3062
3072
  * Returns the new controls, or undefined when controls were disabled at construction or after disposal.
3063
3073
  */
3064
3074
  setControlsMode(mode: ControlsMode): Maybe<RendererControls>;
@@ -3622,7 +3632,6 @@ declare class AnvilParser {
3622
3632
  * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
3623
3633
  */
3624
3634
  static parseChunk(data: RegionInput, localX: number, localZ: number, options?: AnvilParseOptions): Promise<AnvilChunk | undefined>;
3625
- private static assetGuard;
3626
3635
  /**
3627
3636
  * Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
3628
3637
  * @param localX - Chunk x within the region, from 0 to 31.
@@ -1607,7 +1607,8 @@ var init_MineRenderData = __esm({
1607
1607
  }
1608
1608
  /**
1609
1609
  * Uses the exact version when published. Otherwise, selects the nearest patch in the same
1610
- * release line (older on ties), then the latest earlier release. Unpublished snapshots reject.
1610
+ * release line (older on ties), then the latest earlier release or the oldest published release.
1611
+ * Unpublished snapshots reject.
1611
1612
  */
1612
1613
  static async resolve(root) {
1613
1614
  const requestRoot = root?.replace(/\/+$/, "");
@@ -1616,11 +1617,11 @@ var init_MineRenderData = __esm({
1616
1617
  const scope = AssetLoader.persistentScope;
1617
1618
  return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
1618
1619
  const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
1619
- let manifest = await this.manifest(root, release(requestedVersion) ? requestedVersion : void 0, requestRoot);
1620
+ let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
1620
1621
  if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
1621
1622
  const requested = release(requestedVersion);
1622
- if (!requested || compare(requested, [1, 16, 5]) < 0) {
1623
- throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release from 1.16.5 onward`);
1623
+ if (!requested) {
1624
+ throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
1624
1625
  }
1625
1626
  const base = root.slice(0, root.lastIndexOf("/"));
1626
1627
  const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
@@ -1630,9 +1631,9 @@ var init_MineRenderData = __esm({
1630
1631
  }
1631
1632
  return value.versions;
1632
1633
  });
1633
- const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number && compare(entry.number, [1, 16, 5]) >= 0).sort((left, right) => compare(left.number, right.number));
1634
+ const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
1634
1635
  const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
1635
- const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare(left.number, right.number))[0] ?? versions.filter((entry) => compare(entry.number, requested) <= 0).pop();
1636
+ const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare(left.number, right.number))[0] ?? versions.filter((entry) => compare(entry.number, requested) <= 0).pop() ?? versions[0];
1636
1637
  if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
1637
1638
  const resolvedRoot = `${base}/${selected.name}`;
1638
1639
  manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
@@ -1748,12 +1749,7 @@ var init_BannerPatterns = __esm({
1748
1749
  static async getLayers(value, target, root) {
1749
1750
  if (value === void 0) return [];
1750
1751
  if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
1751
- const activeRoot = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
1752
- const checkSources = () => {
1753
- if (activeRoot !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading banner patterns; retry the request");
1754
- };
1755
1752
  const colors = value.length ? await this.getColors(root) : void 0;
1756
- checkSources();
1757
1753
  return Promise.all(value.slice(0, 16).map(async (layer) => {
1758
1754
  if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
1759
1755
  if (typeof layer.color !== "string" || !Object.prototype.hasOwnProperty.call(colors, layer.color)) throw new Error(`Unsupported dye color ${layer.color}`);
@@ -1763,7 +1759,6 @@ var init_BannerPatterns = __esm({
1763
1759
  const id = this.identifier(pattern2);
1764
1760
  const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
1765
1761
  pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
1766
- checkSources();
1767
1762
  if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
1768
1763
  }
1769
1764
  if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
@@ -2048,30 +2043,24 @@ var init_Models = __esm({
2048
2043
  }).then((r) => r?.files ?? []);
2049
2044
  }
2050
2045
  static async loadAndMerge(key, context = {}) {
2051
- const checkSources = this.sourceCheck();
2052
2046
  const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
2053
2047
  if (!model) {
2054
2048
  return void 0;
2055
2049
  }
2056
2050
  const merged = await ModelMerger.mergeWithParents(model);
2057
- checkSources();
2058
2051
  return merged;
2059
2052
  }
2060
2053
  static async getItemModel(key, context = {}) {
2061
- const checkSources = this.sourceCheck();
2062
2054
  const preview = this.snapshotContext(key, context);
2063
2055
  const dataKey = await this.applyDefaults(key, preview);
2064
- checkSources();
2065
2056
  return this.getPreparedItemModel(key, preview, dataKey);
2066
2057
  }
2067
2058
  static async getPreparedItemModel(key, preview, dataKey) {
2068
- const checkSources = this.sourceCheck();
2069
2059
  const itemId = `${key.namespace}:${key.path}`;
2070
2060
  const itemKey2 = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
2071
2061
  const cacheKey = itemKey2.serialize() + this.contextKey(preview) + dataKey;
2072
2062
  const model = await this.PERSISTENT_CACHE.getOrLoad(`${this.ITEM_CACHE_VERSION}:${AssetLoader.persistentKey(cacheKey)}`, async () => {
2073
2063
  const result = await AssetLoader.getFirst([itemKey2, key], "json" /* JSON */);
2074
- checkSources();
2075
2064
  if (!result) return void 0;
2076
2065
  if (result.key.assetType !== "items") return { ...result.asset, key, itemId, components: preview.components };
2077
2066
  const load = async (selected) => {
@@ -2090,10 +2079,8 @@ var init_Models = __esm({
2090
2079
  return { ...model3, itemId, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
2091
2080
  };
2092
2081
  const model2 = await load(this.selectItemModel(result.asset.model, key, preview));
2093
- checkSources();
2094
2082
  return model2;
2095
2083
  });
2096
- checkSources();
2097
2084
  const restore = (model2) => ({
2098
2085
  ...model2,
2099
2086
  key: Object.assign(new AssetKey("", ""), model2.key),
@@ -2157,22 +2144,14 @@ var init_Models = __esm({
2157
2144
  }
2158
2145
  static async applyDefaults(key, preview) {
2159
2146
  if (key.type !== "item") return "";
2160
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
2161
2147
  const resolved = await MineRenderData.resolve(key.root);
2162
2148
  const defaults = await ItemDefaults.get(`${key.namespace}:${key.path}`, key.root);
2163
2149
  preview.components = { ...defaults, ...preview.components };
2164
2150
  if (resolved.manifest.dataVersion < 3337 && key.namespace === DEFAULT_NAMESPACE && ["potion", "splash_potion", "lingering_potion"].includes(key.path) && !Object.prototype.hasOwnProperty.call(preview.components, "minecraft:enchantment_glint_override") && await ItemTints.hasPotionEffects(preview.components["minecraft:potion_contents"], key.root)) {
2165
2151
  preview.components["minecraft:enchantment_glint_override"] = true;
2166
2152
  }
2167
- if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item defaults; retry the request");
2168
2153
  return `|data:${resolved.root}:${resolved.manifest.datasets.itemDefaults?.sha256}:${resolved.manifest.datasets.potionColors?.sha256}`;
2169
2154
  }
2170
- static sourceCheck() {
2171
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
2172
- return () => {
2173
- if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item models; retry the request");
2174
- };
2175
- }
2176
2155
  static contextKey(context) {
2177
2156
  const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
2178
2157
  return `|${this.ITEM_CACHE_VERSION}:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
@@ -2359,7 +2338,6 @@ var init_Models = __esm({
2359
2338
  * @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
2360
2339
  */
2361
2340
  static async getMerged(key, context = {}) {
2362
- const checkSources = this.sourceCheck();
2363
2341
  if (!key.assetType) {
2364
2342
  key.assetType = "models";
2365
2343
  }
@@ -2368,15 +2346,12 @@ var init_Models = __esm({
2368
2346
  }
2369
2347
  const preview = this.snapshotContext(key, context);
2370
2348
  const dataKey = await this.applyDefaults(key, preview);
2371
- checkSources();
2372
2349
  const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
2373
2350
  const model = await Caching.mergedModelCache.get(keyStr, async () => {
2374
2351
  const raw = key.type === "item" ? await this.getPreparedItemModel(key, preview, dataKey) : await this.getRaw(key);
2375
2352
  const merged = raw ? await ModelMerger.mergeWithParents(raw) : void 0;
2376
- checkSources();
2377
2353
  return merged;
2378
2354
  });
2379
- checkSources();
2380
2355
  return model;
2381
2356
  }
2382
2357
  /** Alias for {@link getMerged}. */
@@ -12531,7 +12506,6 @@ init_AssetKey();
12531
12506
  init_ModelCulling();
12532
12507
  init_FluidGeometry();
12533
12508
  init_MineRenderData();
12534
- init_AssetLoader();
12535
12509
  init_Entities();
12536
12510
  var _BlockObject = class _BlockObject extends SceneObject {
12537
12511
  constructor(blockState3, options) {
@@ -12556,19 +12530,12 @@ var _BlockObject = class _BlockObject extends SceneObject {
12556
12530
  this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
12557
12531
  }
12558
12532
  async init() {
12559
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
12560
- const checkSources = () => {
12561
- if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading a block; retry the request");
12562
- };
12563
12533
  this._fluidRules = await MineRenderData.get("fluids", this.blockState.key?.root);
12564
- checkSources();
12565
12534
  if (this.options.applyDefaultState) {
12566
12535
  this._setState(await BlockStateResolver.defaults(this.blockState));
12567
- checkSources();
12568
12536
  }
12569
12537
  if (this.options.initialState !== void 0) this._setState(this.options.initialState);
12570
12538
  await this.recreateModels();
12571
- checkSources();
12572
12539
  }
12573
12540
  dispose() {
12574
12541
  this.clearModels();
@@ -13420,6 +13387,8 @@ var import_three35 = require("three");
13420
13387
  var VANILLA_FLY_SPEED = 10.89;
13421
13388
  var PITCH_LIMIT = Math.PI / 2;
13422
13389
  var MOVE_EPSILON = 1e-6;
13390
+ var MAX_STEP_SECONDS = 0.1;
13391
+ var CLICK_SLOP_PIXELS = 4;
13423
13392
  var _euler = new import_three35.Euler(0, 0, 0, "YXZ");
13424
13393
  var _forward = new import_three35.Vector3();
13425
13394
  var _right = new import_three35.Vector3();
@@ -13454,7 +13423,8 @@ var FlyControls = class extends import_three35.Controls {
13454
13423
  right: ["KeyD", "ArrowRight"],
13455
13424
  up: ["Space"],
13456
13425
  down: ["ShiftLeft", "ShiftRight"],
13457
- sprint: ["ControlLeft", "ControlRight"]
13426
+ // Not Control: Ctrl+W closes the browser tab on Windows and Linux, and a page cannot block it.
13427
+ sprint: ["KeyR"]
13458
13428
  };
13459
13429
  this._pressed = /* @__PURE__ */ new Set();
13460
13430
  this._velocity = new import_three35.Vector3();
@@ -13462,8 +13432,12 @@ var FlyControls = class extends import_three35.Controls {
13462
13432
  this._lastQuaternion = new import_three35.Quaternion();
13463
13433
  this._position0 = new import_three35.Vector3();
13464
13434
  this._quaternion0 = new import_three35.Quaternion();
13435
+ this._dragX = 0;
13436
+ this._dragY = 0;
13465
13437
  this._dragMoved = false;
13438
+ this._dragTravel = 0;
13466
13439
  this._locked = false;
13440
+ this._madeFocusable = false;
13467
13441
  this._onKeyDown = (event) => this.handleKey(event, true);
13468
13442
  this._onKeyUp = (event) => this.handleKey(event, false);
13469
13443
  this._onPointerDown = (event) => this.handlePointerDown(event);
@@ -13493,7 +13467,10 @@ var FlyControls = class extends import_three35.Controls {
13493
13467
  element.addEventListener("wheel", this._onWheel, { passive: false });
13494
13468
  element.addEventListener("blur", this._onBlur);
13495
13469
  element.style.touchAction = "none";
13496
- if (element.tabIndex < 0) element.tabIndex = 0;
13470
+ if (element.tabIndex < 0) {
13471
+ element.tabIndex = 0;
13472
+ this._madeFocusable = true;
13473
+ }
13497
13474
  }
13498
13475
  disconnect() {
13499
13476
  const element = this.domElement;
@@ -13511,6 +13488,10 @@ var FlyControls = class extends import_three35.Controls {
13511
13488
  element.removeEventListener("wheel", this._onWheel);
13512
13489
  element.removeEventListener("blur", this._onBlur);
13513
13490
  element.style.touchAction = "auto";
13491
+ if (this._madeFocusable) {
13492
+ element.removeAttribute("tabindex");
13493
+ this._madeFocusable = false;
13494
+ }
13514
13495
  this.unlock();
13515
13496
  this._locked = false;
13516
13497
  this.releaseKeys();
@@ -13581,6 +13562,7 @@ var FlyControls = class extends import_three35.Controls {
13581
13562
  /**
13582
13563
  * Advances movement by `delta` seconds (measured since the previous call when omitted)
13583
13564
  * and dispatches `change` when the camera moved.
13565
+ * A step longer than 0.1 s is applied as 0.1 s.
13584
13566
  */
13585
13567
  update(delta) {
13586
13568
  if (delta === void 0) {
@@ -13592,7 +13574,7 @@ var FlyControls = class extends import_three35.Controls {
13592
13574
  this._velocity.set(0, 0, 0);
13593
13575
  return;
13594
13576
  }
13595
- if (delta > 0) this.move(delta);
13577
+ if (delta > 0) this.move(Math.min(delta, MAX_STEP_SECONDS));
13596
13578
  this.notifyChange();
13597
13579
  }
13598
13580
  move(delta) {
@@ -13639,15 +13621,21 @@ var FlyControls = class extends import_three35.Controls {
13639
13621
  this._pressed.clear();
13640
13622
  }
13641
13623
  handleKey(event, down) {
13624
+ if (event.code === "MetaLeft" || event.code === "MetaRight") this.releaseKeys();
13642
13625
  const action = this.actionFor(event.code);
13643
13626
  if (!action) return;
13644
13627
  const ownsInput = this.enabled && (this._locked || event.target === this.domElement);
13645
- if (down && !ownsInput) return;
13628
+ if (down && (!ownsInput || this.isBrowserShortcut(event))) return;
13646
13629
  const pressed = this._pressed.has(action);
13647
13630
  if (down) this._pressed.add(action);
13648
13631
  else this._pressed.delete(action);
13649
13632
  if (ownsInput || this.enabled && pressed) event.preventDefault();
13650
13633
  }
13634
+ /** A key pressed with Control, Alt, or Command stays a browser shortcut unless that modifier is bound to an action. */
13635
+ isBrowserShortcut(event) {
13636
+ const bound = (modifier) => Object.values(this.keys).some((codes) => codes.some((code) => code.startsWith(modifier)));
13637
+ return event.ctrlKey && !bound("Control") || event.altKey && !bound("Alt") || event.metaKey && !bound("Meta");
13638
+ }
13651
13639
  actionFor(code) {
13652
13640
  for (const action in this.keys) {
13653
13641
  if (this.keys[action].includes(code)) return action;
@@ -13667,7 +13655,10 @@ var FlyControls = class extends import_three35.Controls {
13667
13655
  if (this._dragPointer !== void 0) return;
13668
13656
  if (event.pointerType === "mouse" && event.button !== 0 && event.button !== 2) return;
13669
13657
  this._dragPointer = event.pointerId;
13658
+ this._dragX = event.clientX;
13659
+ this._dragY = event.clientY;
13670
13660
  this._dragMoved = false;
13661
+ this._dragTravel = 0;
13671
13662
  element.setPointerCapture?.(event.pointerId);
13672
13663
  }
13673
13664
  onJoystickSide(event) {
@@ -13683,8 +13674,20 @@ var FlyControls = class extends import_three35.Controls {
13683
13674
  return;
13684
13675
  }
13685
13676
  if (!this._locked && event.pointerId !== this._dragPointer) return;
13686
- const dx = event.movementX ?? 0;
13687
- const dy = event.movementY ?? 0;
13677
+ let dx, dy;
13678
+ if (this._locked) {
13679
+ dx = event.movementX ?? 0;
13680
+ dy = event.movementY ?? 0;
13681
+ } else {
13682
+ dx = event.clientX - this._dragX;
13683
+ dy = event.clientY - this._dragY;
13684
+ this._dragX = event.clientX;
13685
+ this._dragY = event.clientY;
13686
+ if (!this._dragMoved) {
13687
+ this._dragTravel += Math.hypot(dx, dy);
13688
+ if (this._dragTravel <= CLICK_SLOP_PIXELS) return;
13689
+ }
13690
+ }
13688
13691
  if (!dx && !dy) return;
13689
13692
  this._dragMoved = true;
13690
13693
  this.turn(dx, dy);
@@ -15594,6 +15597,7 @@ var _Renderer = class _Renderer {
15594
15597
  /**
15595
15598
  * Replaces renderer-owned controls with the selected mode, keeping the camera where it is.
15596
15599
  * Switching to orbit controls targets the point the camera looks at, at the previous orbit distance.
15600
+ * The previous controls object is disposed, so its listeners and settings do not carry over.
15597
15601
  * Returns the new controls, or undefined when controls were disabled at construction or after disposal.
15598
15602
  */
15599
15603
  setControlsMode(mode) {
@@ -16483,7 +16487,6 @@ init_util2();
16483
16487
  // src/world/AnvilParser.ts
16484
16488
  init_MineRenderError();
16485
16489
  init_MineRenderData();
16486
- init_AssetLoader();
16487
16490
  init_NBTHelper();
16488
16491
 
16489
16492
  // src/world/ChunkData.ts
@@ -16754,14 +16757,12 @@ var AnvilParser = class {
16754
16757
  /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
16755
16758
  static async parse(data, options = {}) {
16756
16759
  options = { ...options };
16757
- const checkSources = this.assetGuard();
16758
16760
  options.signal?.throwIfAborted();
16759
16761
  const bytes = this.bytes(data);
16760
16762
  const chunks = [];
16761
16763
  for (const location of this.locations(bytes)) {
16762
16764
  chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
16763
16765
  }
16764
- checkSources();
16765
16766
  return { chunks };
16766
16767
  }
16767
16768
  /**
@@ -16772,22 +16773,12 @@ var AnvilParser = class {
16772
16773
  */
16773
16774
  static async parseChunk(data, localX, localZ, options = {}) {
16774
16775
  options = { ...options };
16775
- const checkSources = this.assetGuard();
16776
16776
  options.signal?.throwIfAborted();
16777
16777
  const bytes = this.bytes(data);
16778
16778
  const location = this.location(bytes, localX, localZ);
16779
16779
  const chunk = location ? await this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
16780
- checkSources();
16781
16780
  return chunk;
16782
16781
  }
16783
- static assetGuard() {
16784
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
16785
- return () => {
16786
- if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) {
16787
- throw new MineRenderError("Asset sources changed while parsing Anvil data; retry the request");
16788
- }
16789
- };
16790
- }
16791
16782
  /**
16792
16783
  * Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
16793
16784
  * @param localX - Chunk x within the region, from 0 to 31.
@@ -17508,7 +17499,6 @@ function resolveSavedEntity(entity) {
17508
17499
 
17509
17500
  // src/world/_entities/WorldEntities.ts
17510
17501
  init_BannerPatterns();
17511
- init_AssetLoader();
17512
17502
  var WorldEntities = class {
17513
17503
  constructor(scene) {
17514
17504
  this.scene = scene;
@@ -17516,10 +17506,6 @@ var WorldEntities = class {
17516
17506
  }
17517
17507
  /** Captures column ownership before asynchronous block or asset loading starts. */
17518
17508
  prepare(entities = [], column) {
17519
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
17520
- const checkSources = () => {
17521
- if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading saved entities; retry the request");
17522
- };
17523
17509
  const placements = entities.flatMap((entity) => {
17524
17510
  const resolved = resolveSavedEntity(entity);
17525
17511
  if (!resolved) return [];
@@ -17535,17 +17521,13 @@ var WorldEntities = class {
17535
17521
  let object4;
17536
17522
  try {
17537
17523
  if (!current()) return;
17538
- checkSources();
17539
17524
  const models = await Entities.getEntityList();
17540
- checkSources();
17541
17525
  if (!models.includes(placement.key.path) || !current()) return;
17542
17526
  const model = await Entities.getEntity(placement.key, placement.texture, { when: placement.when });
17543
- checkSources();
17544
17527
  if (!model || !current()) return;
17545
17528
  let tints = placement.tints;
17546
17529
  if (placement.woolDye) {
17547
17530
  const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];
17548
- checkSources();
17549
17531
  const tint = placement.woolDye === "white" ? 15132390 : Math.floor((dye >> 16 & 255) * 0.75) << 16 | Math.floor((dye >> 8 & 255) * 0.75) << 8 | Math.floor((dye & 255) * 0.75);
17550
17532
  tints = { ...tints, wool_color: tint };
17551
17533
  }
@@ -17555,7 +17537,6 @@ var WorldEntities = class {
17555
17537
  object4.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);
17556
17538
  object4.rotation.y = placement.yaw;
17557
17539
  await object4.init();
17558
- checkSources();
17559
17540
  if (!current()) return;
17560
17541
  placement.owner.add(object4);
17561
17542
  this.scene.add(object4);