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.
@@ -1348,6 +1348,7 @@ interface MineRenderDataManifest {
1348
1348
  interface ResolvedMineRenderData {
1349
1349
  requestedVersion: string;
1350
1350
  version: string;
1351
+ /** Registry lookup root. Rootless requests still allow registered sources to override this URL. */
1351
1352
  root: string;
1352
1353
  manifest: MineRenderDataManifest;
1353
1354
  }
@@ -1357,7 +1358,8 @@ declare class MineRenderData {
1357
1358
  static get<K extends keyof MineRenderDatasets>(dataset: K, root?: string): Promise<MineRenderDatasets[K]>;
1358
1359
  /**
1359
1360
  * Uses the exact version when published. Otherwise, selects the nearest patch in the same
1360
- * release line (older on ties), then the latest earlier release. Unpublished snapshots reject.
1361
+ * release line (older on ties), then the latest earlier release or the oldest published release.
1362
+ * Unpublished snapshots reject.
1361
1363
  */
1362
1364
  static resolve(root?: string): Promise<ResolvedMineRenderData>;
1363
1365
  private static manifest;
@@ -1394,7 +1396,6 @@ declare class Models {
1394
1396
  static componentValue(components: Record<string, unknown>, id: string): unknown;
1395
1397
  private static snapshotContext;
1396
1398
  private static applyDefaults;
1397
- private static sourceCheck;
1398
1399
  private static contextKey;
1399
1400
  private static snapshotJson;
1400
1401
  private static propertyValue;
@@ -2924,11 +2925,12 @@ interface FlyControlsEventMap {
2924
2925
  type FlyAction = "forward" | "back" | "left" | "right" | "up" | "down" | "sprint";
2925
2926
  /**
2926
2927
  * Creative-flight camera controls: WASD moves horizontally along the view direction, space and shift
2927
- * move up and down, control sprints, and the mouse turns the camera (pointer lock, or drag).
2928
+ * move up and down, R sprints, and the mouse turns the camera (pointer lock, or drag).
2928
2929
  *
2929
2930
  * Call {@link FlyControls.update} with the elapsed seconds once per frame; the `change` event marks the
2930
2931
  * renderer dirty while keys are held. Speeds use scene units (16 per block). The element becomes focusable
2931
- * and keys apply while it has focus or the pointer is locked. The wheel changes the speed, as in vanilla
2932
+ * and keys apply while it has focus or the pointer is locked. A key pressed with Control, Alt, or Command
2933
+ * stays a browser shortcut unless that modifier is bound. The wheel changes the speed, as in vanilla
2932
2934
  * spectator mode. Touch input uses the left half of the element as a joystick and the right half for looking.
2933
2935
  */
2934
2936
  declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
@@ -2960,9 +2962,13 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
2960
2962
  private _position0;
2961
2963
  private _quaternion0;
2962
2964
  private _dragPointer?;
2965
+ private _dragX;
2966
+ private _dragY;
2963
2967
  private _dragMoved;
2968
+ private _dragTravel;
2964
2969
  private _joystick?;
2965
2970
  private _locked;
2971
+ private _madeFocusable;
2966
2972
  private _lastTime?;
2967
2973
  private readonly _onKeyDown;
2968
2974
  private readonly _onKeyUp;
@@ -3000,12 +3006,15 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
3000
3006
  /**
3001
3007
  * Advances movement by `delta` seconds (measured since the previous call when omitted)
3002
3008
  * and dispatches `change` when the camera moved.
3009
+ * A step longer than 0.1 s is applied as 0.1 s.
3003
3010
  */
3004
3011
  update(delta?: number): void;
3005
3012
  private move;
3006
3013
  private notifyChange;
3007
3014
  private releaseKeys;
3008
3015
  private handleKey;
3016
+ /** A key pressed with Control, Alt, or Command stays a browser shortcut unless that modifier is bound to an action. */
3017
+ private isBrowserShortcut;
3009
3018
  private actionFor;
3010
3019
  private handlePointerDown;
3011
3020
  private onJoystickSide;
@@ -3061,6 +3070,7 @@ declare class Renderer implements Disposable {
3061
3070
  /**
3062
3071
  * Replaces renderer-owned controls with the selected mode, keeping the camera where it is.
3063
3072
  * Switching to orbit controls targets the point the camera looks at, at the previous orbit distance.
3073
+ * The previous controls object is disposed, so its listeners and settings do not carry over.
3064
3074
  * Returns the new controls, or undefined when controls were disabled at construction or after disposal.
3065
3075
  */
3066
3076
  setControlsMode(mode: ControlsMode): Maybe<RendererControls>;
@@ -3624,7 +3634,6 @@ declare class AnvilParser {
3624
3634
  * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
3625
3635
  */
3626
3636
  static parseChunk(data: RegionInput, localX: number, localZ: number, options?: AnvilParseOptions): Promise<AnvilChunk | undefined>;
3627
- private static assetGuard;
3628
3637
  /**
3629
3638
  * Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
3630
3639
  * @param localX - Chunk x within the region, from 0 to 31.
@@ -1348,6 +1348,7 @@ interface MineRenderDataManifest {
1348
1348
  interface ResolvedMineRenderData {
1349
1349
  requestedVersion: string;
1350
1350
  version: string;
1351
+ /** Registry lookup root. Rootless requests still allow registered sources to override this URL. */
1351
1352
  root: string;
1352
1353
  manifest: MineRenderDataManifest;
1353
1354
  }
@@ -1357,7 +1358,8 @@ declare class MineRenderData {
1357
1358
  static get<K extends keyof MineRenderDatasets>(dataset: K, root?: string): Promise<MineRenderDatasets[K]>;
1358
1359
  /**
1359
1360
  * Uses the exact version when published. Otherwise, selects the nearest patch in the same
1360
- * release line (older on ties), then the latest earlier release. Unpublished snapshots reject.
1361
+ * release line (older on ties), then the latest earlier release or the oldest published release.
1362
+ * Unpublished snapshots reject.
1361
1363
  */
1362
1364
  static resolve(root?: string): Promise<ResolvedMineRenderData>;
1363
1365
  private static manifest;
@@ -1394,7 +1396,6 @@ declare class Models {
1394
1396
  static componentValue(components: Record<string, unknown>, id: string): unknown;
1395
1397
  private static snapshotContext;
1396
1398
  private static applyDefaults;
1397
- private static sourceCheck;
1398
1399
  private static contextKey;
1399
1400
  private static snapshotJson;
1400
1401
  private static propertyValue;
@@ -2924,11 +2925,12 @@ interface FlyControlsEventMap {
2924
2925
  type FlyAction = "forward" | "back" | "left" | "right" | "up" | "down" | "sprint";
2925
2926
  /**
2926
2927
  * Creative-flight camera controls: WASD moves horizontally along the view direction, space and shift
2927
- * move up and down, control sprints, and the mouse turns the camera (pointer lock, or drag).
2928
+ * move up and down, R sprints, and the mouse turns the camera (pointer lock, or drag).
2928
2929
  *
2929
2930
  * Call {@link FlyControls.update} with the elapsed seconds once per frame; the `change` event marks the
2930
2931
  * renderer dirty while keys are held. Speeds use scene units (16 per block). The element becomes focusable
2931
- * and keys apply while it has focus or the pointer is locked. The wheel changes the speed, as in vanilla
2932
+ * and keys apply while it has focus or the pointer is locked. A key pressed with Control, Alt, or Command
2933
+ * stays a browser shortcut unless that modifier is bound. The wheel changes the speed, as in vanilla
2932
2934
  * spectator mode. Touch input uses the left half of the element as a joystick and the right half for looking.
2933
2935
  */
2934
2936
  declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
@@ -2960,9 +2962,13 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
2960
2962
  private _position0;
2961
2963
  private _quaternion0;
2962
2964
  private _dragPointer?;
2965
+ private _dragX;
2966
+ private _dragY;
2963
2967
  private _dragMoved;
2968
+ private _dragTravel;
2964
2969
  private _joystick?;
2965
2970
  private _locked;
2971
+ private _madeFocusable;
2966
2972
  private _lastTime?;
2967
2973
  private readonly _onKeyDown;
2968
2974
  private readonly _onKeyUp;
@@ -3000,12 +3006,15 @@ declare class FlyControls extends Controls<FlyControlsEventMap, Camera> {
3000
3006
  /**
3001
3007
  * Advances movement by `delta` seconds (measured since the previous call when omitted)
3002
3008
  * and dispatches `change` when the camera moved.
3009
+ * A step longer than 0.1 s is applied as 0.1 s.
3003
3010
  */
3004
3011
  update(delta?: number): void;
3005
3012
  private move;
3006
3013
  private notifyChange;
3007
3014
  private releaseKeys;
3008
3015
  private handleKey;
3016
+ /** A key pressed with Control, Alt, or Command stays a browser shortcut unless that modifier is bound to an action. */
3017
+ private isBrowserShortcut;
3009
3018
  private actionFor;
3010
3019
  private handlePointerDown;
3011
3020
  private onJoystickSide;
@@ -3061,6 +3070,7 @@ declare class Renderer implements Disposable {
3061
3070
  /**
3062
3071
  * Replaces renderer-owned controls with the selected mode, keeping the camera where it is.
3063
3072
  * Switching to orbit controls targets the point the camera looks at, at the previous orbit distance.
3073
+ * The previous controls object is disposed, so its listeners and settings do not carry over.
3064
3074
  * Returns the new controls, or undefined when controls were disabled at construction or after disposal.
3065
3075
  */
3066
3076
  setControlsMode(mode: ControlsMode): Maybe<RendererControls>;
@@ -3624,7 +3634,6 @@ declare class AnvilParser {
3624
3634
  * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
3625
3635
  */
3626
3636
  static parseChunk(data: RegionInput, localX: number, localZ: number, options?: AnvilParseOptions): Promise<AnvilChunk | undefined>;
3627
- private static assetGuard;
3628
3637
  /**
3629
3638
  * Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
3630
3639
  * @param localX - Chunk x within the region, from 0 to 31.
@@ -1663,7 +1663,8 @@ var init_MineRenderData = __esm({
1663
1663
  }
1664
1664
  /**
1665
1665
  * Uses the exact version when published. Otherwise, selects the nearest patch in the same
1666
- * release line (older on ties), then the latest earlier release. Unpublished snapshots reject.
1666
+ * release line (older on ties), then the latest earlier release or the oldest published release.
1667
+ * Unpublished snapshots reject.
1667
1668
  */
1668
1669
  static async resolve(root) {
1669
1670
  const requestRoot = root?.replace(/\/+$/, "");
@@ -1672,11 +1673,11 @@ var init_MineRenderData = __esm({
1672
1673
  const scope = AssetLoader.persistentScope;
1673
1674
  return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
1674
1675
  const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
1675
- let manifest = await this.manifest(root, release(requestedVersion) ? requestedVersion : void 0, requestRoot);
1676
+ let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
1676
1677
  if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
1677
1678
  const requested = release(requestedVersion);
1678
- if (!requested || compare(requested, [1, 16, 5]) < 0) {
1679
- throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release from 1.16.5 onward`);
1679
+ if (!requested) {
1680
+ throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
1680
1681
  }
1681
1682
  const base = root.slice(0, root.lastIndexOf("/"));
1682
1683
  const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
@@ -1686,9 +1687,9 @@ var init_MineRenderData = __esm({
1686
1687
  }
1687
1688
  return value.versions;
1688
1689
  });
1689
- 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));
1690
+ const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
1690
1691
  const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
1691
- 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();
1692
+ 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];
1692
1693
  if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
1693
1694
  const resolvedRoot = `${base}/${selected.name}`;
1694
1695
  manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
@@ -1804,12 +1805,7 @@ var init_BannerPatterns = __esm({
1804
1805
  static async getLayers(value, target, root) {
1805
1806
  if (value === void 0) return [];
1806
1807
  if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
1807
- const activeRoot = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
1808
- const checkSources = () => {
1809
- if (activeRoot !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading banner patterns; retry the request");
1810
- };
1811
1808
  const colors = value.length ? await this.getColors(root) : void 0;
1812
- checkSources();
1813
1809
  return Promise.all(value.slice(0, 16).map(async (layer) => {
1814
1810
  if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
1815
1811
  if (typeof layer.color !== "string" || !Object.prototype.hasOwnProperty.call(colors, layer.color)) throw new Error(`Unsupported dye color ${layer.color}`);
@@ -1819,7 +1815,6 @@ var init_BannerPatterns = __esm({
1819
1815
  const id = this.identifier(pattern2);
1820
1816
  const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
1821
1817
  pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
1822
- checkSources();
1823
1818
  if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
1824
1819
  }
1825
1820
  if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
@@ -2108,30 +2103,24 @@ var init_Models = __esm({
2108
2103
  }).then((r) => r?.files ?? []);
2109
2104
  }
2110
2105
  static async loadAndMerge(key, context = {}) {
2111
- const checkSources = this.sourceCheck();
2112
2106
  const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
2113
2107
  if (!model) {
2114
2108
  return void 0;
2115
2109
  }
2116
2110
  const merged = await ModelMerger.mergeWithParents(model);
2117
- checkSources();
2118
2111
  return merged;
2119
2112
  }
2120
2113
  static async getItemModel(key, context = {}) {
2121
- const checkSources = this.sourceCheck();
2122
2114
  const preview = this.snapshotContext(key, context);
2123
2115
  const dataKey = await this.applyDefaults(key, preview);
2124
- checkSources();
2125
2116
  return this.getPreparedItemModel(key, preview, dataKey);
2126
2117
  }
2127
2118
  static async getPreparedItemModel(key, preview, dataKey) {
2128
- const checkSources = this.sourceCheck();
2129
2119
  const itemId = `${key.namespace}:${key.path}`;
2130
2120
  const itemKey2 = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
2131
2121
  const cacheKey = itemKey2.serialize() + this.contextKey(preview) + dataKey;
2132
2122
  const model = await this.PERSISTENT_CACHE.getOrLoad(`${this.ITEM_CACHE_VERSION}:${AssetLoader.persistentKey(cacheKey)}`, async () => {
2133
2123
  const result = await AssetLoader.getFirst([itemKey2, key], "json" /* JSON */);
2134
- checkSources();
2135
2124
  if (!result) return void 0;
2136
2125
  if (result.key.assetType !== "items") return { ...result.asset, key, itemId, components: preview.components };
2137
2126
  const load = async (selected) => {
@@ -2150,10 +2139,8 @@ var init_Models = __esm({
2150
2139
  return { ...model3, itemId, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
2151
2140
  };
2152
2141
  const model2 = await load(this.selectItemModel(result.asset.model, key, preview));
2153
- checkSources();
2154
2142
  return model2;
2155
2143
  });
2156
- checkSources();
2157
2144
  const restore = (model2) => ({
2158
2145
  ...model2,
2159
2146
  key: Object.assign(new AssetKey("", ""), model2.key),
@@ -2217,22 +2204,14 @@ var init_Models = __esm({
2217
2204
  }
2218
2205
  static async applyDefaults(key, preview) {
2219
2206
  if (key.type !== "item") return "";
2220
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
2221
2207
  const resolved = await MineRenderData.resolve(key.root);
2222
2208
  const defaults = await ItemDefaults.get(`${key.namespace}:${key.path}`, key.root);
2223
2209
  preview.components = { ...defaults, ...preview.components };
2224
2210
  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)) {
2225
2211
  preview.components["minecraft:enchantment_glint_override"] = true;
2226
2212
  }
2227
- if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item defaults; retry the request");
2228
2213
  return `|data:${resolved.root}:${resolved.manifest.datasets.itemDefaults?.sha256}:${resolved.manifest.datasets.potionColors?.sha256}`;
2229
2214
  }
2230
- static sourceCheck() {
2231
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
2232
- return () => {
2233
- if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item models; retry the request");
2234
- };
2235
- }
2236
2215
  static contextKey(context) {
2237
2216
  const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
2238
2217
  return `|${this.ITEM_CACHE_VERSION}:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
@@ -2419,7 +2398,6 @@ var init_Models = __esm({
2419
2398
  * @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
2420
2399
  */
2421
2400
  static async getMerged(key, context = {}) {
2422
- const checkSources = this.sourceCheck();
2423
2401
  if (!key.assetType) {
2424
2402
  key.assetType = "models";
2425
2403
  }
@@ -2428,15 +2406,12 @@ var init_Models = __esm({
2428
2406
  }
2429
2407
  const preview = this.snapshotContext(key, context);
2430
2408
  const dataKey = await this.applyDefaults(key, preview);
2431
- checkSources();
2432
2409
  const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
2433
2410
  const model = await Caching.mergedModelCache.get(keyStr, async () => {
2434
2411
  const raw = key.type === "item" ? await this.getPreparedItemModel(key, preview, dataKey) : await this.getRaw(key);
2435
2412
  const merged = raw ? await ModelMerger.mergeWithParents(raw) : void 0;
2436
- checkSources();
2437
2413
  return merged;
2438
2414
  });
2439
- checkSources();
2440
2415
  return model;
2441
2416
  }
2442
2417
  /** Alias for {@link getMerged}. */
@@ -12584,7 +12559,6 @@ init_AssetKey();
12584
12559
  init_ModelCulling();
12585
12560
  init_FluidGeometry();
12586
12561
  init_MineRenderData();
12587
- init_AssetLoader();
12588
12562
  init_Entities();
12589
12563
  var BlockObject = class _BlockObject extends SceneObject {
12590
12564
  constructor(blockState3, options) {
@@ -12614,19 +12588,12 @@ var BlockObject = class _BlockObject extends SceneObject {
12614
12588
  });
12615
12589
  }
12616
12590
  async init() {
12617
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
12618
- const checkSources = () => {
12619
- if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading a block; retry the request");
12620
- };
12621
12591
  this._fluidRules = await MineRenderData.get("fluids", this.blockState.key?.root);
12622
- checkSources();
12623
12592
  if (this.options.applyDefaultState) {
12624
12593
  this._setState(await BlockStateResolver.defaults(this.blockState));
12625
- checkSources();
12626
12594
  }
12627
12595
  if (this.options.initialState !== void 0) this._setState(this.options.initialState);
12628
12596
  await this.recreateModels();
12629
- checkSources();
12630
12597
  }
12631
12598
  dispose() {
12632
12599
  this.clearModels();
@@ -13474,6 +13441,8 @@ var import_three35 = require("three");
13474
13441
  var VANILLA_FLY_SPEED = 10.89;
13475
13442
  var PITCH_LIMIT = Math.PI / 2;
13476
13443
  var MOVE_EPSILON = 1e-6;
13444
+ var MAX_STEP_SECONDS = 0.1;
13445
+ var CLICK_SLOP_PIXELS = 4;
13477
13446
  var _euler = new import_three35.Euler(0, 0, 0, "YXZ");
13478
13447
  var _forward = new import_three35.Vector3();
13479
13448
  var _right = new import_three35.Vector3();
@@ -13508,7 +13477,8 @@ var FlyControls = class extends import_three35.Controls {
13508
13477
  right: ["KeyD", "ArrowRight"],
13509
13478
  up: ["Space"],
13510
13479
  down: ["ShiftLeft", "ShiftRight"],
13511
- sprint: ["ControlLeft", "ControlRight"]
13480
+ // Not Control: Ctrl+W closes the browser tab on Windows and Linux, and a page cannot block it.
13481
+ sprint: ["KeyR"]
13512
13482
  };
13513
13483
  this._pressed = /* @__PURE__ */ new Set();
13514
13484
  this._velocity = new import_three35.Vector3();
@@ -13516,8 +13486,12 @@ var FlyControls = class extends import_three35.Controls {
13516
13486
  this._lastQuaternion = new import_three35.Quaternion();
13517
13487
  this._position0 = new import_three35.Vector3();
13518
13488
  this._quaternion0 = new import_three35.Quaternion();
13489
+ this._dragX = 0;
13490
+ this._dragY = 0;
13519
13491
  this._dragMoved = false;
13492
+ this._dragTravel = 0;
13520
13493
  this._locked = false;
13494
+ this._madeFocusable = false;
13521
13495
  this._onKeyDown = (event) => this.handleKey(event, true);
13522
13496
  this._onKeyUp = (event) => this.handleKey(event, false);
13523
13497
  this._onPointerDown = (event) => this.handlePointerDown(event);
@@ -13547,7 +13521,10 @@ var FlyControls = class extends import_three35.Controls {
13547
13521
  element.addEventListener("wheel", this._onWheel, { passive: false });
13548
13522
  element.addEventListener("blur", this._onBlur);
13549
13523
  element.style.touchAction = "none";
13550
- if (element.tabIndex < 0) element.tabIndex = 0;
13524
+ if (element.tabIndex < 0) {
13525
+ element.tabIndex = 0;
13526
+ this._madeFocusable = true;
13527
+ }
13551
13528
  }
13552
13529
  disconnect() {
13553
13530
  const element = this.domElement;
@@ -13565,6 +13542,10 @@ var FlyControls = class extends import_three35.Controls {
13565
13542
  element.removeEventListener("wheel", this._onWheel);
13566
13543
  element.removeEventListener("blur", this._onBlur);
13567
13544
  element.style.touchAction = "auto";
13545
+ if (this._madeFocusable) {
13546
+ element.removeAttribute("tabindex");
13547
+ this._madeFocusable = false;
13548
+ }
13568
13549
  this.unlock();
13569
13550
  this._locked = false;
13570
13551
  this.releaseKeys();
@@ -13635,6 +13616,7 @@ var FlyControls = class extends import_three35.Controls {
13635
13616
  /**
13636
13617
  * Advances movement by `delta` seconds (measured since the previous call when omitted)
13637
13618
  * and dispatches `change` when the camera moved.
13619
+ * A step longer than 0.1 s is applied as 0.1 s.
13638
13620
  */
13639
13621
  update(delta) {
13640
13622
  if (delta === void 0) {
@@ -13646,7 +13628,7 @@ var FlyControls = class extends import_three35.Controls {
13646
13628
  this._velocity.set(0, 0, 0);
13647
13629
  return;
13648
13630
  }
13649
- if (delta > 0) this.move(delta);
13631
+ if (delta > 0) this.move(Math.min(delta, MAX_STEP_SECONDS));
13650
13632
  this.notifyChange();
13651
13633
  }
13652
13634
  move(delta) {
@@ -13693,15 +13675,21 @@ var FlyControls = class extends import_three35.Controls {
13693
13675
  this._pressed.clear();
13694
13676
  }
13695
13677
  handleKey(event, down) {
13678
+ if (event.code === "MetaLeft" || event.code === "MetaRight") this.releaseKeys();
13696
13679
  const action = this.actionFor(event.code);
13697
13680
  if (!action) return;
13698
13681
  const ownsInput = this.enabled && (this._locked || event.target === this.domElement);
13699
- if (down && !ownsInput) return;
13682
+ if (down && (!ownsInput || this.isBrowserShortcut(event))) return;
13700
13683
  const pressed = this._pressed.has(action);
13701
13684
  if (down) this._pressed.add(action);
13702
13685
  else this._pressed.delete(action);
13703
13686
  if (ownsInput || this.enabled && pressed) event.preventDefault();
13704
13687
  }
13688
+ /** A key pressed with Control, Alt, or Command stays a browser shortcut unless that modifier is bound to an action. */
13689
+ isBrowserShortcut(event) {
13690
+ const bound = (modifier) => Object.values(this.keys).some((codes) => codes.some((code) => code.startsWith(modifier)));
13691
+ return event.ctrlKey && !bound("Control") || event.altKey && !bound("Alt") || event.metaKey && !bound("Meta");
13692
+ }
13705
13693
  actionFor(code) {
13706
13694
  for (const action in this.keys) {
13707
13695
  if (this.keys[action].includes(code)) return action;
@@ -13721,7 +13709,10 @@ var FlyControls = class extends import_three35.Controls {
13721
13709
  if (this._dragPointer !== void 0) return;
13722
13710
  if (event.pointerType === "mouse" && event.button !== 0 && event.button !== 2) return;
13723
13711
  this._dragPointer = event.pointerId;
13712
+ this._dragX = event.clientX;
13713
+ this._dragY = event.clientY;
13724
13714
  this._dragMoved = false;
13715
+ this._dragTravel = 0;
13725
13716
  element.setPointerCapture?.(event.pointerId);
13726
13717
  }
13727
13718
  onJoystickSide(event) {
@@ -13737,8 +13728,20 @@ var FlyControls = class extends import_three35.Controls {
13737
13728
  return;
13738
13729
  }
13739
13730
  if (!this._locked && event.pointerId !== this._dragPointer) return;
13740
- const dx = event.movementX ?? 0;
13741
- const dy = event.movementY ?? 0;
13731
+ let dx, dy;
13732
+ if (this._locked) {
13733
+ dx = event.movementX ?? 0;
13734
+ dy = event.movementY ?? 0;
13735
+ } else {
13736
+ dx = event.clientX - this._dragX;
13737
+ dy = event.clientY - this._dragY;
13738
+ this._dragX = event.clientX;
13739
+ this._dragY = event.clientY;
13740
+ if (!this._dragMoved) {
13741
+ this._dragTravel += Math.hypot(dx, dy);
13742
+ if (this._dragTravel <= CLICK_SLOP_PIXELS) return;
13743
+ }
13744
+ }
13742
13745
  if (!dx && !dy) return;
13743
13746
  this._dragMoved = true;
13744
13747
  this.turn(dx, dy);
@@ -15690,6 +15693,7 @@ var Renderer = class _Renderer {
15690
15693
  /**
15691
15694
  * Replaces renderer-owned controls with the selected mode, keeping the camera where it is.
15692
15695
  * Switching to orbit controls targets the point the camera looks at, at the previous orbit distance.
15696
+ * The previous controls object is disposed, so its listeners and settings do not carry over.
15693
15697
  * Returns the new controls, or undefined when controls were disabled at construction or after disposal.
15694
15698
  */
15695
15699
  setControlsMode(mode) {
@@ -16539,7 +16543,6 @@ init_util2();
16539
16543
  // src/world/AnvilParser.ts
16540
16544
  init_MineRenderError();
16541
16545
  init_MineRenderData();
16542
- init_AssetLoader();
16543
16546
  init_NBTHelper();
16544
16547
 
16545
16548
  // src/world/ChunkData.ts
@@ -16810,14 +16813,12 @@ var AnvilParser = class {
16810
16813
  /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
16811
16814
  static async parse(data, options = {}) {
16812
16815
  options = { ...options };
16813
- const checkSources = this.assetGuard();
16814
16816
  options.signal?.throwIfAborted();
16815
16817
  const bytes = this.bytes(data);
16816
16818
  const chunks = [];
16817
16819
  for (const location of this.locations(bytes)) {
16818
16820
  chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
16819
16821
  }
16820
- checkSources();
16821
16822
  return { chunks };
16822
16823
  }
16823
16824
  /**
@@ -16828,22 +16829,12 @@ var AnvilParser = class {
16828
16829
  */
16829
16830
  static async parseChunk(data, localX, localZ, options = {}) {
16830
16831
  options = { ...options };
16831
- const checkSources = this.assetGuard();
16832
16832
  options.signal?.throwIfAborted();
16833
16833
  const bytes = this.bytes(data);
16834
16834
  const location = this.location(bytes, localX, localZ);
16835
16835
  const chunk = location ? await this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
16836
- checkSources();
16837
16836
  return chunk;
16838
16837
  }
16839
- static assetGuard() {
16840
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
16841
- return () => {
16842
- if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) {
16843
- throw new MineRenderError("Asset sources changed while parsing Anvil data; retry the request");
16844
- }
16845
- };
16846
- }
16847
16838
  /**
16848
16839
  * Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
16849
16840
  * @param localX - Chunk x within the region, from 0 to 31.
@@ -17564,7 +17555,6 @@ function resolveSavedEntity(entity) {
17564
17555
 
17565
17556
  // src/world/_entities/WorldEntities.ts
17566
17557
  init_BannerPatterns();
17567
- init_AssetLoader();
17568
17558
  var WorldEntities = class {
17569
17559
  constructor(scene) {
17570
17560
  this.scene = scene;
@@ -17572,10 +17562,6 @@ var WorldEntities = class {
17572
17562
  }
17573
17563
  /** Captures column ownership before asynchronous block or asset loading starts. */
17574
17564
  prepare(entities = [], column) {
17575
- const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
17576
- const checkSources = () => {
17577
- if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading saved entities; retry the request");
17578
- };
17579
17565
  const placements = entities.flatMap((entity) => {
17580
17566
  const resolved = resolveSavedEntity(entity);
17581
17567
  if (!resolved) return [];
@@ -17591,17 +17577,13 @@ var WorldEntities = class {
17591
17577
  let object4;
17592
17578
  try {
17593
17579
  if (!current()) return;
17594
- checkSources();
17595
17580
  const models = await Entities.getEntityList();
17596
- checkSources();
17597
17581
  if (!models.includes(placement.key.path) || !current()) return;
17598
17582
  const model = await Entities.getEntity(placement.key, placement.texture, { when: placement.when });
17599
- checkSources();
17600
17583
  if (!model || !current()) return;
17601
17584
  let tints = placement.tints;
17602
17585
  if (placement.woolDye) {
17603
17586
  const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];
17604
- checkSources();
17605
17587
  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);
17606
17588
  tints = { ...tints, wool_color: tint };
17607
17589
  }
@@ -17611,7 +17593,6 @@ var WorldEntities = class {
17611
17593
  object4.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);
17612
17594
  object4.rotation.y = placement.yaw;
17613
17595
  await object4.init();
17614
- checkSources();
17615
17596
  if (!current()) return;
17616
17597
  placement.owner.add(object4);
17617
17598
  this.scene.add(object4);