#minecraft #minecraft-mod #datapack #mcfunction #vanilla

bin+lib mcfunction-debugger

A debugger for Minecraft's *.mcfunction files that does not require any Minecraft mods

4 releases (stable)

2.0.0 Jan 2, 2024
1.0.3 Feb 3, 2023
1.0.2 Jan 29, 2023
0.1.0 Dec 17, 2021

#159 in Debugging


Used in mcfunction-debug-adapter

GPL-3.0-or-later

315KB
7.5K SLoC

Minecraft: Java Edition 1.14.1 - 1.19.4 Minecraft: Bedrock Edition unsupported
crates.io Build Status

Mcfunction-Debugger

Mcfunction-Debugger is a debugger for Minecraft's *.mcfunction files that does not require any Minecraft mods.

Mcfunction-Debugger implements the Debug Adapter Protocol to allow easy integration with different IDEs such as Eclipse or Vim (see the list of supporting IDEs). The corresponding Visual Studio Code extension can be found here: https://codeberg.org/vanilla-technologies/mcfunction-debugger-vscode

If you would like to implement such an integration for another IDE, you can find documentation in the For Developers chapter.

Contents

  1. Debugging a Datapack
  2. Features
  3. Caveats
  4. Installation
  5. For Developers

Debugging a Datapack

To debug a function the mcfunction file must be contained in a datapack and the datapack must not be a zip file. The datapack doesn't have to be loaded in the Minecraft world since a debuggable version of the datapack will be generated anyways. The special function tags minecraft:load (defined in data/minecraft/tags/functions/load.json) and minecraft:tick (defined in data/minecraft/tags/functions/tick.json) are currently ignored (see #8), so to start debugging you must specify a function.

The function will be executed with a schedule command, so it runs without an @s entity at the world's origin position. To change this, you can create a new function that executes your function as the player or at a different position and debug this new function instead. For example:

execute as @p at @s run function my_namespace:my_function

Because the debugged function is executed using a schedule command it can behave slightly differently from a function executed by a player via the Minecraft chat or via the server console. Unlike manually executed commands, commands that are executed via schedule or command blocks run before the age of entities is incremented. So if you notice different behaviour when executing a function through the debugger and when manually executing the function try to manually schedule the function to see if that makes a difference.

Features

Mcfunction-Debugger allows debugging datapacks in both singleplayer and multiplayer worlds for a wide range of Minecraft versions. This is achieved by using Minect to connect to a vanilla Minecraft instance.

Calling Functions

Functions can be called both with and without execute, but calls to function tags are not supported yet (see #12).

Almost all variants of execute (execute as, execute at, ...) are fully supported. The only exception is execute store for function calls (see #11), which will currently store an arbitrary result/success value.

# Supported
function my_namespace:my_function
execute ... run function my_namespace:my_function

# Not supported
execute store result score my_target my_objective run function my_namespace:my_function
function #my_namespace:my_function_tag
execute ... run function #my_namespace:my_function

Entity Selectors

Mcfunction-Debugger has full support for calling functions with entity selectors (such as execute as @e run function ...). Just like in regular Minecraft the called function will be executed completely for the first entity, then for the second entity and so on. This differs from debugging approaches using command blocks that execute the first command of the called function for all entites, then the second command for all entities and so on.

Function execution context

A function is always executed within a context that is defined when calling the function. This context consists of:

  • executor: The entity executing the function is referred to by the entity selector @s. It is changed when calling a function with execute as.
  • position: The position in the Minecraft world at which the function is executed is referred to by relative coordinates like ~ ~ ~. It is changed when calling a function with execute positioned or execute at.
  • rotation: The look rotation of the function is changed when calling a function with execute rotated or execute at. It affects local coordinates such as ^ ^1 ^.
  • dimension: The dimension in which the function executes such as overworld, the_nether and the_end. It is changed when calling a function with execute in or execute at.
  • anchor: The anchor (eyes or feet) defines the origin of local coordinates. It defines whether a local position like ^ ^ ^ is relative to the executors eyes or feet. It is changed when calling a function with execute anchored.

Mcfunction-Debugger correctly stores and restores the entire execution context of each function throughout the debugging session.

When execution is suspended, the position and rotation are marked using particles:

view position and rotation

Missing and Invalid Functions

Both Minecraft and Mcfunction-Debugger completely skip the execution of functions that are missing or contain an invalid command. This means that breakpoints in an invalid function will be ignored. To highlight this fact Mcfunction-Debugger writes to the debug console whenever a function call is skipped.

Schedule and Timing

The schedule command is fully supported. While execution is suspended, all schedule timers are suspended too, so the datapack does not behave differently just because you hit a breakpoint. Additionally the age of all area effect clouds is frozen to make sure they don't die. The age of other entites and the gametime are currently not frozen (see #24 and #18) and the random tick speed is not yet set to zero (see #14).

Internal Debug Entities are Hidden

Mcfunction-Debugger internally uses various entities. To make sure they don't interfere with the debugged datapack these internal entities are hidden from all entity selectors.

There is one exception and that is Minects connection entity. Minect requires an area effect cloud tagged minect_connection to operate. We do not hide this entity when using the debugger, because it is also visible when a player manually executes functions. It is fine to kill the connection entity, since it will be recreated in the next tick.

So there is no problem if your datapack contains commands like kill @e[type=!player]. Just be aware that commands like execute as @e run ... will target one more entity once Minect ist installed.

Caveats

Unfortunately a program can always behave slightly differently when being debugged. Here are some problems you might encounter with Mcfunction-Debugger.

Calling Functions in Other Datapacks is not Supported Yet

Currently Mcfunction-Debugger only considers the datapack that contains the debugged function (see #9). This means that calls to functions in other datapacks will be skipped as if the function was missing.

To work around this limitation you can merge all datapacks into one big datapack before debugging.

Operating on Dead Entities

In a Minecraft function you can kill an entity and then continue using it. For example, consider the following datapack:

example:sacrifice_pig:

summon pig ~ ~ ~ {Tags: [sacrifice]}
execute as @e[type=pig,tag=sacrifice] run function example:perform_necromancy

example:perform_necromancy:

say I am still alive
function example:kill_me
say I am dead inside

example:kill_me:

kill @s

After the function example:kill_me is executed the pig is dead, yet it speaks to us from the other side. Currently this cannot be handled by the debugger. If you try to debug the function example:sacrifice_pig it will crash:

[Info] Started debugging example:sacrifice_pig
[Pig] I am still alive
[Error] Selected entity was killed!

Hitting the Maximum Command Chain Length

By default Minecraft only executes up to 65536 commands per tick. Since the debugger needs to run many commands in addition to the commands of your datapack, you might hit this limit when debugging a very large datapack. When this happens the debug session will be left in an undefined state. If there are no schedules, the development tool will wait indefinitely and you will have to manually terminate the debug session. If there are upcoming schedules they may crash the debug session or exhibit unexpected behaviour.

To avoid hitting the command limit you can add more breakpoints, step through the function line by line, or increase the command limit:

/gamerule maxCommandChainLength 2147483647

Chunkloading

If a chunk that contains an entity required for debugging is unloaded, while a function is suspended on a breakpoint, the debug session will crash, when you try to resume the execution.

This can for example happen if you go far away or if the function operates in a chunk that is only loaded temporarily (for instance by a teleport command or by going through a portal).

Installation

Using precompiled binaries

Precompiled binaries are available under releases.

Installing from source

Mcfunction-Debugger is written in Rust so to build it from source you need to install Rust.

You can then install it from crates.io by running:

cargo install mcfunction-debugger

Or from Codeberg by running:

cargo install --git https://codeberg.org/vanilla-technologies/mcfunction-debugger.git

To uninstall run:

cargo uninstall mcfunction-debugger

For Developers

Mcfunction-Debugger only supports the single session mode with communication via stdin and stdout. To start executing an mcfunction file the development tool needs to send a launch request (the attach request is not supported).

Launch Arguments

In order for the debug adapter to connect to Minecraft it needs a few arguments as part of the launch request:

program

Path to the mcfunction file to debug. The mcfunction file must be contained in a datapack with a pack.mcmeta file.

minecraftWorldDir

The directory containing the Minecraft world the debug adapter should connect to.

For single player this is typically a directory within the saves directory:

  • Windows: %appdata%\.minecraft\saves\
  • GNU/Linux: ~/.minecraft/saves/
  • Mac: ~/Library/Application Support/minecraft/saves/

For servers it is specified in server.properties.

minecraftLogFile

The path to Minecraft's log file.

For single player this is typically at these locations:

  • Windows: %appdata%\.minecraft\logs\latest.log
  • GNU/Linux: ~/.minecraft/logs/latest.log
  • Mac: ~/Library/Application Support/minecraft/logs/latest.log

For servers it is at logs/latest.log in the server directory.

Example

{
  "program": "C:/Users/Herobrine/my_datapack/data/my_namespace/functions/main.mcfunction",
  "minecraftWorldDir": "C:/Users/Herobrine/AppData/Roaming/.minecraft/saves/New World",
  "minecraftLogFile": "C:/Users/Herobrine/AppData/Roaming/.minecraft/logs/latest.log"
}

Command Line Interface

mcfunction-debugger [FLAGS] [OPTIONS]

Flags

--help

Prints help information.

--version

Prints version information.

Options

--log-file

Path to a log file. If specified the debug adapter will create this file on startup and write log messages to it.

--log-level

The log level can also be configured via the environment variable LOG_LEVEL. Defaults to INFO.

Dependencies

~12–22MB
~367K SLoC