Skip to content

Commit 6b4378b

Browse files
committed
Add Function of Clearing Anchors and Saving Structure
1 parent 1fc8f03 commit 6b4378b

7 files changed

Lines changed: 191 additions & 12 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Of course, this mod can also be used independently. As long as you can generate
3333

3434
* Keys are block IDs, and values are arrays of relative coordinates.
3535
* You can use [**Minecraftify2.0**](https://github.com/Ivans-11/Minecraftify2/releases) to generate JSON files from 3D models. See its [repository documentation](https://github.com/Ivans-11/Minecraftify2) for details.
36+
* You can also use the `/builder save` command to export JSON files from the current world. See the following content for specific usage.
3637

3738
2. Place a `builder:anchor_block` in the game world to define the reference origin and orientation.
3839

@@ -55,6 +56,14 @@ Of course, this mod can also be used independently. As long as you can generate
5556
```
5657

5758
This will revert the most recent build (up to 3 steps).
59+
5. If you need to export a build from the current world, enter:
60+
61+
```
62+
/builder save <x> <y> <z> <name>
63+
```
64+
Where `<x> <y> <z>` represents the selected area range.
65+
The mod will use the nearest anchor block to the player as the starting point `(0,0,0)` and export the block data in the range from `(0,0,0)` to `(x,y,z)` to the `config/mybuilds/<name>.json` file.
66+
Note that the x-axis corresponds to the red axis, the y-axis corresponds to the green axis, and the z-axis corresponds to the blue axis. The format of the exported JSON file is the same as that of the imported one.
5867

5968
## Command List
6069

@@ -66,6 +75,12 @@ Of course, this mod can also be used independently. As long as you can generate
6675
List the coordinates of all anchor blocks
6776
* `/builder undo`
6877
Undo the most recent build operation
78+
* `/builder clear`
79+
Clear all anchor blocks
80+
* `/builder save <x> <y> <z> <name>`
81+
Export the block data within the specified area to the `config/mybuilds/<name>.json` file.
82+
* `/builder help`
83+
Show help information
6984

7085
## Notes
7186

@@ -77,6 +92,8 @@ Of course, this mod can also be used independently. As long as you can generate
7792

7893
* Undo only applies to blocks placed using the `/builder place` command, and does not affect manually placed blocks.
7994

95+
* Since JSON files only record the relative coordinates of blocks, it is not possible to import some special blocks (such as stairs, doors, etc., which contain more information) accurately.
96+
8097
## Acknowledgements
8198

8299
This project is based on [FabricMC/fabric-example-mod](https://github.com/FabricMC/fabric-example-mod).

README_zh.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
}
3131
```
3232
- 其中,键为方块 ID,值为相对坐标数组。
33-
- 可使用工具 [**Minecraftify2.0**](https://github.com/Ivans-11/Minecraftify2/releases) 从三维模型文件生成,具体使用方法见其[仓库说明](https://github.com/Ivans-11/Minecraftify2)
33+
- 可使用工具 [**Minecraftify2.0**](https://github.com/Ivans-11/Minecraftify2/releases) 从三维模型文件生成,具体使用方法见其[仓库说明](https://github.com/Ivans-11/Minecraftify2)
34+
- 也可利用 `/builder save` 命令从当前世界导出 JSON 文件,具体使用方法见后文。
3435
2. 在游戏中放置一个 `builder:anchor_block`(锚点方块) ,用于确定参考坐标系原点和朝向。
3536

3637
![](image/anchor.png)
@@ -50,6 +51,14 @@
5051
```
5152

5253
将撤回最近一次生成操作(最多支持 3 步)。
54+
5. 如果需要导出当前世界的建筑,输入:
55+
56+
```
57+
/builder save <x> <y> <z> <name>
58+
```
59+
其中,`<x> <y> <z>` 为选择的区域范围
60+
模组将以距离玩家最近的锚点为起始点 `(0,0,0)` ,导出 `(0,0,0)``(x,y,z)` 范围内的方块数据到 `config/mybuilds/<name>.json` 文件中。
61+
注意,x 为红轴方向,y 为绿轴方向,z 为蓝轴方向。导出的 JSON 文件格式与导入的格式相同。
5362

5463
## 命令列表
5564

@@ -61,14 +70,21 @@
6170
列出所有锚点方块的坐标
6271
- `/builder undo`
6372
撤回最近一次生成操作
73+
- `/builder clear`
74+
清除所有锚点方块
75+
- `/builder save <x> <y> <z> <name>`
76+
导出指定区域内的方块数据到 `config/mybuilds/<name>.json` 文件中
77+
- `/builder help`
78+
显示帮助信息
6479

6580
## 注意
6681

6782
- 必须先放置锚点方块才能生成建筑,生成建筑时会自动寻找距离玩家最近的锚点方块。
6883
- 锚点方块可在建筑方块物品栏中找到。
6984

7085
![](image/item_zh.png)
71-
- 撤回仅能恢复由 `/build place` 命令生成的方块,不影响手动放置的方块。
86+
- 撤回仅能恢复由 `/builder place` 命令生成的方块,不影响手动放置的方块。
87+
- 由于 JSON 文件只记录了方块的相对坐标,因此对于一些特殊的方块(如楼梯、门等包含更多信息的方块),无法做到精确导入。
7288

7389
## 致谢
7490

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ loader_version=0.16.14
1010
loom_version=1.11-SNAPSHOT
1111

1212
# Mod Properties
13-
mod_version=1.0.0
13+
mod_version=1.0.1
1414
maven_group=com.example
1515
archives_base_name=builder
1616

src/main/java/com/example/BuildHandler.java

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.example;
22

3+
import com.example.block.AnchorBlock;
34
import com.example.block.AnchorState;
45
import com.google.gson.*;
56
import net.minecraft.block.Block;
@@ -16,6 +17,7 @@
1617
import net.minecraft.text.Text;
1718

1819
import java.io.FileReader;
20+
import java.io.FileWriter;
1921
import java.io.File;
2022
import java.nio.file.Path;
2123
import java.nio.file.Paths;
@@ -52,13 +54,31 @@ public static void listBuilds(ServerCommandSource source) {
5254
public static void listAnchors(ServerCommandSource source) {
5355
ServerPlayerEntity player = source.getPlayer();
5456
ServerWorld world = source.getWorld();
55-
AnchorState cache = AnchorState.getState(world.getServer());// Get cache
56-
List<BlockPos> anchors = cache.getAnchors();// Get anchors
57+
List<BlockPos> anchors = getAnchors(world);// Get anchors
58+
if (anchors.isEmpty()) {
59+
player.sendMessage(Text.literal("No Anchors Found"), false);
60+
return;
61+
}
62+
player.sendMessage(Text.literal("Anchors:"), false);
5763
for (BlockPos p : anchors) {
5864
player.sendMessage(Text.literal(p.toString()), false);
5965
}
6066
}
6167

68+
// Clear all AnchorBlocks
69+
public static void clearAnchors(ServerCommandSource source) {
70+
ServerPlayerEntity player = source.getPlayer();
71+
ServerWorld world = source.getWorld();
72+
List<BlockPos> anchors = getAnchors(world);// Get anchors
73+
for (BlockPos p : anchors) {
74+
BlockState state = world.getBlockState(p);
75+
if (state.getBlock() instanceof AnchorBlock) {
76+
world.setBlockState(p, Blocks.AIR.getDefaultState());// Set to air
77+
}
78+
}
79+
player.sendMessage(Text.literal("All Anchors Cleared"), false);
80+
}
81+
6282
// Load and place a build from a JSON file
6383
public static void loadAndPlace(ServerCommandSource source, String filename) {
6484
ServerWorld world = source.getWorld();
@@ -73,7 +93,7 @@ public static void loadAndPlace(ServerCommandSource source, String filename) {
7393
}
7494

7595
BlockState anchorState = world.getBlockState(anchorPos);
76-
if (!(anchorState.getBlock() instanceof com.example.block.AnchorBlock)) {
96+
if (!(anchorState.getBlock() instanceof AnchorBlock)) {
7797
// Not found anchor
7898
player.sendMessage(Text.literal("Invalid Anchor, Please Place Anchor First"), false);
7999
return;
@@ -94,6 +114,28 @@ public static void loadAndPlace(ServerCommandSource source, String filename) {
94114
if (block == Blocks.AIR) continue;
95115

96116
JsonArray coords = entry.getValue().getAsJsonArray();
117+
118+
// If it is an anchor block, record the position
119+
if (block instanceof AnchorBlock) {
120+
player.sendMessage(Text.literal("Anchor Block Found: " + origin), false);
121+
for (JsonElement coordEl : coords) {
122+
JsonArray arr = coordEl.getAsJsonArray();
123+
int dx = arr.get(0).getAsInt();
124+
int dy = arr.get(1).getAsInt();
125+
int dz = arr.get(2).getAsInt();
126+
127+
BlockPos placePos = transformPos(origin, dx, dy, dz, facing);
128+
129+
BlockState oldState = world.getBlockState(placePos);
130+
snapshots.add(new UndoManager.BlockSnapshot(placePos, oldState));
131+
world.setBlockState(placePos, block.getDefaultState());
132+
player.sendMessage(Text.literal("Anchor Block Placed: " + placePos), false);
133+
cache.add(placePos);
134+
player.sendMessage(Text.literal("Anchor Block Added: " + placePos), false);
135+
}
136+
continue;
137+
}
138+
97139
for (JsonElement coordEl : coords) {
98140
JsonArray arr = coordEl.getAsJsonArray();
99141
int dx = arr.get(0).getAsInt();
@@ -118,6 +160,68 @@ public static void loadAndPlace(ServerCommandSource source, String filename) {
118160
}
119161
}
120162

163+
// Save structure to a JSON file
164+
public static void saveStructure(ServerCommandSource source, int x, int y, int z, String name) {
165+
ServerWorld world = source.getWorld();
166+
ServerPlayerEntity player = source.getPlayer();
167+
BlockPos playerPos = player.getBlockPos();
168+
169+
AnchorState cache = AnchorState.getState(world.getServer());// Get cache
170+
BlockPos anchorPos = findNearest(cache.getAnchors(), playerPos);// Find nearest anchor
171+
if (anchorPos == null) {
172+
player.sendMessage(Text.literal("Not Found Anchor, Please Place Anchor First"), false);
173+
return;
174+
}
175+
176+
BlockState anchorState = world.getBlockState(anchorPos);
177+
if (!(anchorState.getBlock() instanceof AnchorBlock)) {
178+
// Not found anchor
179+
player.sendMessage(Text.literal("Invalid Anchor, Please Place Anchor First"), false);
180+
return;
181+
}
182+
183+
Direction facing = anchorState.get(Properties.HORIZONTAL_FACING);
184+
BlockPos origin = anchorPos;
185+
186+
// Record the structure
187+
JsonObject obj = new JsonObject();
188+
for (int dx = 0; dx <= x; dx++) {
189+
for (int dy = 0; dy <= y; dy++) {
190+
for (int dz = 0; dz <= z; dz++) {
191+
if (dx == 0 && dy == 0 && dz == 0) continue;
192+
BlockPos placePos = transformPos(origin, dx, dy, dz, facing);
193+
BlockState state = world.getBlockState(placePos);
194+
Block block = state.getBlock();
195+
if (block != Blocks.AIR) {
196+
String blockId = Registries.BLOCK.getId(block).toString();
197+
JsonArray arr = new JsonArray();
198+
arr.add(dx);
199+
arr.add(dy);
200+
arr.add(dz);
201+
if (obj.has(blockId)) {
202+
obj.get(blockId).getAsJsonArray().add(arr);
203+
} else {
204+
JsonArray coords = new JsonArray();
205+
coords.add(arr);
206+
obj.add(blockId, coords);
207+
}
208+
}
209+
}
210+
}
211+
}
212+
213+
// Save to file
214+
Path filePath = Paths.get(PARENT_PATH, name + ".json");
215+
try (FileWriter writer = new FileWriter(filePath.toFile())) {
216+
writer.write(obj.toString());
217+
player.sendMessage(Text.literal("Save Success: " + name), false);
218+
} catch (Exception e) {
219+
player.sendMessage(Text.literal("Save Failed: " + e.getMessage()), false);
220+
e.printStackTrace();
221+
}
222+
}
223+
224+
121225
// Transform position based on facing
122226
private static BlockPos transformPos(BlockPos origin, int dx, int dy, int dz, Direction facing) {
123227
switch (facing) {
@@ -142,4 +246,10 @@ private static BlockPos findNearest(List<BlockPos> anchors, BlockPos playerPos)
142246
}
143247
return bestPos;
144248
}
249+
250+
private static List<BlockPos> getAnchors(ServerWorld world) {
251+
AnchorState cache = AnchorState.getState(world.getServer());// Get cache
252+
// Get copy of anchors to avoid ConcurrentModificationException
253+
return new ArrayList<>(cache.getAnchors());
254+
}
145255
}

src/main/java/com/example/BuilderMod.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import net.fabricmc.api.ModInitializer;
44
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
5+
6+
import com.mojang.brigadier.arguments.IntegerArgumentType;
57
import com.mojang.brigadier.arguments.StringArgumentType;
68
import net.minecraft.server.command.CommandManager;
79
import net.minecraft.text.Text;
@@ -43,24 +45,51 @@ public void onInitialize() {
4345
})
4446
)
4547
.then(literal("anchors")// List anchor positions
46-
.executes(context -> {
48+
.executes(context -> {
4749
BuildHandler.listAnchors(context.getSource());
4850
return 1;
4951
})
5052
)
5153
.then(literal("undo")// Undo the last build action
52-
.executes(context -> {
54+
.executes(context -> {
5355
UndoManager.undo(context.getSource().getPlayer(), context.getSource().getWorld());
5456
return 1;
5557
})
5658
)
59+
.then(literal("clear")// Clear all anchors
60+
.executes(context -> {
61+
BuildHandler.clearAnchors(context.getSource());
62+
return 1;
63+
})
64+
)
65+
.then(CommandManager.literal("save")// Save structure to file
66+
.then(CommandManager.argument("x", IntegerArgumentType.integer())
67+
.then(CommandManager.argument("y", IntegerArgumentType.integer())
68+
.then(CommandManager.argument("z", IntegerArgumentType.integer())
69+
.then(CommandManager.argument("name", StringArgumentType.string())
70+
.executes(context -> {
71+
int x = IntegerArgumentType.getInteger(context, "x");
72+
int y = IntegerArgumentType.getInteger(context, "y");
73+
int z = IntegerArgumentType.getInteger(context, "z");
74+
String name = StringArgumentType.getString(context, "name");
75+
76+
BuildHandler.saveStructure(context.getSource(), x, y, z, name);
77+
return 1;
78+
})
79+
)
80+
)
81+
)
82+
)
83+
)
5784
.then(literal("help")// Display help message
58-
.executes(context -> {
85+
.executes(context -> {
5986
context.getSource().sendFeedback(() -> Text.literal("Builder Mod Commands:"), false);
60-
context.getSource().sendFeedback(() -> Text.literal("/builder place <filename> - Place blocks based on the specified file."), false);
87+
context.getSource().sendFeedback(() -> Text.literal("/builder place <filename> - Place blocks based on the specified JSON file."), false);
6188
context.getSource().sendFeedback(() -> Text.literal("/builder list - List available build files."), false);
6289
context.getSource().sendFeedback(() -> Text.literal("/builder anchors - List anchor positions."), false);
6390
context.getSource().sendFeedback(() -> Text.literal("/builder undo - Undo the last build action."), false);
91+
context.getSource().sendFeedback(() -> Text.literal("/builder clear - Clear all anchors."), false);
92+
context.getSource().sendFeedback(() -> Text.literal("/builder save <x> <y> <z> <name> - Save the structure within the specified area to JSON file."), false);
6493
return 1;
6594
})
6695
)

src/main/java/com/example/UndoManager.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import java.util.Map;
88
import java.util.UUID;
99

10+
import com.example.block.AnchorBlock;
11+
import com.example.block.AnchorState;
12+
1013
import net.minecraft.block.BlockState;
1114
//import net.minecraft.block.entity.BlockEntity;
1215
//import net.minecraft.nbt.NbtCompound;
@@ -47,6 +50,10 @@ public static void undo(ServerPlayerEntity player, ServerWorld world) {
4750
//be.readNbt(snap.nbt);
4851
//}
4952
//}
53+
// If it is an anchor block, record the position
54+
if (snap.state.getBlock() instanceof AnchorBlock) {
55+
AnchorState.getState(world.getServer()).add(snap.pos);
56+
}
5057
}
5158

5259
player.sendMessage(Text.literal("Undo Success"), false);

src/main/resources/fabric.mod.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
"Ivans"
99
],
1010
"contact": {
11-
"homepage": "https://github.com/Ivans-11/Builder",
11+
"homepage": "https://www.curseforge.com/minecraft/mc-mods/builder-from-json",
1212
"sources": "https://github.com/Ivans-11/Builder",
1313
"issues": "https://github.com/Ivans-11/Builder/issues"
1414
},
15-
"license": "CC0-1.0",
15+
"license": "MIT",
1616
"icon": "assets/builder/icon.png",
1717
"environment": "*",
1818
"entrypoints": {

0 commit comments

Comments
 (0)