English | 简体中文
Be.D.D. (Bedrock Deep Dive) is named after the H.D.D. of Zenless Zone Zero: a Proxy guides agents through the Hollows from afar, your script guides Minecraft from the background.
Run end-to-end tests of your LeviLamina client mods in the background. Your script presses keys, types and clicks inside Minecraft Bedrock, while your own mouse, keyboard and windows stay yours, and several games can play at once.
Be.D.D. is a testing toolkit, not a mod for players: a client-side LeviLamina module that your test harness puts into a game instance for a run, the PowerShell client that drives it, and the scripts that set up, clone and start the games. Documentation: https://docs.levimc.org/BeDD/
Warning
This is a testing tool. A game with Be.D.D. in its mods\ folder ignores your real mouse
and keyboard and shows up only in the manager window. Do not leave it in a game you play.
- Keep using your PC. The game never takes the foreground, the cursor or your keystrokes, and your clicks never land in it.
- Several games at once. Each game instance has its own pipe; a cheap clone of an instance takes a few seconds.
- One window for all of them. The games of every test run on the PC show up live in one manager window instead of popping up on your desktop; closing it closes them all.
- Plays like a player. Keys, chat, key bindings, menus, hover tooltips, clicks and the camera all work as with a real mouse and keyboard.
- Deterministic. Every call returns once the game has handled the input, so no guessing sleeps.
- Screenshots of covered windows. The manager window can sit behind your other windows.
- Drop-in for existing harnesses. One file replaces the usual
Click,Press-Key,Focus-Gamehelpers of LeviLauncher harnesses; scenarios stay as they are.
Download bedd-client-windows-x64.zip from the
releases and unzip it anywhere: it holds the module
(BeDD.dll) and the scripts. Harnesses copy the module into the game instance's mods\ folder at the
start of a run and remove it afterwards (see below); to try it by hand,
unzip it into the instance's mods\ folder. Be.D.D. is not a lip package and is not listed on
LeviMC Mods.
Needs Minecraft Bedrock Edition 1.26.51 (GDK) with LeviLamina 26.51.x (client), in a game
instance installed with LeviLauncher. Tests start that instance with Start-BeDDGame.ps1
rather than through the launcher (see below). Only the test PC's game
needs it; servers and players do not.
Nothing needs preparing by hand except the game itself. Initialize-BeDD.ps1 (also run by
Start-BeDDGame.ps1 and clone-instance.ps1) finds the instance and sets up whatever the PC
lacks, each step only once:
| Missing | What happens |
|---|---|
| LeviLauncher | the installed one is used; otherwise the latest release is downloaded to %LOCALAPPDATA%\BeDD\tools |
| the game | LeviLauncher opens and the script waits while you sign in with a Microsoft account that owns Minecraft and install 1.26.51 (a script cannot do this step; with CI set it fails with these instructions instead) |
| LeviLamina | installed into the instance with lip, which is downloaded with the .NET runtime it needs (per user, no admin) |
| LeviLauncher's loader in the game exe | the instance is started once through LeviLauncher, which adds it, and the game is closed again. Without the loader the game would use your normal Minecraft data instead of the instance's |
a clone (-Slot 2 and up) |
made from the base instance, and refreshed when the base instance has changed since |
$i = & .\Initialize-BeDD.ps1 -Slot 2 # Name, Dir, ModsDir, PipeName of versions\<base>-hi2BeDD finds LeviLauncher's data in %APPDATA%\levilauncher.exe, follows its "data folder" setting
(base_root), and picks the instance of 1.26.51 there (one with LeviLamina first). To point it
elsewhere, set BEDD_INSTANCE (instance name), BEDD_VERSIONS_DIR, BEDD_LAUNCHER_DATA,
BEDD_LEVILAUNCHER (LeviLauncher.exe) or BEDD_LIP (lip.exe).
A harness can fetch BeDD itself the same way instead of expecting a checkout somewhere:
$bedd = if ($env:BEDD_DIR) { $env:BEDD_DIR } else { Join-Path $env:LOCALAPPDATA 'BeDD\client' }
if (-not (Test-Path "$bedd\harness-shim.ps1")) {
$zip = Join-Path $env:TEMP 'bedd.zip'
Invoke-WebRequest https://github.com/LiteLDev/BeDD/releases/latest/download/bedd-client-windows-x64.zip -OutFile $zip
Expand-Archive $zip (Split-Path $bedd) -Force; Rename-Item (Join-Path (Split-Path $bedd) 'BeDD') $bedd
}The PowerShell module ships next to the mod:
Import-Module .\mods\BeDD\BeDD.psm1
Connect-BeDD # waits until the game is up
Send-BeDDKey E # open the inventory
Send-BeDDMouse click -RX 0.5 -RY 0.474 # click at a fraction of the window
Send-BeDDKey T; Send-BeDDText '/give @s apple 3'; Send-BeDDKey ENTER
Save-BeDDScreenshot C:\temp\shot.png # works while the window is covered| Command | What it does |
|---|---|
Connect-BeDD [-PipeName p] |
wait for the game and pick which game the next calls go to |
Send-BeDDKey E |
press a key; -Action down/up to hold, -Vk 0x10 for any virtual key |
Send-BeDDText '...' |
type text (no key presses, so t does not open chat); -DelayMs per character |
Send-BeDDMouse click -RX 0.5 -RY 0.5 |
move, down, up, click, wheel at -X -Y pixels or -RX -RY fractions; -Button right |
Send-BeDDRaw move -Dx 200 |
turn the camera in the world; click -Button right to use an item |
Clear-BeDDInput |
release every key and button still held |
Save-BeDDScreenshot path.png / Get-BeDDBitmap |
the game's picture, even when covered |
Set-BeDDWindow 1600 900 |
resize the game window without bringing it forward |
Get-BeDDState |
is the game up, how much input arrived, did it ever get the foreground |
Get-BeDDWorld |
in a world? which screen, mouse grabbed, menu open, the player's position and rotation (gameHooks) |
Set-BeDDLook 90 -30 |
turn the player to a yaw and pitch in degrees (gameHooks) |
Set-BeDDInput -Z 1 -Sprint -TtlMs 2000 |
walk, jump, sneak or sprint the player through the game's input systems for a while, whatever the key bindings (gameHooks) |
Set-BeDDSlot 3 / Invoke-BeDDAction inventory |
select a hotbar slot; drop, drop all, open the inventory, switch perspective, pause (gameHooks) |
Set-BeDDButton attack -Action down -ReleaseOnBlockChange |
hold attack/use/pick for game ticks or until the targeted block breaks, without blocking the pipe (gameHooks) |
Send-BeDDPacket -Base64 ... |
the player sends a chat or command packet a script serialized (allowSend) |
Send-BeDDStack -Buffer ... / -View |
the player sends an item stack request a script serialized; -View shows the inventory as the client sees it (allowStack) |
Key names: letters, digits, ESC, ENTER, BACK, TAB, SPACE, SHIFT, CTRL, ALT, arrows
(LEFT...), HOME, END, PGUP, PGDN, INSERT, DELETE, F1..F24, SLASH, GRAVE,
MINUS, EQUALS.
pwsh -NoProfile -File clone-instance.ps1 -Slot 2 # versions\1.26.51.01-hi2, ~90 MB, seconds
& .\Start-BeDDGame.ps1 -Instance 1.26.51.01-hi2 # starts it without bringing it forward
Connect-BeDD -PipeName BeDD-1.26.51.01-hi2 # the pipeName in that instance's configStart-BeDDGame.ps1 -Slot 2 (or -Instance <base>-hi2) makes the clone itself when it is missing. clone-instance.ps1 takes slots 1 to 32; for a higher slot use New-BeDDClone -Source <base> -Slot 41 from BeDDSetup.psm1.
A clone has its own mods\, logs, worlds and options. A few rules keep parallel runs from
tripping over each other:
- Give every instance its own lock file, and find your game by its instance folder, never just
by the name
Minecraft.Windows(or you close someone else's game). - Start games as
Minecraft.Windows.exe(Start-BeDDGame.ps1does): under another file name the game uses your normal Minecraft profile instead of the instance's. - Make test worlds single-player with
Set-WorldSinglePlayer <worldDir>(from the harness shim): games on one PC share an online sign-in, and multiplayer worlds opened at the same time fail with "An error has occurred" (code "Blaze"). - A run that joins a server online sets
$script:ExclusiveSignaling = $truebeforeStart-Gameand gets the PC to itself while the other games close.
The first Start-BeDDGame.ps1 of your logon session opens the manager window, and every
later run (from any terminal, harness or agent) reuses it. Every game shows up there as a live
picture, and nowhere else: its own window stays off screen, with no taskbar button and nothing in
Alt+Tab, so no windows pop up and vanish while several runs start and close games. It never takes
the focus.
- Each game is labelled with the project (and worktree) of the script that started it, its
instance and pid, and whether input is arriving.
-LabelonStart-BeDDGame.ps1names it. - Closing the manager window closes every game in it (it asks first). The games also go if the manager is killed, so nothing is left running in the background.
- Double-click a game to show only that one. Right-click it to open its
latest.logor instance folder, or to close it. - From a script:
BeDDManager.ps1 -Listlists the games,-Game <pid> -Action closecloses one,-Shutdowncloses the manager and every game without asking.
It needs PowerShell 7 (pwsh). A game never runs without it: a game that finds no manager window
starts one (managerScript in its config), and a game that no manager links within 90 s, or that
loses its manager for good, closes itself (the reason is in its latest.log).
harness-shim.ps1 replaces the usual helpers of LeviLauncher harnesses (Focus-Game,
Assert-GameForeground, Set-GameWindowSize, Move-Mouse, Click, Scroll, Press-Key,
Type-Text, Get-ClientBitmap, same parameters) and wraps Start-Game/Stop-Game:
. "$PSScriptRoot/lib.ps1"
. "$beddDir\harness-shim.ps1"
Register-BeDDNative ([MyE2E.Native]) # optional, see below
# after deploying your mod and moving the other mods aside:
Install-BeDDMod -ModsDir $script:ModsDir -PipeName 'BeDD-<instance>'
# ... and in finally, once the game is closed:
Uninstall-BeDDMod -ModsDir $script:ModsDirStart games with Start-BeDDGame.ps1 rather than the launcher, and keep BeDD
out of any "move other mods aside" step: it is installed fresh each run and removed afterwards (a
leftover copy would put a normally started game into the manager window too).
If your scenarios call the harness's own input class directly ([MyE2E.Native]::Key(0x10, $false)
and friends), give it these fields and a one-line guard at the top of each method;
Register-BeDDNative fills them:
public static Action<ushort, bool> KeyRedirect; // (vk, up)
public static Action<int, bool> MouseButtonRedirect; // (0 left / 1 right / 2 middle, up)
public static Action<int> WheelRedirect; // (delta, 120 per notch)
public static Action<char> UnicodeRedirect;
public static Action<int, int> MoveRelativeRedirect; // camera
public static void Key(ushort vk, bool up) { if (KeyRedirect != null) { KeyRedirect(vk, up); return; } /* SendInput as before */ }BeetrootSkin and AlleviatedBackpacks have complete harnesses to copy from.
mods\BeDD\config\config.json, usually written by the harness
(Install-BeDDMod does it). Without it the game still runs headless, with the defaults and no
managerScript (so only in a manager window that already runs). Install-BeDDMod and
Start-BeDDGame.ps1 also switch the instance's LeviLamina to its built-in crash logger
(modules.crashLogger.builtin in mods\LeviLamina\config\Config.json): the default
CrashLogger.exe is a console program, and its console window would flash on the desktop at every
game start.
{ "clientWidth": 1600, "clientHeight": 900, "pipeName": "BeDD", "managerScript": "C:\\BeDD\\BeDDManager.ps1" }| Key | |
|---|---|
clientWidth, clientHeight |
game window size (0 = leave it), so clicks land in the same place every run |
pipeName |
one per game instance when several run at once |
loopbackOnly |
default true: the game stays on the PC, so no Windows Firewall prompt for each new instance, and game servers outside the PC are not queried (they could show up as "LAN worlds" ahead of your test world). Dedicated servers your harness starts still ask once per server folder; Cancel is fine |
managerScript |
path of BeDDManager.ps1 (Install-BeDDMod writes it): the game starts the manager window with it when none runs |
mute |
default true: the game makes no sound |
trace |
log every input that reaches the game to logs\latest.log, for bug reports |
pipeInstances |
default 4: scripts that may use the game's pipe at once (each one's held keys, buttons and input override are released when it disconnects); 1 = one at a time |
gameHooks |
default false: hook the game's own systems for the ops bedboo uses: world and look (Get-BeDDWorld, Set-BeDDLook), input (Set-BeDDInput: walk, jump, sneak and sprint whatever the key bindings), slot, action and button (Set-BeDDSlot, Invoke-BeDDAction, Set-BeDDButton: hotbar, drop/inventory/perspective/pause, attack/use/pick held for game ticks or until the targeted block breaks); see CONTRIBUTING.md |
tap |
default false: serve \\.\pipe\<pipeName>-tap, a binary stream of the game packets the client receives and sends, for bedboo's transport bedd (see CONTRIBUTING.md, Tap protocol), with a join journal so a reader can attach in the middle of a session. It carries the whole session of the signed-in player, so only your user can open it. While it is on the client asks servers not to use its blob cache, so every chunk arrives in full |
tapLoopback |
default true, used only with tap: also tap singleplayer worlds and LAN worlds this game hosts (packets re-serialized; LAN guests' traffic is left out) |
tapJournalMiB |
default 128: size cap of the tap's join journal; past it a late attach is incomplete (complete:false) |
allowSend |
default false: allow the send pipe op (Send-BeDDPacket), with which a script such as bedboo makes the client send chat and commands it serialized (any local program of yours could then do so as your player) |
sendAllow |
packet ids send may send, default [9, 77] (text, command_request); login, inventory_transaction, player_auth_input and item_stack_request are always refused |
allowStack |
default false: the stack op (Send-BeDDStack) lets a pipe client make the game send item stack requests (inventory moves, crafting, chest moves) for the signed-in player, as bedboo does in bedd mode. Turn it on only for such harnesses |
The full documentation, with more videos and pictures, is at https://docs.levimc.org/BeDD/.
Building it yourself, the pipe protocol or how it works inside? See CONTRIBUTING.md.
LGPL-3.0-or-later © LeviMC. GameInput interface layouts follow Microsoft's public
GameInput.h (v2); no Microsoft code is included.

