{
  "camera-grapple.ts": {
    "description": "An editor extension that smoothly flies the camera to a target location using grapple-style movement",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import { Vector3Utils } from '@minecraft/math';",
      "import {",
      "    ActionTypes,",
      "    CursorControlMode,",
      "    EditorInputContext,",
      "    IPlayerUISession,",
      "    KeyboardKey,",
      "    registerEditorExtension,",
      "} from '@minecraft/server-editor';",
      "import { EasingType, TicksPerSecond, Vector3, system } from '@minecraft/server';",
      "interface GrappleStorage {",
      "    latestRunId?: number;",
      "}",
      "type GrappleSession = IPlayerUISession<GrappleStorage>;",
      "function flyCameraToTarget(uiSession: GrappleSession, viewTarget: Vector3, radius: number) {",
      "    if (uiSession.scratchStorage) {",
      "        const player = uiSession.extensionContext.player;",
      "        // This is imperfect and causes a visible pop.  Would be better if we could get the player's exact eye height",
      "        const eyeHeight = Vector3Utils.subtract(player.getHeadLocation(), player.location);",
      "        const viewVector = player.getViewDirection();",
      "        radius = Math.max(radius, 1);",
      "        // FOV in first_person.json is 66 degrees",
      "        const halfFOV = 66 / 2;",
      "        // Compute adjacent side of triangle (distance) when opposite side is radius",
      "        const distanceAway = radius / Math.tan((halfFOV * Math.PI) / 180);",
      "        const destCameraLocation = Vector3Utils.subtract(viewTarget, Vector3Utils.scale(viewVector, distanceAway));",
      "        const destPlayerLocation = Vector3Utils.subtract(destCameraLocation, eyeHeight);",
      "        const easeTimeInSeconds = 1.5;",
      "        // Unhook camera and have it start moving to the new location",
      "        player.camera.setCamera('minecraft:free', {",
      "            rotation: { x: player.getRotation().x, y: player.getRotation().y },",
      "            location: { x: destCameraLocation.x, y: destCameraLocation.y, z: destCameraLocation.z },",
      "            easeOptions: {",
      "                easeTime: easeTimeInSeconds,",
      "                easeType: EasingType.InOutQuad,",
      "            },",
      "        });",
      "        uiSession.scratchStorage.latestRunId = system.runTimeout(() => {",
      "            // Move the player to the final location and re-hook the camera to it",
      "            player.teleport(destPlayerLocation);",
      "            player.camera.clear();",
      "        }, easeTimeInSeconds * TicksPerSecond);",
      "    }",
      "}",
      "/**",
      " * Provides a 'Grapple' extension for quickly moving the player around the world",
      " * @beta",
      " */",
      "export function registerCameraGrapple() {",
      "    registerEditorExtension(",
      "        'camera-grapple-sample',",
      "        (uiSession: GrappleSession) => {",
      "            uiSession.log.debug(`Initializing extension [${uiSession.extensionContext.extensionInfo.name}]`);",
      "            const grappleAction = uiSession.actionManager.createAction({",
      "                actionType: ActionTypes.NoArgsAction,",
      "                onExecute: () => {",
      "                    // don't execute if there is already a command running as this can be visually disorienting",
      "                    if (uiSession.scratchStorage?.latestRunId) {",
      "                        return;",
      "                    }",
      "                    let destBlockLoc: Vector3 | undefined = undefined;",
      "                    const cursor = uiSession.extensionContext.cursor;",
      "                    // Fixed cursor mode will default to the player view direction",
      "                    if (cursor.isVisible && cursor.getProperties().controlMode !== CursorControlMode.Fixed) {",
      "                        destBlockLoc = cursor.getPosition();",
      "                    } else {",
      "                        const result = uiSession.extensionContext.player.getBlockFromViewDirection();",
      "                        if (!result) {",
      "                            uiSession.log.warning('No Block Found.  Aborting Grapple');",
      "                            return;",
      "                        }",
      "                        destBlockLoc = result?.block.location;",
      "                    }",
      "                    // Location of the center of the block",
      "                    const viewTarget = Vector3Utils.add(destBlockLoc, { x: 0.5, y: 0.5, z: 0.5 });",
      "                    flyCameraToTarget(uiSession, viewTarget, 2);",
      "                },",
      "            });",
      "            const frameAction = uiSession.actionManager.createAction({",
      "                actionType: ActionTypes.NoArgsAction,",
      "                onExecute: () => {",
      "                    // don't execute if there is already a command running as this can be visually disorienting",
      "                    if (uiSession.scratchStorage?.latestRunId) {",
      "                        return;",
      "                    }",
      "                    const selection = uiSession.extensionContext.selectionManager.selection;",
      "                    if (selection.isEmpty) {",
      "                        return;",
      "                    }",
      "                    const bounds = selection.getBoundingBox();",
      "                    bounds.max = Vector3Utils.add(bounds.max, { x: 1, y: 1, z: 1 });",
      "                    const halfSize = Vector3Utils.scale(Vector3Utils.subtract(bounds.max, bounds.min), 0.5);",
      "                    const viewTarget = Vector3Utils.add(bounds.min, halfSize);",
      "                    const radius = Math.sqrt(",
      "                        halfSize.x * halfSize.x + halfSize.y * halfSize.y + halfSize.z * halfSize.z",
      "                    );",
      "                    flyCameraToTarget(uiSession, viewTarget, radius);",
      "                },",
      "            });",
      "            uiSession.inputManager.registerKeyBinding(",
      "                EditorInputContext.GlobalToolMode,",
      "                grappleAction,",
      "                { key: KeyboardKey.KEY_G },",
      "                {",
      "                    uniqueId: 'editorSamples:grapple:flyToCursor',",
      "                    label: 'sample.cameragrapple.keyBinding.flyToCursor',",
      "                }",
      "            );",
      "            uiSession.inputManager.registerKeyBinding(",
      "                EditorInputContext.GlobalToolMode,",
      "                frameAction,",
      "                { key: KeyboardKey.KEY_F },",
      "                {",
      "                    uniqueId: 'editorSamples:grapple:flyToSelection',",
      "                    label: 'sample.cameragrapple.keyBinding.flyToSelection',",
      "                }",
      "            );",
      "            return [];",
      "        },",
      "        (uiSession: GrappleSession) => {",
      "            uiSession.log.debug(",
      "                `Shutting down extension [${uiSession.extensionContext.extensionInfo.name}] for player [${uiSession.extensionContext.player.name}]`",
      "            );",
      "            if (uiSession.scratchStorage?.latestRunId) {",
      "                system.clearRun(uiSession.scratchStorage.latestRunId);",
      "                uiSession.scratchStorage.latestRunId = undefined;",
      "            }",
      "        },",
      "        {",
      "            description: 'Camera Grapple Sample Extension',",
      "            notes: 'by Jonas',",
      "        }",
      "    );",
      "}",
      ""
    ]
  },
  "dye-brush.ts": {
    "description": "An editor extension that provides a brush tool for painting blocks with different dye colors",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import {",
      "    ActionTypes,",
      "    ColorPickerPropertyItemVariant,",
      "    CursorTargetMode,",
      "    IDropdownItem,",
      "    IModalTool,",
      "    IObservable,",
      "    IPlayerUISession,",
      "    makeObservable,",
      "    ModalToolLifecycleEventPayload,",
      "    MouseActionType,",
      "    MouseInputType,",
      "    MouseProps,",
      "    registerEditorExtension,",
      "    Selection,",
      "} from '@minecraft/server-editor';",
      "import {",
      "    BlockVolume,",
      "    BoundingBox,",
      "    BoundingBoxUtils,",
      "    RGBA,",
      "    CompoundBlockVolumeAction,",
      "    Dimension,",
      "    Direction,",
      "    EntityColorComponent,",
      "    Player,",
      "    Vector3,",
      "    RGB,",
      "} from '@minecraft/server';",
      "import { Vector3Utils, VECTOR3_UP } from '@minecraft/math';",
      "// Color identifiers expected by EntityColorComponent",
      "enum EntityColor {",
      "    White = 0,",
      "    Orange = 1,",
      "    Magenta = 2,",
      "    LightBlue = 3,",
      "    Yellow = 4,",
      "    LightGreen = 5,",
      "    Pink = 6,",
      "    Gray = 7,",
      "    Silver = 8,",
      "    Cyan = 9,",
      "    Purple = 10,",
      "    Blue = 11,",
      "    Brown = 12,",
      "    Green = 13,",
      "    Red = 14,",
      "    Black = 15,",
      "}",
      "const directionLookup: Record<Direction, Vector3> = {",
      "    [Direction.North]: { x: 0, y: 0, z: 1 },",
      "    [Direction.East]: { x: -1, y: 0, z: 0 },",
      "    [Direction.South]: { x: 0, y: 0, z: -1 },",
      "    [Direction.West]: { x: 1, y: 0, z: 0 },",
      "    [Direction.Up]: { x: 0, y: 1, z: 0 },",
      "    [Direction.Down]: { x: 0, y: -1, z: 0 },",
      "};",
      "const directionToQuadrant: Record<Direction, number> = {",
      "    [Direction.North]: 0,",
      "    [Direction.East]: 1,",
      "    [Direction.South]: 2,",
      "    [Direction.West]: 3,",
      "    [Direction.Up]: 4,",
      "    [Direction.Down]: 5,",
      "};",
      "const quadrantToDirection: Record<number, Direction> = {",
      "    [0]: Direction.North,",
      "    [1]: Direction.East,",
      "    [2]: Direction.South,",
      "    [3]: Direction.West,",
      "    [4]: Direction.Up,",
      "    [5]: Direction.Down,",
      "};",
      "export function getRotationCorrectedDirection(rotationY: number, realDirection: Direction): Direction {",
      "    if (realDirection === Direction.Up || realDirection === Direction.Down) {",
      "        return realDirection;",
      "    }",
      "    const quadrant = directionToQuadrant[realDirection];",
      "    const rotatedQuadrant = Math.floor(((rotationY + 405 + quadrant * 90) % 360) / 90);",
      "    const rotatedDirection = quadrantToDirection[rotatedQuadrant];",
      "    return rotatedDirection;",
      "}",
      "export function getRotationCorrectedDirectionVector(rotationY: number, realDirection: Direction): Vector3 {",
      "    const relativeDirection = getRotationCorrectedDirection(rotationY, realDirection);",
      "    return directionLookup[relativeDirection];",
      "}",
      "// Calculate nearest entity color to an RGBA color",
      "function findClosestColor(targetColor: RGBA, colorPalette: Map<EntityColor, RGB>): EntityColor {",
      "    let minDistance = Number.MAX_VALUE;",
      "    let closestColor: EntityColor = EntityColor.White;",
      "    colorPalette.forEach((paletteColor, color) => {",
      "        const distance = Math.sqrt(",
      "            Math.pow(targetColor.red - paletteColor.red, 2) +",
      "                Math.pow(targetColor.green - paletteColor.green, 2) +",
      "                Math.pow(targetColor.blue - paletteColor.blue, 2)",
      "        );",
      "        if (distance < minDistance) {",
      "            minDistance = distance;",
      "            closestColor = color;",
      "        }",
      "    });",
      "    return closestColor;",
      "}",
      "const colorPalette = new Map<EntityColor, RGB>([",
      "    [EntityColor.White, { red: 1, green: 1, blue: 1 }],",
      "    [EntityColor.Orange, { red: 0.95, green: 0.459, blue: 0 }],",
      "    [EntityColor.Magenta, { red: 0.94, green: 0, blue: 0.9 }],",
      "    [EntityColor.LightBlue, { red: 0, green: 0.85, blue: 0.95 }],",
      "    [EntityColor.Yellow, { red: 0.85, green: 0.95, blue: 0 }],",
      "    [EntityColor.LightGreen, { red: 0, green: 0.95, blue: 0.6 }],",
      "    [EntityColor.Pink, { red: 0.9, green: 0.65, blue: 0.85 }],",
      "    [EntityColor.Gray, { red: 0.6, green: 0.6, blue: 0.6 }],",
      "    [EntityColor.Silver, { red: 0.75, green: 0.75, blue: 0.75 }],",
      "    [EntityColor.Cyan, { red: 0, green: 0.9, blue: 0.9 }],",
      "    [EntityColor.Purple, { red: 0.45, green: 0, blue: 0.9 }],",
      "    [EntityColor.Blue, { red: 0, green: 0, blue: 1 }],",
      "    [EntityColor.Brown, { red: 0.8, green: 0.5, blue: 0.1 }],",
      "    [EntityColor.Green, { red: 0, green: 1, blue: 0 }],",
      "    [EntityColor.Red, { red: 1, green: 0, blue: 0 }],",
      "    [EntityColor.Black, { red: 0, green: 0, blue: 0 }],",
      "]);",
      "interface DyeBrushStorage {",
      "    previewSelection: Selection;",
      "    lastVolumePlaced?: BoundingBox;",
      "    currentColor: EntityColor;",
      "    brushColor: IObservable<RGBA>;",
      "    brushSize: number;",
      "}",
      "type DyeBrushSession = IPlayerUISession<DyeBrushStorage>;",
      "function onColorUpdated(newColor: RGBA, uiSession: DyeBrushSession) {",
      "    if (uiSession.scratchStorage) {",
      "        uiSession.scratchStorage.previewSelection.setFillColor(newColor);",
      "        uiSession.scratchStorage.previewSelection.setOutlineColor({ ...newColor, alpha: 1 });",
      "        const cursorProps = uiSession.extensionContext.cursor.getProperties();",
      "        cursorProps.outlineColor = { ...newColor, alpha: 1 };",
      "        cursorProps.targetMode = CursorTargetMode.Face;",
      "        uiSession.extensionContext.cursor.setProperties(cursorProps);",
      "    }",
      "}",
      "function addDyeBrushPane(uiSession: DyeBrushSession, tool: IModalTool) {",
      "    if (!uiSession.scratchStorage) {",
      "        throw Error('UI Session storage should exist');",
      "    }",
      "    const brushColor = uiSession.scratchStorage.brushColor;",
      "    const brushSize = uiSession.scratchStorage.brushSize;",
      "    const pane = uiSession.createPropertyPane({",
      "        title: 'sample.dyeBrush.pane.title',",
      "    });",
      "    const entityBrush = makeObservable(EntityColor.White);",
      "    onColorUpdated(brushColor.value, uiSession);",
      "    pane.addDropdown(entityBrush, {",
      "        title: 'Brush',",
      "        entries: Object.values(EntityColor).reduce<IDropdownItem[]>((list, dye, index) => {",
      "            if (typeof dye === 'string') {",
      "                list.push({",
      "                    label: dye,",
      "                    value: index,",
      "                });",
      "            }",
      "            return list;",
      "        }, []),",
      "        onChange: (newVal: number) => {",
      "            if (newVal in EntityColor) {",
      "                const foundColor = colorPalette.get(newVal);",
      "                if (foundColor) {",
      "                    brushColor.set({ ...foundColor, alpha: brushColor.value.alpha });",
      "                }",
      "                onColorUpdated(brushColor.value, uiSession);",
      "            }",
      "        },",
      "    });",
      "    pane.addColorPicker(brushColor, {",
      "        variant: ColorPickerPropertyItemVariant.Expanded,",
      "        onChange: (color: RGBA) => {",
      "            entityBrush.set(findClosestColor(color, colorPalette));",
      "            onColorUpdated(brushColor.value, uiSession);",
      "        },",
      "    });",
      "    tool.bindPropertyPane(pane);",
      "    const onExecuteBrush = () => {",
      "        if (uiSession.scratchStorage === undefined) {",
      "            uiSession.log.error('Storage was not initialized.');",
      "            return;",
      "        }",
      "        const previewSelection = uiSession.scratchStorage.previewSelection;",
      "        const player = uiSession.extensionContext.player;",
      "        const targetBlock = player.dimension.getBlock(uiSession.extensionContext.cursor.getPosition());",
      "        if (targetBlock === undefined) {",
      "            return;",
      "        }",
      "        const rotationY = uiSession.extensionContext.player.getRotation().y;",
      "        const directionRight = getRotationCorrectedDirectionVector(rotationY, Direction.East);",
      "        const directionForward = getRotationCorrectedDirectionVector(rotationY, Direction.South);",
      "        const relativeDirection = Vector3Utils.add(Vector3Utils.add(directionRight, directionForward), VECTOR3_UP);",
      "        const sizeHalf = Math.floor(brushSize / 2);",
      "        let fromOffset = Vector3Utils.scale(relativeDirection, -sizeHalf);",
      "        const toOffset = Vector3Utils.scale(relativeDirection, brushSize - 1);",
      "        const isEven = brushSize % 2 === 0;",
      "        if (isEven) {",
      "            fromOffset = Vector3Utils.add(fromOffset, VECTOR3_UP);",
      "        }",
      "        const location = targetBlock.location;",
      "        const from: Vector3 = {",
      "            x: location.x + fromOffset.x,",
      "            y: location.y + fromOffset.y,",
      "            z: location.z + fromOffset.z,",
      "        };",
      "        const to: Vector3 = { x: from.x + toOffset.x, y: from.y + toOffset.y, z: from.z + toOffset.z };",
      "        const blockVolume = new BlockVolume(from, to);",
      "        const bounds = blockVolume.getBoundingBox();",
      "        if (uiSession.scratchStorage.lastVolumePlaced) {",
      "            if (BoundingBoxUtils.equals(uiSession.scratchStorage.lastVolumePlaced, bounds)) {",
      "                return;",
      "            }",
      "        }",
      "        previewSelection.pushVolume({ action: CompoundBlockVolumeAction.Add, volume: blockVolume });",
      "        uiSession.scratchStorage.lastVolumePlaced = bounds;",
      "    };",
      "    const mouseButtonAction = uiSession.actionManager.createAction({",
      "        actionType: ActionTypes.MouseRayCastAction,",
      "        onExecute: (_, mouseProps: MouseProps) => {",
      "            if (uiSession.scratchStorage === undefined) {",
      "                uiSession.log.error('Storage was not initialized.');",
      "                return;",
      "            }",
      "            if (mouseProps.mouseAction === MouseActionType.LeftButton) {",
      "                if (mouseProps.inputType === MouseInputType.ButtonDown) {",
      "                    uiSession.scratchStorage.previewSelection.clear();",
      "                    onExecuteBrush();",
      "                } else if (mouseProps.inputType === MouseInputType.ButtonUp) {",
      "                    const player: Player = uiSession.extensionContext.player;",
      "                    const dimension: Dimension = player.dimension;",
      "                    const iterator = uiSession.scratchStorage.previewSelection.getBlockLocationIterator();",
      "                    for (const pos of iterator) {",
      "                        const entities = dimension.getEntities({ location: pos, closest: 1 });",
      "                        for (const entity of entities) {",
      "                            const colorComp = entity.getComponent('minecraft:color') as EntityColorComponent;",
      "                            if (colorComp) {",
      "                                colorComp.value = entityBrush.value;",
      "                            }",
      "                        }",
      "                    }",
      "                    uiSession.scratchStorage.previewSelection.clear();",
      "                }",
      "            }",
      "        },",
      "    });",
      "    tool.registerMouseButtonBinding(mouseButtonAction);",
      "    const executeBrushRayAction = uiSession.actionManager.createAction({",
      "        actionType: ActionTypes.MouseRayCastAction,",
      "        onExecute: (_, mouseProps: MouseProps) => {",
      "            if (mouseProps.inputType === MouseInputType.Drag) {",
      "                onExecuteBrush();",
      "            }",
      "        },",
      "    });",
      "    tool.registerMouseDragBinding(executeBrushRayAction);",
      "    // Example for adding mouse wheel",
      "    const executeBrushSizeAction = uiSession.actionManager.createAction({",
      "        actionType: ActionTypes.MouseRayCastAction,",
      "        onExecute: (_, mouseProps: MouseProps) => {",
      "            if (mouseProps.mouseAction === MouseActionType.Wheel) {",
      "                if (mouseProps.inputType === MouseInputType.WheelOut) {",
      "                    if (entityBrush.value > 0) {",
      "                        entityBrush.set(entityBrush.value - 1);",
      "                    }",
      "                } else if (mouseProps.inputType === MouseInputType.WheelIn) {",
      "                    if (entityBrush.value < 15) {",
      "                        entityBrush.set(entityBrush.value + 1);",
      "                    }",
      "                }",
      "                onColorUpdated(brushColor.value, uiSession);",
      "            }",
      "        },",
      "    });",
      "    tool.registerMouseWheelBinding(executeBrushSizeAction);",
      "    tool.onModalToolActivation.subscribe((evt: ModalToolLifecycleEventPayload) => {",
      "        if (evt.isActiveTool) {",
      "            onColorUpdated(brushColor.value, uiSession);",
      "        }",
      "        uiSession.scratchStorage?.previewSelection?.clear();",
      "    });",
      "    pane.hide();",
      "}",
      "export function addDyeBrushTool(uiSession: DyeBrushSession) {",
      "    const tool = uiSession.toolRail.addTool({",
      "        title: 'sample.dyebrush.tool.title',",
      "        tooltip: 'sample.dyebrush.tool.tooltip',",
      "        icon: 'pack://textures/dye-brush.png',",
      "    });",
      "    return tool;",
      "}",
      "export function registerDyeBrushExtension() {",
      "    registerEditorExtension<DyeBrushStorage>(",
      "        'dye-brush-sample',",
      "        (uiSession: IPlayerUISession<DyeBrushStorage>) => {",
      "            uiSession.log.debug(`Initializing extension [${uiSession.extensionContext.extensionInfo.name}]`);",
      "            const previewSelection = uiSession.extensionContext.selectionManager.create();",
      "            previewSelection.visible = true;",
      "            const storage: DyeBrushStorage = {",
      "                previewSelection: previewSelection,",
      "                currentColor: EntityColor.White,",
      "                brushColor: makeObservable<RGBA>({ red: 1, green: 1, blue: 1, alpha: 0.5 }),",
      "                brushSize: 4,",
      "            };",
      "            uiSession.scratchStorage = storage;",
      "            const cubeBrushTool = addDyeBrushTool(uiSession);",
      "            addDyeBrushPane(uiSession, cubeBrushTool);",
      "            return [];",
      "        },",
      "        (uiSession: IPlayerUISession<DyeBrushStorage>) => {",
      "            uiSession.log.debug(`Shutting down extension [${uiSession.extensionContext.extensionInfo.name}] `);",
      "        },",
      "        {",
      "            description: 'Dye Brush Sample Extension',",
      "            notes: 'By Eser',",
      "        }",
      "    );",
      "}",
      ""
    ]
  },
  "farm-generator.ts": {
    "description": "An editor extension that generates customizable farms with crops, irrigation, and fencing options",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import { VECTOR3_ZERO } from '@minecraft/math';",
      "import {",
      "  ActionTypes,",
      "  EditorInputContext,",
      "  IModalTool,",
      "  IObservable,",
      "  IPlayerUISession,",
      "  InputModifier,",
      "  KeyboardKey,",
      "  MouseActionType,",
      "  MouseInputType,",
      "  MouseProps,",
      "  Ray,",
      "  makeObservable,",
      "  registerEditorExtension,",
      "} from '@minecraft/server-editor';",
      "import { Player, Vector3 } from '@minecraft/server';",
      "import { MinecraftBlockTypes, MinecraftEntityTypes } from '@minecraft/vanilla-data';",
      "type CommonSettingsType = {",
      "  farmWidth: IObservable<number>;",
      "  farmLength: IObservable<number>;",
      "  irrigation: IObservable<boolean>;",
      "  fenceType: IObservable<number>;",
      "};",
      "type CropSettingsType = {",
      "  wheat: IObservable<boolean>;",
      "  pumpkin: IObservable<boolean>;",
      "  potato: IObservable<boolean>;",
      "  carrot: IObservable<boolean>;",
      "  beetroot: IObservable<boolean>;",
      "};",
      "type AnimalSettingsType = {",
      "  pig: IObservable<boolean>;",
      "  sheep: IObservable<boolean>;",
      "  cow: IObservable<boolean>;",
      "};",
      "function getRandomInt(upper: number) {",
      "  return Math.floor(Math.random() * (upper + 1));",
      "}",
      "function fenceTypeToBlockType(fenceType: number): string {",
      "  switch (fenceType) {",
      "    case 0:",
      "      return MinecraftBlockTypes.OakFence;",
      "    case 1:",
      "      return MinecraftBlockTypes.BirchFence;",
      "    case 2:",
      "      return MinecraftBlockTypes.AcaciaFence;",
      "    case 3:",
      "      return MinecraftBlockTypes.BambooFence;",
      "    case 4:",
      "      return MinecraftBlockTypes.CherryFence;",
      "    case 5:",
      "      return MinecraftBlockTypes.JungleFence;",
      "    case 6:",
      "      return MinecraftBlockTypes.SpruceFence;",
      "    case 7:",
      "      return MinecraftBlockTypes.WarpedFence;",
      "    case 8:",
      "      return MinecraftBlockTypes.CrimsonFence;",
      "    default:",
      "      return MinecraftBlockTypes.OakFence;",
      "  }",
      "}",
      "const buildFarm = (",
      "  targetCorner: Vector3,",
      "  x: number,",
      "  z: number,",
      "  length: number,",
      "  width: number,",
      "  possibleAnimals: MinecraftEntityTypes[],",
      "  possibleCrops: string[],",
      "  player: Player,",
      "  commonSettings: CommonSettingsType",
      ") => {",
      "  let didPlaceAnimal = false;",
      "  for (let i = 0; i < width; i++) {",
      "    for (let j = length - 1; j > -1; j--) {",
      "      const xOffset = i * x;",
      "      const zOffset = z * j;",
      "      const location: Vector3 = { x: targetCorner.x + xOffset, y: targetCorner.y, z: targetCorner.z + zOffset };",
      "      const locationAbove: Vector3 = {",
      "        x: targetCorner.x + xOffset,",
      "        y: targetCorner.y + 1,",
      "        z: targetCorner.z + zOffset,",
      "      };",
      "      const block = player.dimension.getBlock(location);",
      "      const blockAbove = player.dimension.getBlock(locationAbove);",
      "      const isBorder = i === 0 || i === width - 1 || j === 0 || j === length - 1;",
      "      if (xOffset % 3 === 0 && !isBorder && commonSettings.irrigation.value) {",
      "        block?.setType(MinecraftBlockTypes.Water);",
      "      } else {",
      "        block?.setType(MinecraftBlockTypes.Farmland);",
      "      }",
      "      if (isBorder) {",
      "        blockAbove?.setType(fenceTypeToBlockType(commonSettings.fenceType.value));",
      "      } else if (possibleAnimals.length > 0 && getRandomInt(5) === 5) {",
      "        const animal = getRandomInt(possibleAnimals.length - 1);",
      "        const entityType = possibleAnimals[animal];",
      "        player.dimension.spawnEntity(entityType, blockAbove?.location ?? VECTOR3_ZERO, {",
      "          initialPersistence: true,",
      "        });",
      "        didPlaceAnimal = true;",
      "      } else if (!block?.isLiquid && possibleCrops.length > 0) {",
      "        const crop = getRandomInt(possibleCrops.length - 1);",
      "        const blockType = possibleCrops[crop];",
      "        blockAbove?.setType(blockType);",
      "      }",
      "    }",
      "  }",
      "  // Guarantee there is at least one animal spawned if we haven't placed one yet and there is room to place one",
      "  if (!didPlaceAnimal && possibleAnimals.length > 0 && width > 2 && length > 2) {",
      "    const locationAbove: Vector3 = {",
      "      x: targetCorner.x + x,",
      "      y: targetCorner.y + 1,",
      "      z: targetCorner.z + z,",
      "    };",
      "    const blockAbove = player.dimension.getBlock(locationAbove);",
      "    const animal = getRandomInt(possibleAnimals.length - 1);",
      "    const entityType = possibleAnimals[animal];",
      "    player.dimension.spawnEntity(entityType, blockAbove?.location ?? VECTOR3_ZERO, { initialPersistence: true });",
      "  }",
      "};",
      "function addFarmGeneratorSettingsPane(uiSession: IPlayerUISession, tool: IModalTool) {",
      "  const windowPane = uiSession.createPropertyPane({",
      "    title: 'sample.farmgenerator.pane.title',",
      "  });",
      "  const cropPane = windowPane.createSubPane({",
      "    title: 'sample.farmgenerator.pane.crops.title',",
      "  });",
      "  const animalPane = windowPane.createSubPane({",
      "    title: 'sample.farmgenerator.pane.animals.title',",
      "  });",
      "  const commonSettings: CommonSettingsType = {",
      "    farmWidth: makeObservable(10),",
      "    farmLength: makeObservable(10),",
      "    irrigation: makeObservable(false),",
      "    fenceType: makeObservable(0),",
      "  };",
      "  const cropSettings: CropSettingsType = {",
      "    wheat: makeObservable(false),",
      "    pumpkin: makeObservable(false),",
      "    potato: makeObservable(false),",
      "    carrot: makeObservable(false),",
      "    beetroot: makeObservable(false),",
      "  };",
      "  const animalSettings: AnimalSettingsType = {",
      "    pig: makeObservable(false),",
      "    sheep: makeObservable(false),",
      "    cow: makeObservable(false),",
      "  };",
      "  const onExecuteGenerator = (ray?: Ray) => {",
      "    const player: Player = uiSession.extensionContext.player;",
      "    // Use the mouse ray if it is available",
      "    const raycastResult =",
      "      ray !== undefined",
      "        ? player.dimension.getBlockFromRay(ray.location, ray.direction)",
      "        : player.getBlockFromViewDirection();",
      "    if (!raycastResult) {",
      "      uiSession.log.error('No block from view vector');",
      "      return;",
      "    }",
      "    const targetBlock = raycastResult.block;",
      "    let targetCorner: Vector3 = { x: targetBlock.location.x, y: targetBlock.location.y, z: targetBlock.location.z };",
      "    const possibleCrops: string[] = [];",
      "    if (cropSettings.beetroot.value) {",
      "      possibleCrops.push(MinecraftBlockTypes.Beetroot);",
      "    }",
      "    if (cropSettings.carrot.value) {",
      "      possibleCrops.push(MinecraftBlockTypes.Carrots);",
      "    }",
      "    if (cropSettings.pumpkin.value) {",
      "      possibleCrops.push(MinecraftBlockTypes.Pumpkin);",
      "    }",
      "    if (cropSettings.wheat.value) {",
      "      possibleCrops.push(MinecraftBlockTypes.Wheat);",
      "    }",
      "    if (cropSettings.potato.value) {",
      "      possibleCrops.push(MinecraftBlockTypes.Potatoes);",
      "    }",
      "    const possibleAnimals: MinecraftEntityTypes[] = [];",
      "    if (animalSettings.sheep.value) {",
      "      possibleAnimals.push(MinecraftEntityTypes.Sheep);",
      "    }",
      "    if (animalSettings.cow.value) {",
      "      possibleAnimals.push(MinecraftEntityTypes.Cow);",
      "    }",
      "    if (animalSettings.pig.value) {",
      "      possibleAnimals.push(MinecraftEntityTypes.Pig);",
      "    }",
      "    let x = 1;",
      "    let z = 1;",
      "    let length = commonSettings.farmLength.value;",
      "    let width = commonSettings.farmWidth.value;",
      "    if (Math.round(player.getViewDirection().z) === -1) {",
      "      targetCorner = {",
      "        x: targetCorner.x + (commonSettings.farmWidth.value / 2 - 1),",
      "        y: targetCorner.y,",
      "        z: targetCorner.z - (commonSettings.farmLength.value / 2 - 1),",
      "      };",
      "      uiSession.log.info('Facing north');",
      "      x = -1;",
      "    } else if (Math.round(player.getViewDirection().x) === 1) {",
      "      targetCorner = {",
      "        x: targetCorner.x + (commonSettings.farmWidth.value / 2 - 1),",
      "        y: targetCorner.y,",
      "        z: targetCorner.z + (commonSettings.farmLength.value / 2 - 1),",
      "      };",
      "      uiSession.log.info('Facing east');",
      "      length = commonSettings.farmWidth.value;",
      "      width = commonSettings.farmLength.value;",
      "      x = -1;",
      "      z = -1;",
      "    }",
      "    if (Math.round(player.getViewDirection().z) === 1) {",
      "      targetCorner = {",
      "        x: targetCorner.x - (commonSettings.farmWidth.value / 2 - 1),",
      "        y: targetCorner.y,",
      "        z: targetCorner.z + (commonSettings.farmLength.value / 2 - 1),",
      "      };",
      "      uiSession.log.info('Facing south');",
      "      z = -1;",
      "    } else if (Math.round(player.getViewDirection().x) === -1) {",
      "      targetCorner = {",
      "        x: targetCorner.x - (commonSettings.farmWidth.value / 2 - 1),",
      "        y: targetCorner.y,",
      "        z: targetCorner.z - (commonSettings.farmLength.value / 2 - 1),",
      "      };",
      "      uiSession.log.info('Facing west');",
      "      length = commonSettings.farmWidth.value;",
      "      width = commonSettings.farmLength.value;",
      "    }",
      "    buildFarm(targetCorner, x, z, length, width, possibleAnimals, possibleCrops, player, commonSettings);",
      "  };",
      "  // Create an action that will be executed on left mouse click",
      "  const executeMouseAction = uiSession.actionManager.createAction({",
      "    actionType: ActionTypes.MouseRayCastAction,",
      "    onExecute: (mouseRay: Ray, mouseProps: MouseProps) => {",
      "      if (mouseProps.mouseAction === MouseActionType.LeftButton && mouseProps.inputType === MouseInputType.ButtonDown) {",
      "        onExecuteGenerator(mouseRay);",
      "      }",
      "    },",
      "  });",
      "  // Create and an action that will be executed on CTRL + P",
      "  const executeKeyAction = uiSession.actionManager.createAction({",
      "    actionType: ActionTypes.NoArgsAction,",
      "    onExecute: () => {",
      "      onExecuteGenerator();",
      "    },",
      "  });",
      "  // Register actions as input bindings to tool context",
      "  tool.registerKeyBinding(",
      "    executeKeyAction,",
      "    { key: KeyboardKey.KEY_P, modifier: InputModifier.Control },",
      "    {",
      "      uniqueId: 'editorSamples:farmGenerator:place',",
      "      label: 'sample.farmgenerator.keyBinding.place',",
      "    }",
      "  );",
      "  tool.registerMouseButtonBinding(executeMouseAction);",
      "  windowPane.addNumber(commonSettings.farmLength, {",
      "    title: 'sample.farmgenerator.pane.length',",
      "    min: 2,",
      "    max: 20,",
      "    isInteger: true,",
      "  });",
      "  windowPane.addNumber(commonSettings.farmWidth, {",
      "    title: 'sample.farmgenerator.pane.width',",
      "    min: 2,",
      "    max: 20,",
      "    isInteger: true,",
      "  });",
      "  windowPane.addDropdown(commonSettings.fenceType, {",
      "    title: 'sample.farmgenerator.pane.fence',",
      "    enable: true,",
      "    entries: [",
      "      {",
      "        label: 'Oak',",
      "        value: 0,",
      "      },",
      "      {",
      "        label: 'Birch',",
      "        value: 1,",
      "      },",
      "      {",
      "        label: 'Acacia',",
      "        value: 2,",
      "      },",
      "      {",
      "        label: 'Bamboo',",
      "        value: 3,",
      "      },",
      "      {",
      "        label: 'Cherry',",
      "        value: 4,",
      "      },",
      "      {",
      "        label: 'Jungle',",
      "        value: 5,",
      "      },",
      "      {",
      "        label: 'Spruce',",
      "        value: 6,",
      "      },",
      "      {",
      "        label: 'Warped',",
      "        value: 7,",
      "      },",
      "      {",
      "        label: 'Crimson',",
      "        value: 8,",
      "      },",
      "    ],",
      "  });",
      "  windowPane.addBool(commonSettings.irrigation, {",
      "    title: 'sample.farmgenerator.pane.irrigation',",
      "    tooltip: 'sample.farmgenerator.pane.irrigation.tooltip',",
      "  });",
      "  cropPane.addBool(cropSettings.wheat, {",
      "    title: 'sample.farmgenerator.pane.crops.wheat',",
      "  });",
      "  cropPane.addBool(cropSettings.potato, {",
      "    title: 'sample.farmgenerator.pane.crops.potato',",
      "  });",
      "  cropPane.addBool(cropSettings.beetroot, {",
      "    title: 'sample.farmgenerator.pane.crops.beets',",
      "  });",
      "  cropPane.addBool(cropSettings.pumpkin, {",
      "    title: 'sample.farmgenerator.pane.crops.pumpkin',",
      "  });",
      "  cropPane.addBool(cropSettings.carrot, {",
      "    title: 'sample.farmgenerator.pane.crops.carrot',",
      "  });",
      "  animalPane.addBool(animalSettings.cow, {",
      "    title: 'sample.farmgenerator.pane.animals.cow',",
      "  });",
      "  animalPane.addBool(animalSettings.sheep, {",
      "    title: 'sample.farmgenerator.pane.animals.sheep',",
      "  });",
      "  animalPane.addBool(animalSettings.pig, {",
      "    title: 'sample.farmgenerator.pane.animals.pig',",
      "  });",
      "  tool.bindPropertyPane(windowPane);",
      "}",
      "/**",
      " * Create a new tool rail item for farm generator",
      " */",
      "function addFarmGeneratorTool(uiSession: IPlayerUISession) {",
      "  // Create action",
      "  const toolToggleAction = uiSession.actionManager.createAction({",
      "    actionType: ActionTypes.NoArgsAction,",
      "    onExecute: () => {",
      "      uiSession.toolRail.setSelectedToolId(tool.id);",
      "    },",
      "  });",
      "  const tool = uiSession.toolRail.addTool(",
      "    {",
      "      title: 'sample.farmgenerator.tool.title',",
      "      icon: 'pack://textures/farm-generator.png',",
      "      tooltip: 'sample.farmgenerator.tool.tooltip',",
      "      inputContextId: 'editorSamples:farmGenerator',",
      "      inputContextLabel: 'sample.farmgenerator.tool.title',",
      "    },",
      "    toolToggleAction",
      "  );",
      "  // Register a global shortcut (CTRL + SHIFT + P) to select the tool",
      "  uiSession.inputManager.registerKeyBinding(",
      "    EditorInputContext.GlobalToolMode,",
      "    toolToggleAction,",
      "    { key: KeyboardKey.KEY_F, modifier: InputModifier.Control | InputModifier.Shift },",
      "    {",
      "      uniqueId: 'editorSamples:farmGenerator:toggleTool',",
      "      label: 'sample.farmgenerator.keyBinding.toggleTool',",
      "    }",
      "  );",
      "  return tool;",
      "}",
      "/**",
      " * Register Farm Generator extension",
      " */",
      "export function registerFarmGeneratorExtension() {",
      "  registerEditorExtension(",
      "    'FarmGenerator-sample',",
      "    (uiSession: IPlayerUISession) => {",
      "      uiSession.log.debug(`Initializing [${uiSession.extensionContext.extensionInfo.name}] extension`);",
      "      // Add tool to tool rail",
      "      const farmGeneratorTool = addFarmGeneratorTool(uiSession);",
      "      // Create settings pane/window",
      "      addFarmGeneratorSettingsPane(uiSession, farmGeneratorTool);",
      "      return [];",
      "    },",
      "    (uiSession: IPlayerUISession) => {",
      "      uiSession.log.debug(`Initializing [${uiSession.extensionContext.extensionInfo.name}] extension`);",
      "    },",
      "    {",
      "      description: 'Farm Generator Sample Extension',",
      "      notes: 'by Molly',",
      "    }",
      "  );",
      "}",
      ""
    ]
  },
  "goto-mark.ts": {
    "description": "An editor extension that lets you save, name, and teleport to marked locations in the world",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import {",
      "  ActionTypes,",
      "  IButtonPropertyItem,",
      "  IDropdownItem,",
      "  IDropdownPropertyItem,",
      "  IModalTool,",
      "  IObservable,",
      "  IPlayerUISession,",
      "  IPropertyPane,",
      "  UserDefinedTransactionHandle,",
      "  bindDataSource,",
      "  makeObservable,",
      "  registerEditorExtension,",
      "  registerUserDefinedTransactionHandler,",
      "} from '@minecraft/server-editor';",
      "import { Vector3, system } from '@minecraft/server';",
      "const storedLocationDynamicPropertyName = 'goto-mark:storedLocations'; // The key of the stored location dynamic property",
      "const storedLocationNameMaxLength = 16; // This is the maximum length of the name of a stored location",
      "const storedLocationsMax = 9; // The maximum number of stored locations",
      "type GotoTeleportTransactionPayload = {",
      "  current: Vector3;",
      "  destination: Vector3;",
      "};",
      "// The stored location data structure that represents each of the stored locations",
      "// this is also the JSON format that is stored in the dynamic property",
      "type LocationData = {",
      "  location: Vector3;",
      "  name: string;",
      "};",
      "// UI Pane data for the whole extension pane",
      "type ParentPaneDataSourceType = {",
      "  playerLocation: Vector3;",
      "};",
      "// UI Pane data for the sub pane with the stored locations",
      "type LocationPaneDataSourceType = {",
      "  newName: IObservable<string>;",
      "};",
      "// Extension storage data which is pertinent to the the player's context of this extension",
      "type ExtensionStorage = {",
      "  tool?: IModalTool; // The tool handle for the extension",
      "  previousLocation: Vector3; // The players last recorded position",
      "  updateHandle?: number; // The handle for the repeating interval that updates the player position",
      "  parentPaneDataSource?: ParentPaneDataSourceType; // The data source for the parent pane",
      "  parentPane?: IPropertyPane; // The parent pane",
      "  dropdownMenu?: IDropdownPropertyItem; // The dropdown",
      "  locationPaneDataSource?: LocationPaneDataSourceType; // The data source for the location pane",
      "  storedLocations: LocationData[]; // The list of stored locations",
      "  transactionHandler: UserDefinedTransactionHandle<GotoTeleportTransactionPayload>; // The transaction handler for the extension",
      "  teleportButton?: IButtonPropertyItem;",
      "};",
      "// Handy helper to turn a Vector3 into a pretty string",
      "function vector3ToString(vec: Vector3): string {",
      "  return `(${vec.x}, ${vec.y}, ${vec.z})`;",
      "}",
      "// Equality check for a Vector3",
      "function vector3Equals(vec1: Vector3, vec2: Vector3): boolean {",
      "  return vec1.x === vec2.x && vec1.y === vec2.y && vec1.z === vec2.z;",
      "}",
      "// Truncate a Vector3 to the nearest block",
      "function vector3Truncate(vec: Vector3): Vector3 {",
      "  const blockLocation: Vector3 = { x: Math.floor(vec.x), y: Math.floor(vec.y), z: Math.floor(vec.z) };",
      "  return blockLocation;",
      "}",
      "function mapDropdownItems(storage: ExtensionStorage): IDropdownItem[] {",
      "  return storage.storedLocations.map((v, index): IDropdownItem => {",
      "    const item: IDropdownItem = {",
      "      label: `${index + 1}: ${v.name} (${vector3ToString(v.location)})`,",
      "      value: index,",
      "    };",
      "    return item;",
      "  });",
      "}",
      "function createTransaction(uiSession: IPlayerUISession<ExtensionStorage>, current: Vector3, destination: Vector3) {",
      "  const transactionPayload: GotoTeleportTransactionPayload = {",
      "    current,",
      "    destination,",
      "  };",
      "  if (!uiSession.scratchStorage) {",
      "    return;",
      "  }",
      "  uiSession.extensionContext.transactionManager.openTransaction('goto position');",
      "  uiSession.scratchStorage.transactionHandler.addUserDefinedOperation(transactionPayload, 'Goto(Teleport)');",
      "  uiSession.extensionContext.transactionManager.commitOpenTransaction();",
      "}",
      "function teleportTo(uiSession: IPlayerUISession<ExtensionStorage>, destination: Vector3) {",
      "  createTransaction(uiSession, uiSession.extensionContext.player.location, destination);",
      "  uiSession.log.info(`Teleporting to location ${vector3ToString(destination)}`);",
      "  try {",
      "    uiSession.extensionContext.player.teleport(destination);",
      "  } catch (e) {",
      "    uiSession.log.error(`Teleport failed: ${e}`);",
      "  }",
      "}",
      "// Add the extension to the tool rail and give it an icon",
      "function addExtensionTool(uiSession: IPlayerUISession<ExtensionStorage>): IModalTool {",
      "  const tool = uiSession.toolRail.addTool({",
      "    title: 'sample.gotomark.tool.title',",
      "    icon: 'pack://textures/goto-mark.png',",
      "    tooltip: 'Set or Jump to a stored location',",
      "  });",
      "  return tool;",
      "}",
      "function buildParentPane(uiSession: IPlayerUISession<ExtensionStorage>, storage: ExtensionStorage): IPropertyPane {",
      "  const parentPane = uiSession.createPropertyPane({",
      "    title: 'sample.gotomark.pane.title',",
      "  });",
      "  const currentLocation = vector3Truncate(uiSession.extensionContext.player.location);",
      "  const initialPaneData: ParentPaneDataSourceType = {",
      "    playerLocation: currentLocation,",
      "  };",
      "  storage.parentPaneDataSource = bindDataSource(parentPane, initialPaneData);",
      "  storage.previousLocation = currentLocation;",
      "  parentPane.addVector3_deprecated(storage.parentPaneDataSource, 'playerLocation', {",
      "    title: 'sample.gotomark.pane.location',",
      "  });",
      "  // Run interval to refresh coordinate population",
      "  // Issue with refresh on tick rate with show/hide",
      "  storage.updateHandle = system.runInterval(() => {",
      "    if (!storage.parentPaneDataSource) {",
      "      return;",
      "    }",
      "    const currentLocation = vector3Truncate(uiSession.extensionContext.player.location);",
      "    const previousLocation = vector3Truncate(storage.previousLocation);",
      "    // Player hasn't moved - don't refresh",
      "    if (vector3Equals(currentLocation, previousLocation) || !parentPane.visible) {",
      "      return;",
      "    }",
      "    storage.previousLocation = currentLocation;",
      "    storage.parentPaneDataSource.playerLocation = { ...currentLocation };",
      "  }, 10);",
      "  // Jump directly to the location specified in the Vector3 UI control",
      "  parentPane.addButton(",
      "    uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        if (!storage.parentPaneDataSource) {",
      "          uiSession.log.error('An error occurred: No UI pane datasource could be found');",
      "          return;",
      "        }",
      "        const panelLocation = storage.parentPaneDataSource.playerLocation;",
      "        teleportTo(uiSession, panelLocation);",
      "      },",
      "    }),",
      "    {",
      "      title: 'sample.gotomark.pane.button.teleport',",
      "      visible: true,",
      "    }",
      "  );",
      "  parentPane.addDivider();",
      "  // Set the players spawn location based on the current location (or the location typed into the",
      "  // Vector3 UI control)",
      "  parentPane.addButton(",
      "    uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        if (!storage.parentPaneDataSource) {",
      "          uiSession.log.error('An error occurred: No UI pane datasource could be found');",
      "          return;",
      "        }",
      "        const currentLocation = storage.parentPaneDataSource.playerLocation;",
      "        uiSession.log.info(`Setting Spawn Location to ${vector3ToString(currentLocation)}`);",
      "        uiSession.extensionContext.player.setSpawnPoint({",
      "          ...currentLocation,",
      "          dimension: uiSession.extensionContext.player.dimension,",
      "        });",
      "      },",
      "    }),",
      "    {",
      "      title: 'sample.gotomark.pane.button.setspawn',",
      "      visible: true,",
      "    }",
      "  );",
      "  // Jump to the player's spawn location (stored in the entity)",
      "  parentPane.addButton(",
      "    uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        const spawnLocation = uiSession.extensionContext.player.getSpawnPoint();",
      "        if (!spawnLocation) {",
      "          uiSession.log.error('Player Spawn Location is not yet set');",
      "        } else {",
      "          teleportTo(uiSession, spawnLocation);",
      "        }",
      "      },",
      "    }),",
      "    {",
      "      title: 'sample.gotomark.pane.button.gotospawn',",
      "      visible: true,",
      "    }",
      "  );",
      "  storage.parentPane = parentPane;",
      "  // Build/Rebuild a sub-pane with the more dynamic UI controls",
      "  buildLocationPane(uiSession, storage, 0);",
      "  return parentPane;",
      "}",
      "function buildLocationPane(",
      "  uiSession: IPlayerUISession<ExtensionStorage>,",
      "  storage: ExtensionStorage,",
      "  initialSelection: number",
      ") {",
      "  if (!storage.parentPane) {",
      "    uiSession.log.error('An error occurred: No UI pane could be found');",
      "    return;",
      "  }",
      "  const locationPane = storage.parentPane.createSubPane({",
      "    title: 'sample.gotomark.pane.locationpane.title',",
      "  });",
      "  const currentSelection = makeObservable(initialSelection);",
      "  const initialPaneData: LocationPaneDataSourceType = {",
      "    newName: makeObservable(''),",
      "  };",
      "  storage.locationPaneDataSource = bindDataSource(locationPane, initialPaneData);",
      "  const dropdownItems = mapDropdownItems(storage);",
      "  storage.dropdownMenu = locationPane.addDropdown(currentSelection, {",
      "    title: 'sample.gotomark.pane.locationpane.dropdownLabel',",
      "    entries: dropdownItems,",
      "    onChange: (newValue: number) => {",
      "      if (storage.teleportButton) {",
      "        storage.teleportButton.setTitle({",
      "          id: 'sample.gotomark.pane.locationpane.button.teleport',",
      "          props: [`${newValue + 1}`],",
      "        });",
      "      }",
      "    },",
      "  });",
      "  locationPane.addDivider();",
      "  // Jump to the stored location selected in the dropdown",
      "  storage.teleportButton = locationPane.addButton(",
      "    uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        if (!storage.locationPaneDataSource) {",
      "          uiSession.log.error('An error occurred: No UI pane datasource could be found');",
      "          return;",
      "        }",
      "        if (currentSelection.value < 0 || currentSelection.value >= storage.storedLocations.length) {",
      "          uiSession.log.error('No stored locations to delete');",
      "          return;",
      "        }",
      "        const destination = storage.storedLocations[currentSelection.value].location;",
      "        teleportTo(uiSession, destination);",
      "      },",
      "    }),",
      "    {",
      "      title: {",
      "        id: 'sample.gotomark.pane.locationpane.button.teleport',",
      "        props: [dropdownItems.length > 0 ? `${currentSelection.value + 1}` : ''],",
      "      },",
      "      visible: true,",
      "    }",
      "  );",
      "  // Delete the stored location selected in the dropdown",
      "  locationPane.addButton(",
      "    uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        if (!storage.locationPaneDataSource) {",
      "          uiSession.log.error('An error occurred: No UI pane datasource could be found');",
      "          return;",
      "        }",
      "        const selectionValue = currentSelection.value;",
      "        if (selectionValue < 0 || selectionValue >= storage.storedLocations.length) {",
      "          uiSession.log.error('No stored locations to delete');",
      "          return;",
      "        }",
      "        const locationData = storage.storedLocations[selectionValue];",
      "        const locationName = locationData.name;",
      "        uiSession.log.info(`Deleting stored location name '${locationName}'`);",
      "        storage.storedLocations.splice(selectionValue, 1);",
      "        storeLocationsToPlayer(uiSession, storage);",
      "        const dropdownItems = mapDropdownItems(storage);",
      "        const newValue =",
      "          selectionValue >= dropdownItems.length && selectionValue > 0 ? selectionValue - 1 : selectionValue;",
      "        storage.dropdownMenu?.updateEntries(dropdownItems, newValue);",
      "        storage.teleportButton?.setTitle({",
      "          id: 'sample.gotomark.pane.locationpane.button.teleport',",
      "          props: [dropdownItems.length > 0 ? `${newValue + 1}` : ''],",
      "        });",
      "      },",
      "    }),",
      "    {",
      "      title: 'sample.gotomark.pane.locationpane.button.delete',",
      "      visible: true,",
      "    }",
      "  );",
      "  locationPane.addString(storage.locationPaneDataSource.newName, {",
      "    title: 'sample.gotomark.pane.locationpane.input.name',",
      "  });",
      "  locationPane.addButton(",
      "    uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        if (!storage.parentPaneDataSource || !storage.locationPaneDataSource) {",
      "          uiSession.log.error('An error occurred: No UI pane datasource could be found');",
      "          return;",
      "        }",
      "        if (storage.storedLocations.length >= storedLocationsMax) {",
      "          uiSession.log.error(`Cannot store more than ${storedLocationsMax} locations`);",
      "          return;",
      "        }",
      "        const currentLocation = vector3Truncate(storage.parentPaneDataSource.playerLocation);",
      "        const newName = storage.locationPaneDataSource.newName;",
      "        if (!newName.value) {",
      "          newName.set(`Location ${storage.storedLocations.length + 1}`);",
      "        } else {",
      "          const trimmedName = newName.value.trim();",
      "          if (trimmedName.length > storedLocationNameMaxLength) {",
      "            newName.set(trimmedName.substring(0, storedLocationNameMaxLength));",
      "          }",
      "        }",
      "        uiSession.log.info(`Adding Location ${vector3ToString(currentLocation)} as '${newName.value}'`);",
      "        storage.storedLocations.push({",
      "          name: newName.value,",
      "          location: currentLocation,",
      "        });",
      "        storeLocationsToPlayer(uiSession, storage);",
      "        const newSelectionIndex = storage.storedLocations.length - 1;",
      "        const dropdownItems = mapDropdownItems(storage);",
      "        storage.dropdownMenu?.updateEntries(dropdownItems, newSelectionIndex);",
      "        storage.teleportButton?.setTitle({",
      "          id: 'sample.gotomark.pane.locationpane.button.teleport',",
      "          props: [`${newSelectionIndex + 1}`],",
      "        });",
      "      },",
      "    }),",
      "    {",
      "      title: 'sample.gotomark.pane.locationpane.button.store',",
      "    }",
      "  );",
      "}",
      "function storeLocationsToPlayer(uiSession: IPlayerUISession<ExtensionStorage>, storage: ExtensionStorage) {",
      "  const me = uiSession.extensionContext.player;",
      "  me.setDynamicProperty(storedLocationDynamicPropertyName, JSON.stringify(storage.storedLocations));",
      "}",
      "export function registerGotoMarkExtension() {",
      "  registerEditorExtension<ExtensionStorage>(",
      "    'goto-mark-sample',",
      "    (uiSession) => {",
      "      uiSession.log.debug(",
      "        `Initializing extension [${uiSession.extensionContext.extensionInfo.name}] for player [${uiSession.extensionContext.player.name}]`",
      "      );",
      "      const storage: ExtensionStorage = {",
      "        previousLocation: uiSession.extensionContext.player.location,",
      "        storedLocations: [],",
      "        transactionHandler: registerUserDefinedTransactionHandler<GotoTeleportTransactionPayload>(",
      "          uiSession.extensionContext.transactionManager,",
      "          (payload: GotoTeleportTransactionPayload) => {",
      "            // undo handler",
      "            uiSession.log.info(`Teleporting to location ${vector3ToString(payload.current)}`);",
      "            try {",
      "              uiSession.extensionContext.player.teleport(payload.current);",
      "            } catch (e) {",
      "              uiSession.log.error(`Teleport failed: ${e}`);",
      "            }",
      "          },",
      "          (payload: GotoTeleportTransactionPayload) => {",
      "            // redo handler",
      "            uiSession.log.info(`Teleporting to location ${vector3ToString(payload.destination)}`);",
      "            try {",
      "              uiSession.extensionContext.player.teleport(payload.destination);",
      "            } catch (e) {",
      "              uiSession.log.error(`Teleport failed: ${e}`);",
      "            }",
      "          }",
      "        ),",
      "      };",
      "      const me = uiSession.extensionContext.player;",
      "      try {",
      "        const fetchedLocationsString = me.getDynamicProperty(storedLocationDynamicPropertyName) as string;",
      "        if (!fetchedLocationsString) {",
      "          uiSession.log.info('No stored locations found during initialization');",
      "        } else {",
      "          const fetchedLocationsArray = JSON.parse(fetchedLocationsString) as LocationData[];",
      "          if (fetchedLocationsArray) {",
      "            storage.storedLocations = fetchedLocationsArray;",
      "          }",
      "          uiSession.log.info(`Found ${storage.storedLocations.length} stored locations during initialization`);",
      "        }",
      "      } catch (e) {",
      "        uiSession.log.info('No stored locations found during initialization');",
      "      }",
      "      storage.tool = addExtensionTool(uiSession);",
      "      const pane = buildParentPane(uiSession, storage);",
      "      storage.tool.bindPropertyPane(pane);",
      "      uiSession.scratchStorage = storage;",
      "      return [];",
      "    },",
      "    (uiSession: IPlayerUISession<ExtensionStorage>) => {",
      "      uiSession.log.debug(",
      "        `Shutting down extension [${uiSession.extensionContext.extensionInfo.name}] for player [${uiSession.extensionContext.player.name}]`",
      "      );",
      "      if (uiSession.scratchStorage) {",
      "        // If we still have a system interval runner, then shut it down",
      "        if (uiSession.scratchStorage.updateHandle) {",
      "          system.clearRun(uiSession.scratchStorage.updateHandle);",
      "        }",
      "      }",
      "    },",
      "    {",
      "      description: 'Go to Bookmark Sample Extension',",
      "      notes: 'by Chloe, Eser & Dave',",
      "    }",
      "  );",
      "}",
      ""
    ]
  },
  "portal-generator.ts": {
    "description": "An editor extension that generates portal structures at a target location in the world",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import {",
      "  BoolPropertyItemVariant,",
      "  CursorTargetMode,",
      "  IDisposable,",
      "  ImageResourceType,",
      "  IObservable,",
      "  makeObservable,",
      "  NumberPropertyItemVariant,",
      "  Ray,",
      "} from '@minecraft/server-editor';",
      "import {",
      "  IPlayerUISession,",
      "  registerEditorExtension,",
      "  IModalTool,",
      "  ActionTypes,",
      "  MouseProps,",
      "  MouseActionType,",
      "  MouseInputType,",
      "  KeyboardKey,",
      "  InputModifier,",
      "  EditorInputContext,",
      "  IPropertyPane,",
      "} from '@minecraft/server-editor';",
      "import { Vector3 } from '@minecraft/server';",
      "import { MinecraftBlockTypes } from '@minecraft/vanilla-data';",
      "enum PortalType {",
      "  Nether = 0,",
      "  End = 1,",
      "}",
      "enum PortalOrientation {",
      "  X = 0,",
      "  Z = 1,",
      "}",
      "type ExtensionStorage = {",
      "  tool?: IModalTool;",
      "};",
      "type PortalGeneratorSession = IPlayerUISession<ExtensionStorage>;",
      "type PaneSettingsType = {",
      "  portalType: IObservable<number>;",
      "  shouldReplaceFloor: IObservable<boolean>;",
      "};",
      "interface IPortalGenerator {",
      "  set parentPane(value: IPropertyPane);",
      "  subPane(uiSession: PortalGeneratorSession): IPropertyPane | undefined;",
      "  activatePane(uiSession: PortalGeneratorSession): void;",
      "  deactivatePane(): void;",
      "  generatePortal(uiSession: PortalGeneratorSession): void;",
      "}",
      "class PortalGenerator implements IDisposable {",
      "  private _netherPortal: NetherPortal;",
      "  private _endPortal: EndPortal;",
      "  private _activePortal?: IPortalGenerator;",
      "  private _pane?: IPropertyPane;",
      "  private _settings: PaneSettingsType = {",
      "    portalType: makeObservable<number>(PortalType.Nether),",
      "    shouldReplaceFloor: makeObservable(true),",
      "  };",
      "  constructor() {",
      "    this._netherPortal = new NetherPortal();",
      "    this._endPortal = new EndPortal();",
      "  }",
      "  public toolPane(uiSession: PortalGeneratorSession): IPropertyPane | undefined {",
      "    if (!this._pane) {",
      "      uiSession.log.error('Tool pane not initialized');",
      "      return undefined;",
      "    }",
      "    return this._pane;",
      "  }",
      "  initialize(uiSession: PortalGeneratorSession, storage: ExtensionStorage) {",
      "    // Create Action",
      "    const toolToggleAction = uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        uiSession.toolRail.setSelectedToolId(tool.id);",
      "      },",
      "    });",
      "    // Add the extension to the tool rail and give it an icon",
      "    const tool = uiSession.toolRail.addTool(",
      "      {",
      "        title: 'sample.portalgenerator.title',",
      "        icon: 'pack://textures/portal-generator.png',",
      "        tooltip: 'sample.portalgenerator.tooltip',",
      "      },",
      "      toolToggleAction",
      "    );",
      "    // Register a global shortcut (CTRL + SHIFT + P) to select the tool",
      "    uiSession.inputManager.registerKeyBinding(",
      "      EditorInputContext.GlobalToolMode,",
      "      toolToggleAction,",
      "      { key: KeyboardKey.KEY_P, modifier: InputModifier.Control | InputModifier.Shift },",
      "      {",
      "        uniqueId: 'editorSamples:portalGenerator:toggleTool',",
      "        label: 'sample.portalgenerator.keyBinding.toggleTool',",
      "      }",
      "    );",
      "    // Create an action that will be executed on left mouse click",
      "    const executeMouseAction = uiSession.actionManager.createAction({",
      "      actionType: ActionTypes.MouseRayCastAction,",
      "      onExecute: (_mouseRay: Ray, mouseProps: MouseProps) => {",
      "        if (",
      "          mouseProps.mouseAction === MouseActionType.LeftButton &&",
      "          mouseProps.inputType === MouseInputType.ButtonDown &&",
      "          this._activePortal",
      "        ) {",
      "          this._activePortal.generatePortal(uiSession);",
      "        }",
      "      },",
      "    });",
      "    tool.registerMouseButtonBinding(executeMouseAction);",
      "    storage.tool = tool;",
      "    // Build the UI components (and the sub pane with the options)",
      "    this.buildPane(uiSession);",
      "    if (this._pane) {",
      "      tool.bindPropertyPane(this._pane);",
      "      this.activatePortalGenerator(uiSession, this._settings.portalType.value);",
      "    }",
      "  }",
      "  teardown(): void {}",
      "  buildPane(uiSession: PortalGeneratorSession) {",
      "    const pane = uiSession.createPropertyPane({",
      "      title: 'sample.portalgenerator.pane.title',",
      "    });",
      "    pane.addBool(this._settings.shouldReplaceFloor, {",
      "      title: 'sample.portalgenerator.pane.replacefloor',",
      "      onChange: (current: boolean) => {",
      "        const targetMode = current ? CursorTargetMode.Block : CursorTargetMode.Face;",
      "        uiSession.extensionContext.cursor.setProperties({ targetMode });",
      "      },",
      "      variant: BoolPropertyItemVariant.ToggleSwitch,",
      "    });",
      "    pane.addDropdown(this._settings.portalType, {",
      "      title: 'sample.portalgenerator.pane.portaltype',",
      "      entries: [",
      "        {",
      "          label: 'sample.portalgenerator.pane.portaltype.nether',",
      "          value: PortalType.Nether,",
      "          imageData: { path: 'portal', type: ImageResourceType.Block },",
      "        },",
      "        {",
      "          label: 'sample.portalgenerator.pane.portaltype.end',",
      "          value: PortalType.End,",
      "          imageData: { path: 'enderEyeIcon', type: ImageResourceType.Icon },",
      "        },",
      "      ],",
      "      onChange: (newValue: number) => {",
      "        this.activatePortalGenerator(uiSession, newValue);",
      "      },",
      "    });",
      "    this._pane = pane;",
      "    this._endPortal.parentPane = pane;",
      "    this._netherPortal.parentPane = pane;",
      "  }",
      "  activatePortalGenerator(uiSession: PortalGeneratorSession, portalType: PortalType): void {",
      "    this._pane?.hide();",
      "    if (this._activePortal) {",
      "      this._activePortal.deactivatePane();",
      "    }",
      "    if (portalType === PortalType.Nether) {",
      "      this._activePortal = this._netherPortal;",
      "    } else {",
      "      this._activePortal = this._endPortal;",
      "    }",
      "    this._activePortal.activatePane(uiSession);",
      "    this._pane?.show();",
      "  }",
      "}",
      "class NetherPortal implements IPortalGenerator {",
      "  private _pane?: IPropertyPane;",
      "  private _parentPane?: IPropertyPane;",
      "  // Settings",
      "  private _sizeX: IObservable<number> = makeObservable(4);",
      "  private _sizeY: IObservable<number> = makeObservable(5);",
      "  private _percentComplete: IObservable<number> = makeObservable(100);",
      "  private _orientation: IObservable<number> = makeObservable(PortalOrientation.X);",
      "  private _hasCorners: IObservable<boolean> = makeObservable(true);",
      "  constructor() {}",
      "  public subPane(uiSession: PortalGeneratorSession): IPropertyPane | undefined {",
      "    if (!this._pane) {",
      "      uiSession.log.error('Sub pane not initialized');",
      "      return undefined;",
      "    }",
      "    return this._pane;",
      "  }",
      "  public set parentPane(value: IPropertyPane) {",
      "    this._parentPane = value;",
      "  }",
      "  activatePane(uiSession: PortalGeneratorSession): void {",
      "    if (this._pane) {",
      "      this.deactivatePane();",
      "    }",
      "    this._pane = this.buildSubPane(uiSession);",
      "    this._pane?.show();",
      "  }",
      "  deactivatePane(): void {",
      "    if (this._pane) {",
      "      this._pane.hide();",
      "      this._parentPane?.removeSubPane(this._pane);",
      "    }",
      "    this._pane = undefined;",
      "  }",
      "  buildSubPane(uiSession: PortalGeneratorSession): IPropertyPane | undefined {",
      "    const windowPane = this._parentPane;",
      "    if (!windowPane) {",
      "      uiSession.log.error('Failed to find window binding');",
      "      return undefined;",
      "    }",
      "    const subPane = windowPane.createSubPane({",
      "      title: 'sample.portalgenerator.pane.nether.pane.title',",
      "    });",
      "    subPane.addDropdown(this._orientation, {",
      "      title: 'sample.portalgenerator.pane.nether.pane.orientation',",
      "      entries: [",
      "        {",
      "          label: 'sample.portalgenerator.pane.nether.pane.orientation.x',",
      "          value: PortalOrientation.X,",
      "        },",
      "        {",
      "          label: 'sample.portalgenerator.pane.nether.pane.orientation.y',",
      "          value: PortalOrientation.Z,",
      "        },",
      "      ],",
      "    });",
      "    subPane.addNumber(this._sizeX, {",
      "      title: 'sample.portalgenerator.pane.nether.pane.width',",
      "      min: 4,",
      "      max: 33,",
      "      isInteger: true,",
      "    });",
      "    subPane.addNumber(this._sizeY, {",
      "      title: 'sample.portalgenerator.pane.nether.pane.height',",
      "      min: 5,",
      "      max: 33,",
      "      isInteger: true,",
      "    });",
      "    subPane.addBool(this._hasCorners, {",
      "      title: 'sample.portalgenerator.pane.nether.pane.corners',",
      "      tooltip: 'sample.portalgenerator.pane.nether.pane.corners.tooltip',",
      "    });",
      "    subPane.addNumber(this._percentComplete, {",
      "      title: 'sample.portalgenerator.pane.nether.pane.percentage',",
      "      min: 0,",
      "      max: 100,",
      "      variant: NumberPropertyItemVariant.InputFieldAndSlider,",
      "    });",
      "    return subPane;",
      "  }",
      "  generatePortal(uiSession: PortalGeneratorSession): void {",
      "    const me = uiSession.extensionContext.player;",
      "    const location = uiSession.extensionContext.cursor.getPosition();",
      "    const targetBlock = me.dimension.getBlock(location);",
      "    if (targetBlock === undefined) {",
      "      uiSession.log.warning('No block selected');",
      "      return;",
      "    }",
      "    if (this._percentComplete.value === 0) {",
      "      return;",
      "    }",
      "    if (me.dimension.id.endsWith('the_end')) {",
      "      uiSession.log.warning('You cannot create a nether portal in the end');",
      "      return;",
      "    }",
      "    uiSession.extensionContext.transactionManager.openTransaction('Transaction group portal generator');",
      "    let from: Vector3 = location;",
      "    let to: Vector3 = { x: 0, y: 0, z: 0 };",
      "    if (this._orientation.value === PortalOrientation.X) {",
      "      to = {",
      "        x: location.x + this._sizeX.value,",
      "        y: location.y + this._sizeY.value,",
      "        z: location.z,",
      "      };",
      "    } else if (this._orientation.value === PortalOrientation.Z) {",
      "      to = {",
      "        x: location.x,",
      "        y: location.y + this._sizeY.value,",
      "        z: location.z + this._sizeX.value,",
      "      };",
      "    } else {",
      "      uiSession.log.error('Failed to get valid orientation');",
      "      uiSession.extensionContext.transactionManager.discardOpenTransaction();",
      "      return;",
      "    }",
      "    const yEnd = this._sizeY.value - 1;",
      "    const xEnd = this._sizeX.value - 1;",
      "    uiSession.extensionContext.transactionManager.trackBlockChangeArea(from, to);",
      "    for (let y = 0; y < this._sizeY.value; ++y) {",
      "      for (let x = 0; x < this._sizeX.value; ++x) {",
      "        let block = MinecraftBlockTypes.Air;",
      "        // Percent complete is randomized percentage",
      "        if (this._percentComplete.value !== 100) {",
      "          const randVal = getRandomInt(100);",
      "          if (this._percentComplete.value - randVal < 0) {",
      "            continue;",
      "          }",
      "        }",
      "        // Set as obsidian for bottom, top, and edges of portal",
      "        if (",
      "          !this._hasCorners.value &&",
      "          ((y === 0 && x === 0) || (y === 0 && x === xEnd) || (y === yEnd && x === xEnd) || (y === yEnd && x === 0))",
      "        ) {",
      "          continue; // no corners",
      "        } else if (y === 0 || y === yEnd || x === 0 || x === xEnd) {",
      "          block = MinecraftBlockTypes.Obsidian;",
      "        } else {",
      "          continue;",
      "        }",
      "        const loc: Vector3 =",
      "          this._orientation.value === PortalOrientation.X",
      "            ? { x: location.x + x, y: location.y + y, z: location.z }",
      "            : { x: location.x, y: location.y + y, z: location.z + x };",
      "        me.dimension.getBlock(loc)?.setType(block);",
      "      }",
      "    }",
      "    let ori = 'x';",
      "    if (this._orientation.value === PortalOrientation.Z) {",
      "      ori = 'z';",
      "      from = { x: location.x, y: location.y + 1, z: location.z + 1 };",
      "      to = {",
      "        x: location.x,",
      "        y: location.y + this._sizeY.value - 2,",
      "        z: location.z + this._sizeX.value - 2,",
      "      };",
      "    } else {",
      "      from = { x: location.x + 1, y: location.y + 1, z: location.z };",
      "      to = {",
      "        x: location.x + this._sizeX.value - 2,",
      "        y: location.y + this._sizeY.value - 2,",
      "        z: location.z,",
      "      };",
      "    }",
      "    if (this._percentComplete.value === 100) {",
      "      // We must fill the portals as it must have the axis set while setting the type",
      "      // or the engine will destroy the block and the scripting API wont allow both in one operation",
      "      me.dimension.runCommand(",
      "        `FILL ${from.x} ${from.y} ${from.z} ${to.x} ${to.y} ${to.z} portal ['portal_axis':'${ori}']`",
      "      );",
      "    }",
      "    uiSession.extensionContext.transactionManager.commitOpenTransaction();",
      "  }",
      "}",
      "class EndPortal implements IPortalGenerator {",
      "  private _pane?: IPropertyPane;",
      "  private _parentPane?: IPropertyPane;",
      "  // Settings",
      "  private _filledEyeCount: IObservable<number> = makeObservable(12);",
      "  constructor() {}",
      "  public subPane(uiSession: PortalGeneratorSession): IPropertyPane | undefined {",
      "    if (!this._pane) {",
      "      uiSession.log.error('Sub pane not initialized');",
      "      return undefined;",
      "    }",
      "    return this._pane;",
      "  }",
      "  public set parentPane(value: IPropertyPane) {",
      "    this._parentPane = value;",
      "  }",
      "  activatePane(uiSession: PortalGeneratorSession): void {",
      "    if (this._pane) {",
      "      this.deactivatePane();",
      "    }",
      "    this._pane = this.buildSubPane(uiSession);",
      "    this._pane?.show();",
      "  }",
      "  deactivatePane(): void {",
      "    if (this._pane) {",
      "      this._pane.hide();",
      "      this._parentPane?.removeSubPane(this._pane);",
      "    }",
      "    this._pane = undefined;",
      "  }",
      "  buildSubPane(uiSession: PortalGeneratorSession): IPropertyPane | undefined {",
      "    const windowPane = this._parentPane;",
      "    if (!windowPane) {",
      "      uiSession.log.error('Failed to find window pane');",
      "      return undefined;",
      "    }",
      "    const subPane = windowPane.createSubPane({",
      "      title: 'sample.portalgenerator.pane.end.pane.title',",
      "    });",
      "    subPane.addNumber(this._filledEyeCount, {",
      "      title: 'sample.portalgenerator.pane.end.pane.filledcount',",
      "      min: 0,",
      "      max: 12,",
      "      variant: NumberPropertyItemVariant.InputFieldAndSlider,",
      "      isInteger: true,",
      "    });",
      "    return subPane;",
      "  }",
      "  generatePortal(uiSession: PortalGeneratorSession): void {",
      "    const me = uiSession.extensionContext.player;",
      "    const location = uiSession.extensionContext.cursor.getPosition();",
      "    const targetBlock = me.dimension.getBlock(location);",
      "    if (targetBlock === undefined) {",
      "      uiSession.log.error('No block selected');",
      "      return;",
      "    }",
      "    uiSession.extensionContext.transactionManager.openTransaction('Transaction group portal generator');",
      "    const from: Vector3 = { x: location.x, y: location.y, z: location.z };",
      "    const to: Vector3 = { x: location.x + 4, y: location.y, z: location.z + 4 };",
      "    let eyesToUse: boolean[] = [false, false, false, false, false, false, false, false, false, false, false, false];",
      "    if (this._filledEyeCount.value === 12) {",
      "      eyesToUse = [true, true, true, true, true, true, true, true, true, true, true, true];",
      "    } else if (this._filledEyeCount.value !== 0) {",
      "      const possibleEyeLocs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];",
      "      for (let i = 0; i < this._filledEyeCount.value; ++i) {",
      "        const rand = Math.floor(Math.random() * possibleEyeLocs.length);",
      "        eyesToUse[possibleEyeLocs[rand]] = true;",
      "        possibleEyeLocs.splice(rand, 1);",
      "      }",
      "    }",
      "    let i = 0;",
      "    uiSession.extensionContext.transactionManager.trackBlockChangeArea(from, to);",
      "    for (let z = 0; z < 5; ++z) {",
      "      for (let x = 0; x < 5; ++x) {",
      "        let rot = 0;",
      "        let blockType = MinecraftBlockTypes.Air;",
      "        if (x === 0 && z !== 0 && z !== 4) {",
      "          // west edge",
      "          blockType = MinecraftBlockTypes.EndPortalFrame;",
      "          rot = 3;",
      "        } else if (x === 4 && z !== 0 && z !== 4) {",
      "          // east edge",
      "          blockType = MinecraftBlockTypes.EndPortalFrame;",
      "          rot = 1;",
      "        } else if (z === 0 && x !== 0 && x !== 4) {",
      "          // south edge",
      "          blockType = MinecraftBlockTypes.EndPortalFrame;",
      "          rot = 0;",
      "        } else if (z === 4 && x !== 0 && x !== 4) {",
      "          // north edge",
      "          blockType = MinecraftBlockTypes.EndPortalFrame;",
      "          rot = 2;",
      "        } else if (this._filledEyeCount.value === 12 && x >= 1 && z >= 1 && x <= 3 && z <= 3) {",
      "          // center",
      "          blockType = MinecraftBlockTypes.EndPortal;",
      "        } else {",
      "          continue;",
      "        }",
      "        const block = me.dimension.getBlock({ x: location.x + x, y: location.y, z: location.z + z });",
      "        if (block) {",
      "          block.setType(blockType);",
      "          if (blockType === MinecraftBlockTypes.EndPortalFrame) {",
      "            const perm = block.permutation.withState('direction', rot).withState('end_portal_eye_bit', eyesToUse[i]);",
      "            block.setPermutation(perm);",
      "            i += 1;",
      "          }",
      "        } else {",
      "          uiSession.log.error('Failed to get block');",
      "        }",
      "      }",
      "    }",
      "    uiSession.extensionContext.transactionManager.commitOpenTransaction();",
      "  }",
      "}",
      "function getRandomInt(upper: number) {",
      "  return Math.ceil(Math.random() * (upper + 1));",
      "}",
      "/**",
      " * Register Portal Generator extension",
      " */",
      "export function registerPortalGeneratorExtension() {",
      "  registerEditorExtension<ExtensionStorage>(",
      "    'portal-generator-sample',",
      "    (uiSession) => {",
      "      uiSession.log.debug(`Initializing [${uiSession.extensionContext.extensionInfo.name}] extension`);",
      "      uiSession.scratchStorage = {};",
      "      const generator = new PortalGenerator();",
      "      generator.initialize(uiSession, uiSession.scratchStorage);",
      "      return [generator];",
      "    },",
      "    (uiSession) => {",
      "      uiSession.log.debug(`Shutting down [${uiSession.extensionContext.extensionInfo.name}] extension`);",
      "    },",
      "    {",
      "      description: 'Portal Generator Sample Extension',",
      "      notes: 'by Andrew',",
      "    }",
      "  );",
      "}",
      ""
    ]
  },
  "simple-locate-biome.ts": {
    "description": "An editor extension that locates and teleports to a selected biome or structure type",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import { VECTOR3_ZERO, Vector3Utils } from '@minecraft/math';",
      "import {",
      "  ActionTypes,",
      "  IDropdownItem,",
      "  IObservable,",
      "  IPlayerUISession,",
      "  ISimpleToolOptions,",
      "  ISimpleToolPaneComponent,",
      "  InputModifier,",
      "  KeyboardKey,",
      "  SimpleToolWrapper,",
      "  makeObservable,",
      "  registerEditorExtension,",
      "} from '@minecraft/server-editor';",
      "import { BiomeTypes, Player, Vector3 } from '@minecraft/server';",
      "enum LocateMode {",
      "  Biome = 0,",
      "  Structure,",
      "}",
      "type LocateSelectorType = {",
      "  locateMode: IObservable<LocateMode>;",
      "};",
      "type LocateBiomeSourceType = {",
      "  biomeId: IObservable<number>;",
      "  biomePos: Vector3;",
      "};",
      "type ResultsType = {",
      "  foundType: string;",
      "  foundPos: Vector3;",
      "};",
      "// Implementation of a simple tool that allows the player to locate a biome",
      "// This tool demonstrates the use of the SimpleToolWrapper class (and the associated",
      "// ISimpleTool<T>Options interfaces)",
      "export class SimpleLocate extends SimpleToolWrapper {",
      "  private _results: ResultsType = {",
      "    foundType: '',",
      "    foundPos: VECTOR3_ZERO,",
      "  };",
      "  // Activate the results pane with the found biome/structure and position in the",
      "  // world.  This uses the SimpleToolWrapper's showPane method to display the results",
      "  // in a pre-constructed pane which is generally hidden until results are valid.",
      "  activateResultsPane(biome: string, pos: Vector3): void {",
      "    this.simpleTool.logInfo(`Found ${biome} at ${Vector3Utils.toString(pos)}`);",
      "    this._results.foundType = biome;",
      "    this._results.foundPos = pos;",
      "    // Hopefully, we'll be able to get rid of this function in the near future.",
      "    // reconstructing the pane is a last-resort hack to get the pane to update",
      "    // certain UI components which do not currently support dynamic data binding.",
      "    // It's a necessary evil for now, but we're working on a better solution.",
      "    // We certainly don't recommend doing this more than necessary - it causes a",
      "    // lot of classes to be re-created and can be very slow, especially if there",
      "    // are a lot of child panes and UI components.",
      "    const foundPane = this.simpleTool.pane.findPane('results-found');",
      "    foundPane?.reconstructPane();",
      "    // Show the results pane (this will cause the other sibling panes",
      "    // (like 'results-notfound') to be hidden because they're flagged as mutually",
      "    // exclusive)",
      "    this.simpleTool.showPane('results-found');",
      "  }",
      "  activateNoResultsPane(): void {",
      "    // Show the results-notfound pane (this will cause the other sibling panes",
      "    // (like 'results-found') to be hidden because they're flagged as mutually",
      "    // exclusive)",
      "    this.simpleTool.showPane('results-notfound');",
      "  }",
      "  hideResultsPane(): void {",
      "    // Hide the results pane",
      "    // We generally do this when we're changing options or don't have a valid set",
      "    // of results to display.",
      "    // Note that this will hide both the `results-found` and `results-notfound` panes",
      "    // because they're child panes of this one",
      "    this.simpleTool.hidePane('results');",
      "  }",
      "  // The 'typeSelector' pane is the initial pane that allows the player to select the",
      "  // type of 'thing' they want to locate (biome or structure).  This function builds",
      "  // a dropdown containing the two options.",
      "  // The onChange event handler is used to show the appropriate sub-pane based on the",
      "  // selection.",
      "  buildTypeSelectionPane(component: ISimpleToolPaneComponent): void {",
      "    const actualPane = component.pane;",
      "    // We create a data binding between this pane/server script and the client-side",
      "    // UI component.  This binding allows changes here to be reflected in the UI and",
      "    // changes made to the UI to be reflected here.",
      "    // It creates a bi-directional networking link between the client and server.",
      "    // We use this to ensure that the selected type is always up-to-date.",
      "    const locatorType: LocateSelectorType = {",
      "      locateMode: makeObservable(LocateMode.Biome),",
      "    };",
      "    // Create a dropdown with two options: Biome and Structure",
      "    // and an event handler to show the appropriate sub-pane when the selection changes",
      "    actualPane.addDropdown(locatorType.locateMode, {",
      "      title: 'sample.simplelocate.tool.locatetype.title',",
      "      entries: [",
      "        {",
      "          label: 'sample.simplelocate.tool.locatetype.biome',",
      "          value: LocateMode.Biome,",
      "        },",
      "        {",
      "          label: 'sample.simplelocate.tool.locatetype.structure',",
      "          value: LocateMode.Structure,",
      "        },",
      "      ],",
      "      onChange: (newValue: number) => {",
      "        const mode = newValue as LocateMode;",
      "        if (mode === LocateMode.Biome) {",
      "          component.simpleTool.showPane('type-biome');",
      "        } else {",
      "          component.simpleTool.showPane('type-structure');",
      "        }",
      "        component.simpleTool.hidePane('results');",
      "      },",
      "    });",
      "  }",
      "  buildBiomeSearchPane(component: ISimpleToolPaneComponent): void {",
      "    const actualPane = component.pane;",
      "    const biomeType: LocateBiomeSourceType = {",
      "      biomeId: makeObservable(0),",
      "      biomePos: VECTOR3_ZERO,",
      "    };",
      "    const listOfBiomes = BiomeTypes.getAll().map((v, i) => {",
      "      const names = v.id;",
      "      const item: IDropdownItem = {",
      "        label: names.replace('minecraft:', '').replace('_', ' '),",
      "        value: i,",
      "      };",
      "      return item;",
      "    });",
      "    actualPane.addDropdown(biomeType.biomeId, {",
      "      title: 'sample.simplelocate.tool.biome.title',",
      "      entries: listOfBiomes,",
      "      onChange: () => {",
      "        component.simpleTool.hidePane('results');",
      "      },",
      "    });",
      "    const locateBiomeAction = component.session.actionManager.createAction({",
      "      actionType: ActionTypes.NoArgsAction,",
      "      onExecute: () => {",
      "        const biome = BiomeTypes.getAll()[biomeType.biomeId.value].id;",
      "        const player: Player = component.session.extensionContext.player;",
      "        const biomePos = player.dimension.findClosestBiome(player.location, biome);",
      "        if (biomePos) {",
      "          this.activateResultsPane(biome, biomePos);",
      "        } else {",
      "          this.activateNoResultsPane();",
      "        }",
      "      },",
      "    });",
      "    actualPane.addButton(locateBiomeAction, {",
      "      title: 'sample.simplelocate.tool.biome.find',",
      "      visible: true,",
      "      icon: 'pinIcon',",
      "    });",
      "  }",
      "  buildStructurePane(component: ISimpleToolPaneComponent): void {",
      "    const actualPane = component.pane;",
      "    actualPane.addText('sample.simplelocate.tool.structure.message', {",
      "      border: true,",
      "    });",
      "  }",
      "  buildResultsPane(component: ISimpleToolPaneComponent): void {",
      "    const actualPane = component.pane;",
      "    actualPane.addText(`Found ${this._results.foundType}`);",
      "    actualPane.addVector3_deprecated(this._results, 'foundPos', {",
      "      title: 'sample.simplelocate.tool.results.foundat',",
      "      enable: false,",
      "      visible: true,",
      "    });",
      "    actualPane.addButton(",
      "      component.session.actionManager.createAction({",
      "        actionType: ActionTypes.NoArgsAction,",
      "        onExecute: () => {",
      "          const pos = this._results.foundPos;",
      "          component.session.extensionContext.player.teleport(pos);",
      "        },",
      "      }),",
      "      {",
      "        title: 'sample.simplelocate.tool.results.goto',",
      "      }",
      "    );",
      "  }",
      "  buildNoResultsPane(component: ISimpleToolPaneComponent): void {",
      "    const actualPane = component.pane;",
      "    actualPane.addText('sample.simplelocate.tool.results.notfound', {",
      "      border: true,",
      "    });",
      "  }",
      "  constructor(session: IPlayerUISession) {",
      "    super();",
      "    const toolOptions: ISimpleToolOptions = {",
      "      name: 'Simple Locate Biome',",
      "      activationKeyBinding: {",
      "        binding: {",
      "          key: KeyboardKey.KEY_L,",
      "          modifier: InputModifier.Control | InputModifier.Shift,",
      "        },",
      "        info: {",
      "          uniqueId: 'editorSample:locateBiomeToolKeyBinding:toggleTool',",
      "          label: 'sample.simplelocate.tool.keyBinding.toggleTool',",
      "        },",
      "      },",
      "      propertyPaneOptions: {",
      "        id: 'pane',",
      "        title: 'sample.simplelocate.tool.title',",
      "        childPaneInitiallyVisible: 'typeSelector',",
      "        // There are 2 sub-panes in the root window, and 2 sub-panes in the first sub-pane",
      "        // 1        typeSelector            - The Locate Type Selector",
      "        //  1-1         type-biome          - The Biome Locator",
      "        //  1-2         type-structure      - The Structure Locator",
      "        // 2        results                 - The results",
      "        //  2-1         results-found       - Actual Results",
      "        //  2-2         results-notfound    - Not Found warning",
      "        childPanes: [",
      "          {",
      "            id: 'typeSelector',",
      "            title: 'sample.simplelocate.tool.locatetype.title',",
      "            onBeginFinalize: (component) => this.buildTypeSelectionPane(component),",
      "            childPaneInitiallyVisible: 'type-biome',",
      "            childPanesMutuallyExclusive: true,",
      "            childPanes: [",
      "              {",
      "                id: 'type-biome',",
      "                title: 'sample.simplelocate.tool.locatetype.biome.title',",
      "                onBeginFinalize: (component) => this.buildBiomeSearchPane(component),",
      "              },",
      "              {",
      "                id: 'type-structure',",
      "                title: 'sample.simplelocate.tool.locatetype.structure.title',",
      "                onBeginFinalize: (component) => this.buildStructurePane(component),",
      "              },",
      "            ],",
      "          },",
      "          {",
      "            id: 'results',",
      "            title: 'sample.simplelocate.tool.results.title',",
      "            childPanesMutuallyExclusive: true,",
      "            childPanes: [",
      "              {",
      "                id: 'results-found',",
      "                title: 'sample.simplelocate.tool.results.foundat.title',",
      "                onBeginFinalize: (component) => this.buildResultsPane(component),",
      "              },",
      "              {",
      "                id: 'results-notfound',",
      "                title: 'sample.simplelocate.tool.results.notfound.title',",
      "                onBeginFinalize: (component) => this.buildNoResultsPane(component),",
      "              },",
      "            ],",
      "          },",
      "        ],",
      "      },",
      "    };",
      "    this.setupSimpleTool(session, toolOptions);",
      "  }",
      "}",
      "/**",
      " * Provides a 'Simple Biome Locate' extension to demonstrate the new Simple Tool wrapper system",
      " * @beta",
      " */",
      "export function registerSimpleLocateBiomeExtension() {",
      "  registerEditorExtension(",
      "    'simple-locate-sample',",
      "    (uiSession) => {",
      "      uiSession.log.debug(`Initializing extension [${uiSession.extensionContext.extensionInfo.name}]`);",
      "      // Just instantiate the tool and return it to the editor - the editor will deal with cleaning up",
      "      // and shutting down the tool when it's no longer required",
      "      const simpleLocateTool = new SimpleLocate(uiSession);",
      "      // Return an array of things for the editor to clean up.",
      "      // If you wanted to, you can create many individual tools in this single register function",
      "      // and return them all in the array, and the editor will clean them all up when the extension",
      "      // is unloaded",
      "      return [simpleLocateTool];",
      "    },",
      "    (uiSession) => {",
      "      uiSession.log.debug(",
      "        `Shutting down extension [${uiSession.extensionContext.extensionInfo.name}] for player [${uiSession.extensionContext.player.name}]`",
      "      );",
      "    },",
      "    {",
      "      description: 'Simple Locate Biome Tool Sample Extension',",
      "      notes: 'by Dave & Mitch',",
      "    }",
      "  );",
      "}",
      ""
    ]
  },
  "star-brush-shape.ts": {
    "description": "An editor extension that adds a star-shaped brush for painting blocks in a star pattern",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import {",
      "    BlockVolume,",
      "    CompoundBlockVolume,",
      "    CompoundBlockVolumeAction,",
      "    CompoundBlockVolumePositionRelativity,",
      "} from '@minecraft/server';",
      "import { IPlayerUISession, registerEditorExtension, SettingsUIElement } from '@minecraft/server-editor';",
      "type Settings = {",
      "    radius: number;",
      "    includeVertical: boolean;",
      "};",
      "const MIN_RADIUS = 1;",
      "const MAX_RADIUS = 12;",
      "/**",
      " * Register Star Brush extension",
      " */",
      "export function registerStarBrushExtension() {",
      "    registerEditorExtension(",
      "        'StarBrush',",
      "        (uiSession: IPlayerUISession) => {",
      "            uiSession.log.debug(`Initializing ${uiSession.extensionContext.extensionInfo.name}`);",
      "            const settings: Settings = {",
      "                radius: 3,",
      "                includeVertical: true,",
      "            };",
      "            const rebuildStar = () => {",
      "                const compoundVolume = new CompoundBlockVolume();",
      "                // center",
      "                compoundVolume.pushVolume({",
      "                    action: CompoundBlockVolumeAction.Add,",
      "                    locationRelativity: CompoundBlockVolumePositionRelativity.Relative,",
      "                    volume: new BlockVolume({ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }),",
      "                });",
      "                if (settings.radius > 1) {",
      "                    const r = settings.radius;",
      "                    // left",
      "                    compoundVolume.pushVolume({",
      "                        action: CompoundBlockVolumeAction.Add,",
      "                        locationRelativity: CompoundBlockVolumePositionRelativity.Relative,",
      "                        volume: new BlockVolume({ x: -r, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }),",
      "                    });",
      "                    // right",
      "                    compoundVolume.pushVolume({",
      "                        action: CompoundBlockVolumeAction.Add,",
      "                        locationRelativity: CompoundBlockVolumePositionRelativity.Relative,",
      "                        volume: new BlockVolume({ x: 0, y: 0, z: 0 }, { x: r, y: 0, z: 0 }),",
      "                    });",
      "                    // front",
      "                    compoundVolume.pushVolume({",
      "                        action: CompoundBlockVolumeAction.Add,",
      "                        locationRelativity: CompoundBlockVolumePositionRelativity.Relative,",
      "                        volume: new BlockVolume({ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: r }),",
      "                    });",
      "                    // back",
      "                    compoundVolume.pushVolume({",
      "                        action: CompoundBlockVolumeAction.Add,",
      "                        locationRelativity: CompoundBlockVolumePositionRelativity.Relative,",
      "                        volume: new BlockVolume({ x: 0, y: 0, z: -r }, { x: 0, y: 0, z: 0 }),",
      "                    });",
      "                    if (settings.includeVertical) {",
      "                        // up",
      "                        compoundVolume.pushVolume({",
      "                            action: CompoundBlockVolumeAction.Add,",
      "                            locationRelativity: CompoundBlockVolumePositionRelativity.Relative,",
      "                            volume: new BlockVolume({ x: 0, y: 0, z: 0 }, { x: 0, y: r, z: 0 }),",
      "                        });",
      "                        // down",
      "                        compoundVolume.pushVolume({",
      "                            action: CompoundBlockVolumeAction.Add,",
      "                            locationRelativity: CompoundBlockVolumePositionRelativity.Relative,",
      "                            volume: new BlockVolume({ x: 0, y: 0, z: 0 }, { x: 0, y: -r, z: 0 }),",
      "                        });",
      "                    }",
      "                }",
      "                return compoundVolume;",
      "            };",
      "            const getStarUISettings = (): SettingsUIElement[] => {",
      "                return [",
      "                    new SettingsUIElement(",
      "                        'Radius',",
      "                        settings.radius,",
      "                        arg => {",
      "                            if (arg !== undefined && typeof arg === 'number') {",
      "                                settings.radius = arg;",
      "                            }",
      "                        },",
      "                        {",
      "                            min: MIN_RADIUS,",
      "                            max: MAX_RADIUS,",
      "                        }",
      "                    ),",
      "                    new SettingsUIElement(",
      "                        'includeVertical',",
      "                        settings.includeVertical,",
      "                        arg => {",
      "                            if (arg !== undefined && typeof arg === 'boolean') {",
      "                                settings.includeVertical = arg;",
      "                            }",
      "                        },",
      "                        { refreshOnChange: true }",
      "                    ),",
      "                    new SettingsUIElement('Vector3', { x: 1, y: 2, z: 3 }, _value => {}),",
      "                ];",
      "            };",
      "            uiSession.extensionContext.brushShapeManager.registerBrushShape(",
      "                'Star-sample',",
      "                'star',",
      "                rebuildStar,",
      "                getStarUISettings",
      "            );",
      "            return [];",
      "        },",
      "        (uiSession: IPlayerUISession) => {",
      "            uiSession.log.debug(`Uninitializing ${uiSession.extensionContext.extensionInfo.name}`);",
      "        },",
      "        {",
      "            description: 'Star brush shape sample extension',",
      "            notes: 'An example of registering a custom brush shape.',",
      "        }",
      "    );",
      "}",
      ""
    ]
  },
  "tree-generator.ts": {
    "description": "An editor extension that generates different types of trees at a target location",
    "prefix": ["mc"],
    "body": [
      "// Copyright (c) Mojang AB.  All rights reserved.",
      "import {",
      "  ActionTypes,",
      "  EditorInputContext,",
      "  IDropdownPropertyItemEntry,",
      "  IModalTool,",
      "  IObservable,",
      "  IPlayerUISession,",
      "  InputModifier,",
      "  KeyboardKey,",
      "  MouseActionType,",
      "  MouseInputType,",
      "  MouseProps,",
      "  NumberPropertyItemVariant,",
      "  Ray,",
      "  makeObservable,",
      "  registerEditorExtension,",
      "} from '@minecraft/server-editor';",
      "import { BlockPermutation, Vector3 } from '@minecraft/server';",
      "import { MinecraftBlockTypes } from '@minecraft/vanilla-data';",
      "interface TreeToolSettings {",
      "  height: IObservable<number>;",
      "  randomHeightVariance: IObservable<number>;",
      "  treeType: IObservable<number>;",
      "}",
      "interface TreeBlockChangeData {",
      "  location: Vector3;",
      "  newBlock: BlockPermutation;",
      "}",
      "interface ITree {",
      "  place(location: Vector3, settings: TreeToolSettings): TreeBlockChangeData[];",
      "}",
      "export class SimpleTree implements ITree {",
      "  logType: BlockPermutation;",
      "  leafType: BlockPermutation;",
      "  constructor(logType: BlockPermutation, leafType: BlockPermutation) {",
      "    this.logType = logType;",
      "    this.leafType = leafType;",
      "  }",
      "  place(location: Vector3, settings: TreeToolSettings): TreeBlockChangeData[] {",
      "    const result: TreeBlockChangeData[] = [];",
      "    const heightOffset =",
      "      Math.floor(Math.random() * settings.randomHeightVariance.value) - settings.randomHeightVariance.value / 2;",
      "    const calculatedHeight = settings.height.value + heightOffset;",
      "    ///",
      "    // Trunk",
      "    ///",
      "    for (let y = 0; y <= calculatedHeight; ++y) {",
      "      const offsetLocation: Vector3 = {",
      "        x: location.x,",
      "        y: location.y + y,",
      "        z: location.z,",
      "      };",
      "      result.push({",
      "        location: offsetLocation,",
      "        newBlock: this.logType,",
      "      });",
      "    }",
      "    ///",
      "    // Leaves",
      "    ///",
      "    ///",
      "    // Plus sign on top",
      "    ///",
      "    const leafBlocks = [",
      "      { x: 0, y: 1, z: 0 },",
      "      { x: 1, y: 1, z: 0 },",
      "      { x: -1, y: 1, z: 0 },",
      "      { x: 0, y: 1, z: 1 },",
      "      { x: 0, y: 1, z: -1 },",
      "      { x: 1, y: 0, z: 0 },",
      "      { x: -1, y: 0, z: 0 },",
      "      { x: 0, y: 0, z: 1 },",
      "      { x: 0, y: 0, z: -1 },",
      "    ];",
      "    const randomPlusBlocks = [",
      "      { x: 1, y: 0, z: 1 },",
      "      { x: -1, y: 0, z: 1 },",
      "      { x: -1, y: 0, z: -1 },",
      "      { x: 1, y: 0, z: -1 },",
      "    ];",
      "    randomPlusBlocks.forEach((randBlock) => {",
      "      if (Math.random() > 0.5) {",
      "        leafBlocks.push(randBlock);",
      "      }",
      "    });",
      "    ///",
      "    // Fat bottom",
      "    ///",
      "    leafBlocks.push(",
      "      ...[",
      "        { x: 1, y: -1, z: -1 },",
      "        { x: 1, y: -1, z: 0 },",
      "        { x: 1, y: -1, z: 1 },",
      "        { x: 0, y: -1, z: 1 },",
      "        { x: 0, y: -1, z: -1 },",
      "        { x: -1, y: -1, z: -1 },",
      "        { x: -1, y: -1, z: 1 },",
      "        { x: -1, y: -1, z: 0 },",
      "      ]",
      "    );",
      "    if (calculatedHeight > 4) {",
      "      leafBlocks.push(",
      "        ...[",
      "          { x: 1, y: -2, z: -1 },",
      "          { x: 1, y: -2, z: 0 },",
      "          { x: 1, y: -2, z: 1 },",
      "          { x: 0, y: -2, z: 1 },",
      "          { x: 0, y: -2, z: -1 },",
      "          { x: -1, y: -2, z: -1 },",
      "          { x: -1, y: -2, z: 1 },",
      "          { x: -1, y: -2, z: 0 },",
      "          // Outer",
      "          { x: -2, y: -1, z: -1 },",
      "          { x: -2, y: -1, z: 0 },",
      "          { x: -2, y: -1, z: 1 },",
      "          { x: -1, y: -1, z: -2 },",
      "          { x: -1, y: -1, z: -1 },",
      "          { x: -1, y: -1, z: 0 },",
      "          { x: -1, y: -1, z: 1 },",
      "          { x: -1, y: -1, z: 2 },",
      "          { x: 0, y: -1, z: -2 },",
      "          { x: 0, y: -1, z: -1 },",
      "          { x: 0, y: -1, z: 1 },",
      "          { x: 0, y: -1, z: 2 },",
      "          { x: 1, y: -1, z: -2 },",
      "          { x: 1, y: -1, z: -1 },",
      "          { x: 1, y: -1, z: 0 },",
      "          { x: 1, y: -1, z: 1 },",
      "          { x: 1, y: -1, z: 2 },",
      "          { x: 2, y: -1, z: -1 },",
      "          { x: 2, y: -1, z: 0 },",
      "          { x: 2, y: -1, z: 1 },",
      "          { x: -2, y: -2, z: -1 },",
      "          { x: -2, y: -2, z: 0 },",
      "          { x: -2, y: -2, z: 1 },",
      "          { x: -1, y: -2, z: -2 },",
      "          { x: -1, y: -2, z: -1 },",
      "          { x: -1, y: -2, z: 0 },",
      "          { x: -1, y: -2, z: 1 },",
      "          { x: -1, y: -2, z: 2 },",
      "          { x: 0, y: -2, z: -2 },",
      "          { x: 0, y: -2, z: -1 },",
      "          { x: 0, y: -2, z: 1 },",
      "          { x: 0, y: -2, z: 2 },",
      "          { x: 1, y: -2, z: -2 },",
      "          { x: 1, y: -2, z: -1 },",
      "          { x: 1, y: -2, z: 0 },",
      "          { x: 1, y: -2, z: 1 },",
      "          { x: 1, y: -2, z: 2 },",
      "          { x: 2, y: -2, z: -1 },",
      "          { x: 2, y: -2, z: 0 },",
      "          { x: 2, y: -2, z: 1 },",
      "        ]",
      "      );",
      "    }",
      "    const randomFatBottomBlocks = [",
      "      { x: -2, y: -1, z: -2 },",
      "      { x: -2, y: -1, z: 2 },",
      "      { x: 2, y: -1, z: -2 },",
      "      { x: 2, y: -1, z: 2 },",
      "    ];",
      "    if (calculatedHeight > 4) {",
      "      randomFatBottomBlocks.push(",
      "        ...[",
      "          { x: -2, y: -2, z: -2 },",
      "          { x: -2, y: -2, z: 2 },",
      "          { x: 2, y: -2, z: -2 },",
      "          { x: 2, y: -2, z: 2 },",
      "        ]",
      "      );",
      "    }",
      "    leafBlocks.forEach((block) => {",
      "      const offsetLocation: Vector3 = {",
      "        x: location.x + block.x,",
      "        y: location.y + calculatedHeight + block.y,",
      "        z: location.z + block.z,",
      "      };",
      "      result.push({",
      "        location: offsetLocation,",
      "        newBlock: this.leafType,",
      "      });",
      "    });",
      "    return result;",
      "  }",
      "}",
      "const TreeTypes = [",
      "  {",
      "    name: 'Oak',",
      "    type: new SimpleTree(",
      "      BlockPermutation.resolve(MinecraftBlockTypes.OakLog),",
      "      BlockPermutation.resolve(MinecraftBlockTypes.OakLeaves)",
      "    ),",
      "  },",
      "  {",
      "    name: 'Spruce',",
      "    type: new SimpleTree(",
      "      BlockPermutation.resolve(MinecraftBlockTypes.SpruceLog),",
      "      BlockPermutation.resolve(MinecraftBlockTypes.SpruceLeaves)",
      "    ),",
      "  },",
      "  {",
      "    name: 'Birch',",
      "    type: new SimpleTree(",
      "      BlockPermutation.resolve(MinecraftBlockTypes.BirchLog),",
      "      BlockPermutation.resolve(MinecraftBlockTypes.BirchLeaves)",
      "    ),",
      "  },",
      "  {",
      "    name: 'Jungle',",
      "    type: new SimpleTree(",
      "      BlockPermutation.resolve(MinecraftBlockTypes.JungleLog),",
      "      BlockPermutation.resolve(MinecraftBlockTypes.JungleLeaves)",
      "    ),",
      "  },",
      "  {",
      "    name: 'Acacia',",
      "    type: new SimpleTree(",
      "      BlockPermutation.resolve(MinecraftBlockTypes.AcaciaLog),",
      "      BlockPermutation.resolve(MinecraftBlockTypes.AcaciaLeaves)",
      "    ),",
      "  },",
      "  {",
      "    name: 'Dark Oak',",
      "    type: new SimpleTree(",
      "      BlockPermutation.resolve(MinecraftBlockTypes.DarkOakLog),",
      "      BlockPermutation.resolve(MinecraftBlockTypes.DarkOakLeaves)",
      "    ),",
      "  },",
      "];",
      "function addToolSettingsPane(uiSession: IPlayerUISession, tool: IModalTool) {",
      "  // Create a pane that will be shown when the tool is selected",
      "  const pane = uiSession.createPropertyPane({",
      "    title: 'sample.treegenerator.pane.title',",
      "  });",
      "  // Settings",
      "  const settings: TreeToolSettings = {",
      "    height: makeObservable(5),",
      "    randomHeightVariance: makeObservable(0),",
      "    treeType: makeObservable(0),",
      "  };",
      "  const onExecuteTool = (ray?: Ray) => {",
      "    const player = uiSession.extensionContext.player;",
      "    let location: Vector3;",
      "    // Try finding a valid block to place a tree",
      "    if (ray) {",
      "      const raycastResult = player.dimension.getBlockFromRay(ray.location, ray.direction);",
      "      if (!raycastResult) {",
      "        uiSession.log.warning('Invalid target block!');",
      "        return;",
      "      }",
      "      location = raycastResult.block.location;",
      "    } else {",
      "      const targetBlock = player.dimension.getBlock(uiSession.extensionContext.cursor.getPosition());",
      "      if (!targetBlock) {",
      "        uiSession.log.warning('Invalid target block!');",
      "        return;",
      "      }",
      "      location = targetBlock.location;",
      "    }",
      "    // Begin transaction",
      "    uiSession.extensionContext.transactionManager.openTransaction('Tree Tool');",
      "    const selectedTreeType = TreeTypes[settings.treeType.value];",
      "    const affectedBlocks = selectedTreeType.type.place(location, settings);",
      "    // Track changes",
      "    uiSession.extensionContext.transactionManager.trackBlockChangeList(affectedBlocks.map((x) => x.location));",
      "    // Apply changes",
      "    let invalidBlockCount = 0;",
      "    affectedBlocks.forEach((item) => {",
      "      const block = player.dimension.getBlock(item.location);",
      "      if (block) {",
      "        block.setPermutation(item.newBlock);",
      "      } else {",
      "        ++invalidBlockCount;",
      "      }",
      "    });",
      "    if (invalidBlockCount > 0) {",
      "      uiSession.log.warning(`There were ${invalidBlockCount} invalid blocks while placing a tree!`);",
      "    }",
      "    // End transaction",
      "    uiSession.extensionContext.transactionManager.commitOpenTransaction();",
      "  };",
      "  // Add a dropdown for available tree types",
      "  pane.addDropdown(settings.treeType, {",
      "    title: 'sample.treegenerator.pane.type',",
      "    enable: true,",
      "    entries: TreeTypes.map((tree, index): IDropdownPropertyItemEntry => {",
      "      return {",
      "        label: tree.name,",
      "        value: index,",
      "      };",
      "    }, []),",
      "  });",
      "  pane.addNumber(settings.height, {",
      "    title: 'sample.treegenerator.pane.height',",
      "    min: 1,",
      "    max: 16,",
      "    variant: NumberPropertyItemVariant.InputFieldAndSlider,",
      "    isInteger: true,",
      "  });",
      "  pane.addNumber(settings.randomHeightVariance, {",
      "    title: 'sample.treegenerator.pane.variance',",
      "    min: 0,",
      "    max: 5,",
      "    variant: NumberPropertyItemVariant.InputFieldAndSlider,",
      "    isInteger: true,",
      "  });",
      "  // Create and an action that will be executed on key press",
      "  const executeAction = uiSession.actionManager.createAction({",
      "    actionType: ActionTypes.NoArgsAction,",
      "    onExecute: onExecuteTool,",
      "  });",
      "  // Register the action as a keyboard shortcut",
      "  tool.registerKeyBinding(",
      "    executeAction,",
      "    { key: KeyboardKey.KEY_T },",
      "    { uniqueId: 'editorSamples:treeGenerator:place', label: 'sample.treegenerator.keyBinding.place' }",
      "  );",
      "  tool.bindPropertyPane(pane);",
      "  pane.hide();",
      "  // Create an action that will be executed on left mouse click",
      "  const executeMouseAction = uiSession.actionManager.createAction({",
      "    actionType: ActionTypes.MouseRayCastAction,",
      "    onExecute: (mouseRay: Ray, mouseProps: MouseProps) => {",
      "      if (mouseProps.mouseAction === MouseActionType.LeftButton && mouseProps.inputType === MouseInputType.ButtonDown) {",
      "        onExecuteTool(mouseRay);",
      "      }",
      "    },",
      "  });",
      "  // Register the action for mouse button",
      "  tool.registerMouseButtonBinding(executeMouseAction);",
      "  return settings;",
      "}",
      "/**",
      " * Create a new tool rail item for tree generator",
      " */",
      "function addTool(uiSession: IPlayerUISession) {",
      "  // Create action",
      "  const toolToggleAction = uiSession.actionManager.createAction({",
      "    actionType: ActionTypes.NoArgsAction,",
      "    onExecute: () => {",
      "      uiSession.toolRail.setSelectedToolId(tool.id);",
      "    },",
      "  });",
      "  const tool = uiSession.toolRail.addTool(",
      "    {",
      "      title: 'sample.treegenerator.tool.title',",
      "      icon: 'pack://textures/tree-generator.png',",
      "      tooltip: 'sample.treegenerator.tool.tooltip',",
      "      inputContextId: 'editorSamples:treeGenerator',",
      "      inputContextLabel: 'sample.treegenerator.tool.title',",
      "    },",
      "    toolToggleAction",
      "  );",
      "  // Register a global shortcut to select the tool",
      "  uiSession.inputManager.registerKeyBinding(",
      "    EditorInputContext.GlobalToolMode,",
      "    toolToggleAction,",
      "    { key: KeyboardKey.KEY_T, modifier: InputModifier.Control | InputModifier.Shift },",
      "    { uniqueId: 'editorSamples:treeGenerator:toggleTool', label: 'sample.treegenerator.keyBinding.toggleTool' }",
      "  );",
      "  return tool;",
      "}",
      "/**",
      " * Register Tree Generator extension",
      " */",
      "export function registerTreeGeneratorExtension() {",
      "  registerEditorExtension(",
      "    'TreeGenerator-sample',",
      "    (uiSession: IPlayerUISession) => {",
      "      uiSession.log.debug(`Initializing [${uiSession.extensionContext.extensionInfo.name}] extension`);",
      "      // Add extension tool to tool rail",
      "      const tool = addTool(uiSession);",
      "      // Create settings pane/window for the extension",
      "      addToolSettingsPane(uiSession, tool);",
      "      return [];",
      "    },",
      "    (uiSession: IPlayerUISession) => {",
      "      uiSession.log.debug(`Shutting down [${uiSession.extensionContext.extensionInfo.name}] extension`);",
      "    },",
      "    {",
      "      description: 'Tree Generator Sample Extension',",
      "      notes: 'by Jake',",
      "    }",
      "  );",
      "}",
      ""
    ]
  }
}
