Skip to content

Commit 2933d1f

Browse files
committed
Resolved an issue that caused mods to be packaged incompletely.
Corrected a bug whereby directly accessing fields via their 'named' identifiers failed in the runtime environment. Updated the README.md.
1 parent 09c8189 commit 2933d1f

18 files changed

Lines changed: 318 additions & 103 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ hs_err_*.log
3838
replay_*.log
3939
*.hprof
4040
*.jfr
41+
42+
README.md

README.md

Lines changed: 191 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,233 @@
1-
> [!NOTE]
2-
> This project is now being developed by Bookshelf team and has been renamed to Sniffer. Bookshelf has added more features to Sniffer and developed a VSCode plugin! So head over to their repository to stay updated~
3-
>
4-
> It's worth mentioning that the original author is still actively contributing to Sniffer's development~
5-
>
6-
> Sniffer: <https://github.com/mcbookshelf/sniffer>
1+
# Sniffer
72

3+
## Overview
84

9-
# Datapack Breakpoint
5+
Sniffer is a debug adapter for Minecraft datapacks that allows you to debug your `.mcfunction` files directly from Visual Studio Code. It provides features like breakpoints, step execution, and variable inspection to make datapack development easier and more efficient.
106

11-
English | [简体中文](README_zh.md)
7+
## Features
128

13-
## Introduce
9+
- Set breakpoints in `.mcfunction` files
10+
- Connect to a running Minecraft instance
11+
- Inspect game state during debugging
12+
- Step through command execution
13+
- Path mapping between Minecraft and local files
14+
- Debug commands started with `#!`
15+
- Assert command
16+
- Log command (Much more powerful than `tellraw` or `say`)
17+
- Hot reload datapack changes
1418

15-
This is a fabric mod for Minecraft 1.21, which allows you to set breakpoints in the game and "freeze" the game when
16-
the breakpoint is reached.
19+
## Requirements
1720

18-
## Usage
21+
- Minecraft with Fabric Loader
22+
- Visual Studio Code
1923

20-
* Set a breakpoint
2124

22-
In datapack, you can insert `#breakpoint` into .mcfunction file to set a breakpoint. For example:
25+
<!-- ## Installation
2326
24-
```mcfunction
25-
#test:test
27+
### Minecraft Mod Installation
28+
29+
1. Install [Fabric Loader](https://fabricmc.net/use/) for your Minecraft version
30+
2. Download the Sniffer mod JAR from the [releases page](https://github.com/mcbookshelf/sniffer/releases)
31+
3. Place the JAR file in your Minecraft `mods` folder
32+
4. Launch Minecraft with Fabric
33+
34+
### VSCode Extension Installation
35+
36+
1. Open Visual Studio Code
37+
2. Go to the Extensions view (Ctrl+Shift+X)
38+
3. Search for "Sniffer"
39+
4. Click Install -->
40+
41+
## Mod Configuration
42+
The mod can be configured through the in-game configuration screen, accessible via Mod Menu.
43+
You can also configure the mod in the `config/sniffer.json` file.
44+
The following options are available:
45+
46+
### Debug Server Settings
47+
- **Server Port**: The port number for the debug server (default: 25599)
48+
- **Server path**: The path to the debug server (default: `/dap`)
49+
50+
## Connecting to Minecraft
51+
52+
1. Open your datapack project in VSCode
53+
2. Create a `.vscode/launch.json` file with the following configuration:
54+
55+
```json
56+
{
57+
"version": "0.2.0",
58+
"configurations": [
59+
{
60+
"type": "sniffer",
61+
"request": "attach",
62+
"name": "Connect to Minecraft",
63+
"address": "ws://localhost:25599/dap"
64+
}
65+
]
66+
}
67+
```
68+
69+
3. Start Minecraft with the Sniffer mod installed
70+
4. In VSCode, press F5 or click the "Run and Debug" button
71+
5. Select "Connect to Minecraft" from the dropdown menu
72+
73+
You can now place breakpoints in your `.mcfunction` files and execute it from the game to step through the code.
74+
75+
## Usage in Minecraft
76+
77+
### Breakpoint
78+
79+
The debugger can be controlled directly from Minecraft using the following commands:
80+
81+
- `/breakpoint continue`: Resume execution after hitting a breakpoint
82+
- `/breakpoint step`: Execute the next command and pause
83+
- `/breakpoint step_over`: Skip to the next command in the current function
84+
- `/breakpoint step_out`: Continue execution until the current function returns
2685

86+
All commands require operator permissions (level 2) to use.
87+
88+
When execution is paused at a breakpoint, the gametick will be freezed.
89+
90+
### Debug Command
91+
92+
Comment started with `#!` are recognized as debug commands. They will be executed as same as the normal commands in the game but without Sniffer, those debug commands will only be considered as comments.
93+
94+
Sniffer adds some useful commands for datapack developer. By using debug command, you can use those commands in your datapack without worrying about breaking your datapack in vanilla Minecraft.
95+
96+
For example, if you want to place a breakpoint in your function:
97+
98+
```mcfunction
2799
say 1
28100
say 2
29-
#breakpoint
30101
say 3
102+
103+
#The function will stop at the line with `#!breakpoint`
104+
#!breakpoint
31105
say 4
106+
say 5
32107
```
33108

34-
In this case, after the game executes `say 2`, the game will be "frozen" because it meets the breakpoint.
109+
Also you can execute normal commands in debug command line:
35110

36-
When the game is "frozen", you can still move around, do whatever you want, just like execute the command `tick freeze`.
37-
So you can check the game state, or do some debugging.
38-
39-
* Step
111+
```mcfunction
112+
say 1
113+
say 2
114+
say 3
40115
41-
When the game is "frozen", you can use the command `/breakpoint step` to execute the next command. In above example,
42-
after the game meets the breakpoint, you can use `/breakpoint step` to execute `say 3`, and then use `/breakpoint step`
43-
to execute `say 4`. When all commands are executed, the game will be unfrozen and continue running.
116+
#!execute if score @s test matches 1..10 run breakpoint
117+
say 4
118+
say 5
119+
```
44120

45-
* Continue
121+
This breakpoint will only be triggerred when the score of the executor in test object is between 1 and 10.
46122

47-
When the game is "frozen", you can use the command `/breakpoint move` to unfreeze the game and continue running.
123+
### Assert
48124

49-
* Get Macro Arguments
125+
Assert command is used to check if a condition is true. If the condition is false, the mod will print an error message to the game chat.
50126

51-
By using `/breakpoint get <key>`, you can get the value of the macro argument if the game is executing a macro function.
52-
For example:
127+
Syntax: `assert <expr>`
53128

54129
```mcfunction
55-
#test:test_macro
130+
say 1
131+
say 2
132+
say 3
56133
57-
say start
58-
#breakpoint
59-
$say $(msg)
60-
say end
134+
#!assert {(score @s test ) <= 10}
135+
say 4
136+
say 5
61137
```
62138

63-
After executing `function test:test_macro {"msg":"test"}`, we passed the value `test` to the macro argument `msg` and
64-
then the game will pause before `$say $(msg)`. At this time, you can use `/breakpoint get msg` to get the value `test`.
139+
This assert command will check if the score of the executor in test object is less than or equal to 10.
65140

66-
* Get Function Stack
141+
#### Expression Argument
67142

68-
By using `/breakpoint stack`, you can get the function stack of the current game. For example, if we have following two
69-
functions:
143+
In sniffer command, you can use complex expression as command argument. Normally, an expression is surrounded by curly braces `{}`. Within an expression, you may obtain a value from an argument enclosed in parentheses and employ it in the expression’s evaluation.
70144

71-
```mcfunction
72-
#test:test1
145+
Supported expression arguments:
146+
- `score <score_holder> <objective>`: The same as `execute if score`. Returns an int value.
147+
- `data entity <entity>/storage <id>/block <pos> path`: The same as `execute if data`. Returns a nbt value.
148+
- `name <entity>`: return a text components with all names of the entities.
149+
150+
> [!WARNING]
151+
> Owing to inherent limitations in the command-parsing system, the closing parenthesis of an expression argument must be preceded by at least one space
152+
153+
Supported operators:
154+
- `+`, `-`, `*`, `/`, `%`: Arithmetic operators
155+
- `==`, `!=`, `<`, `<=`, `>`, `>=`: Comparison operators
156+
- `&&`, `||`, `!`: Logical operators
157+
- `is`: Check if a value is a certain type. Returns a boolean value. Available types: `nbt`, `text`, `string`, `number`, `byte`, `short`, `int`, `long`, `float`, `double`, `int_array`, `long_array`,` byte_array`, `list`, `compound`, `
158+
159+
> [!Note]
160+
> No operator possesses intrinsic precedence; expressions are evaluated strictly from left to right. By nesting subexpressions, however, you can enforce prioritized evaluation
73161
162+
### Log
163+
164+
Log command is used to print a message to the game chat. The log message can contain expression arguments.
165+
166+
Syntax: `log <log>`
167+
168+
```mcfunction
74169
say 1
75-
function test:test2
76170
say 2
171+
say 3
77172
78-
#test: test2
79-
say A
80-
#breakpoint
81-
say B
173+
#!log The score of @s in test objective is {(score @s test )}
174+
say 4
175+
say 5
82176
```
83177

84-
When the game pauses at the breakpoint, you can use `/breakpoint stack` and the function stack will be printed in the
85-
chat screen:
178+
This log command will print `The score of @s in test objective is 10` to the game chat.
179+
180+
### Hot reload
181+
182+
Sniffer can watch the changes of the datapack files (only `*.mcfunction` files currently) and reload them automatically. Hot reloading will not reload all resources, as it will only reload the function that is changed. It will not trigger functions with `#minecraft:load` tag too. In large-scale datapack projects, or if you installed kubejs mod, hot reloading can markedly mitigate unnecessary reload-induced pauses and stuttering.
86183

184+
Hot reloading will watch any changes, create or delete of the datapack files.
185+
186+
Hot reloading is not enabled by default. You must use the `watch start` command to start watching a datapack. Each time you re-enter the world, the watcher must be reestablished. After making any changes in the datapack, you can use the `watch reload` command to trigger hot reloading and apply changes in your datapack. A message will be sent to the game chat to tell you what changes have been actually applied.
187+
188+
If the command parser encounters any error while parsing a function, the changes in the function will not apply, and a message will be sent to the game chat to tell you the error.
189+
190+
Command Syntax:
191+
192+
- `watch start <Datapack Folder Name>`: Start watching a datapack folder.
193+
- `watch stop <Datapack Folder Name>`: Stop watching a datapack folder.
194+
- `watch reload`: Trigger the hot loading to apply changes.
195+
- `watch auto [true|false]`: Set whether auto reloading is enabled. Auto reloading will apply any change once the watcher detects it. Default is `false`. If value is not provided, it will tell you if the auto reloading is enabled.
196+
197+
## Development
198+
199+
### Project Structure
200+
201+
- `src/main`: Main mod code for Minecraft
202+
- `src/client`: Client-side mod code
203+
- `vscode`: VSCode extension source code
204+
205+
### Building the Project
206+
207+
To build the Minecraft mod:
208+
209+
```bash
210+
./gradlew build
87211
```
88-
test:test2
89-
test:test
90212

213+
To build the VSCode extension:
214+
215+
```bash
216+
cd vscode
217+
npm install
218+
npm run build
91219
```
92220

93-
* Run command in current context
221+
## License
222+
223+
This project is licensed under the MPL-2.0 License - see the [LICENSE](LICENSE) file for details.
224+
225+
## Contributing
226+
227+
Contributions are welcome! Please feel free to submit a Pull Request.
228+
229+
## Acknowledgements
94230

95-
By using `/breakpoint run <command>`, you can run any command in the current context, just like `execute ... run ...`.
231+
- [Fabric](https://fabricmc.net/) - Mod loader for Minecraft
232+
- [VSCode Debug Adapter](https://code.visualstudio.com/api/extension-guides/debugger-extension) - VSCode debugging API
233+
- [Datapack Debugger](https://github.com/Alumopper/Datapack-Debugger/) by [Alumopper](https://github.com/Alumopper) - Original implementation of the debugger, without the DAP layer

build.gradle

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
id 'fabric-loom' version '1.11-SNAPSHOT'
33
id 'maven-publish'
44
id "org.jetbrains.kotlin.jvm" version "2.1.0"
5+
id "com.gradleup.shadow" version "9.2.2"
56
}
67

78
version = project.mod_version
@@ -37,6 +38,12 @@ loom {
3738

3839
}
3940

41+
configurations {
42+
shadowImpl
43+
}
44+
45+
configurations.shadowImpl.extendsFrom(configurations.implementation)
46+
4047
dependencies {
4148
implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0'
4249
implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j.debug:0.24.0'
@@ -52,7 +59,6 @@ dependencies {
5259

5360
// Fabric API. This is technically optional, but you probably want it anyway.
5461
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
55-
modImplementation "net.fabricmc.fabric-api:fabric-key-binding-api-v1:${project.fabric_version}"
5662
modImplementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}"
5763

5864
// Added Cloth Config and ModMenu
@@ -80,6 +86,22 @@ tasks.withType(JavaCompile).configureEach {
8086
it.options.release = 21
8187
}
8288

89+
90+
tasks.named("shadowJar") {
91+
archiveClassifier.set("all")
92+
from(sourceSets.main.output)
93+
from(sourceSets.client.output)
94+
configurations = [project.configurations.shadowImpl]
95+
}
96+
97+
tasks.named("remapJar") {
98+
dependsOn("shadowJar")
99+
inputFile.set(tasks.named("shadowJar").flatMap { it.archiveFile })
100+
archiveClassifier.set("")
101+
}
102+
103+
tasks.build { dependsOn(tasks.remapJar) }
104+
83105
java {
84106
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
85107
// if it is present.

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ loom_version=1.13-SNAPSHOT
1313
fabric_kotlin_version=1.13.0+kotlin.2.1.0
1414

1515
# Mod Properties
16-
mod_version=0.1.0
16+
mod_version=0.2.0
1717
maven_group=net.gunivers
1818
archives_base_name=sniffer
1919

src/main/java/net/gunivers/sniffer/Utils.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,6 @@
1515
*/
1616
public class Utils {
1717

18-
/**
19-
* Retrieves the identifier from an ExpandedMacro function.
20-
* This method uses EncapsulationBreaker to access the private field
21-
* that contains the function's identifier.
22-
*
23-
* @param function The ExpandedMacro function to get the ID from
24-
* @return The Identifier of the function, or a fallback identifier if not found
25-
*/
26-
public static Identifier getId(ExpandedMacro<?> function) {
27-
return ReflectUtil.getT(function, "functionIdentifier", Identifier.class).getDataOrElse(Identifier.of("foo:bar"));
28-
}
29-
3018
private static final String MESSAGE_PREFIX = "[Sniffer] ";
3119

3220
public static Text addSnifferPrefix(Text text) {

src/main/java/net/gunivers/sniffer/mixin/CommandExecutionContextMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ private Optional<SourcedCommandAction<?>> getNextCommand(CommandQueueEntry<T> co
418418
return Optional.empty();
419419
}
420420

421-
int index = ReflectUtil.getT(steppedAction, "nextActionIndex", int.class).getData();
421+
int index = ((SteppedCommandActionAccessors) steppedAction).getNextActionIndex();
422422
if(index < 0) {
423423
return Optional.empty();
424424
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package net.gunivers.sniffer.mixin;
2+
3+
import net.minecraft.server.function.CommandFunctionManager;
4+
import net.minecraft.server.function.FunctionLoader;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.gen.Accessor;
7+
8+
@Mixin(CommandFunctionManager.class)
9+
public interface CommandFunctionManagerAccessors {
10+
11+
@Accessor("loader")
12+
FunctionLoader getLoader();
13+
}

0 commit comments

Comments
 (0)