Skip to content

Commit a13fc49

Browse files
authored
Merge pull request #26 from box3lab/dev_coolfish
Dev coolfish
2 parents 68c5387 + 728dda8 commit a13fc49

8 files changed

Lines changed: 378 additions & 3 deletions

File tree

Fabric-1.21.11/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ loader_version=0.18.4
1212
loom_version=1.15-SNAPSHOT
1313

1414
# Mod Properties
15-
mod_version=1.4.2-mc1.21.11
15+
mod_version=1.4.3-mc1.21.11
1616
maven_group=com.box3lab
1717
archives_base_name=box3
1818

Fabric-1.21.11/src/main/java/com/box3lab/command/ModCommands.java

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.box3lab.command;
22

33
import java.io.IOException;
4+
import java.util.ArrayList;
45
import java.util.List;
56

67
import com.box3lab.block.BarrierVoxelBlock;
8+
import com.box3lab.register.VoxelExport;
79
import com.box3lab.register.VoxelImport;
810
import com.box3lab.util.Box3ImportFiles;
911
import com.mojang.brigadier.arguments.BoolArgumentType;
@@ -16,14 +18,21 @@
1618
import static net.minecraft.commands.Commands.argument;
1719
import static net.minecraft.commands.Commands.literal;
1820
import net.minecraft.core.BlockPos;
21+
import net.minecraft.core.registries.BuiltInRegistries;
1922
import net.minecraft.network.chat.Component;
23+
import net.minecraft.resources.Identifier;
2024
import net.minecraft.server.level.ServerLevel;
2125
import net.minecraft.server.level.ServerPlayer;
26+
import net.minecraft.world.level.block.Block;
2227

2328
public final class ModCommands {
2429
private ModCommands() {
2530
}
2631

32+
private static final String DEFAULT_EXPORT_MARKER_BLOCK = "minecraft:redstone_block";
33+
private static final int MAX_MARKER_SCAN_RADIUS = 1024;
34+
private static final int MARKER_Y_TOLERANCE = 512;
35+
2736
private static final SuggestionProvider<CommandSourceStack> BOX3_FILE_SUGGESTIONS = (context, builder) -> {
2837
try {
2938
List<String> files = Box3ImportFiles.listJsonFiles();
@@ -125,6 +134,16 @@ public static void register() {
125134
.then(literal("toggle")
126135
.executes(context -> toggleBarrierVisible(
127136
context.getSource())))));
137+
138+
dispatcher.register(
139+
literal("box3export")
140+
.executes(context -> showBox3ExportUsage(context.getSource()))
141+
.then(argument("fileName", StringArgumentType.word())
142+
.executes(context -> executeBox3ExportByMarkers(
143+
context.getSource(),
144+
StringArgumentType.getString(
145+
context,
146+
"fileName")))));
128147
});
129148
}
130149

@@ -158,6 +177,11 @@ private static int listBox3ImportFiles(CommandSourceStack source) {
158177
return 1;
159178
}
160179

180+
private static int showBox3ExportUsage(CommandSourceStack source) {
181+
source.sendFailure(Component.translatable("command.box3.box3export.usage"));
182+
return 0;
183+
}
184+
161185
private static String resolveMapName(String fileName) {
162186
if (fileName != null && fileName.startsWith("Box3-")) {
163187
String suffix = fileName.substring("Box3-".length());
@@ -221,4 +245,143 @@ private static int toggleBarrierVisible(CommandSourceStack source) {
221245
false);
222246
return 1;
223247
}
248+
249+
private static int executeBox3Export(CommandSourceStack source, String fileName,
250+
int x1, int y1, int z1, int x2, int y2, int z2) {
251+
ServerLevel level = source.getLevel();
252+
BlockPos from = new BlockPos(x1, y1, z1);
253+
BlockPos to = new BlockPos(x2, y2, z2);
254+
255+
try {
256+
VoxelExport.ExportResult result = VoxelExport.exportRegion(level, from, to, fileName);
257+
source.sendSuccess(
258+
() -> Component.translatable(
259+
"command.box3.box3export.success",
260+
result.output().toString(),
261+
result.scannedBlocks(),
262+
result.exportedBlocks()),
263+
false);
264+
} catch (Exception e) {
265+
source.sendFailure(
266+
Component.translatable("command.box3.box3export.failure", e.getMessage()));
267+
}
268+
return 1;
269+
}
270+
271+
private static int executeBox3ExportByMarkers(CommandSourceStack source, String fileName) {
272+
ServerPlayer player = source.getPlayer();
273+
if (player == null) {
274+
source.sendFailure(Component.translatable("command.box3.box3export.player_only"));
275+
return 0;
276+
}
277+
278+
Block markerBlock = resolveMarkerBlock(DEFAULT_EXPORT_MARKER_BLOCK);
279+
if (markerBlock == null) {
280+
source.sendFailure(Component.translatable("command.box3.box3export.marker_invalid",
281+
DEFAULT_EXPORT_MARKER_BLOCK));
282+
return 0;
283+
}
284+
285+
List<BlockPos> positions = findMarkerPositions(source.getLevel(), player.blockPosition(), markerBlock,
286+
MAX_MARKER_SCAN_RADIUS, MARKER_Y_TOLERANCE, 2);
287+
if (positions.size() < 2) {
288+
source.sendFailure(Component.translatable(
289+
"command.box3.box3export.marker_count_invalid",
290+
MAX_MARKER_SCAN_RADIUS,
291+
positions.size(),
292+
BuiltInRegistries.BLOCK.getKey(markerBlock).toString()));
293+
return 0;
294+
}
295+
296+
BlockPos p1 = positions.get(0);
297+
BlockPos p2 = positions.get(1);
298+
return executeBox3Export(source, fileName, p1.getX(), p1.getY(), p1.getZ(), p2.getX(), p2.getY(),
299+
p2.getZ());
300+
}
301+
302+
private static Block resolveMarkerBlock(String blockId) {
303+
Identifier id = Identifier.tryParse(blockId);
304+
if (id == null) {
305+
return null;
306+
}
307+
if (!BuiltInRegistries.BLOCK.containsKey(id)) {
308+
return null;
309+
}
310+
return BuiltInRegistries.BLOCK.get(id).map(holder -> holder.value()).orElse(null);
311+
}
312+
313+
private static List<BlockPos> findMarkerPositions(ServerLevel level, BlockPos center, Block markerBlock,
314+
int maxRadius, int yTolerance, int maxResults) {
315+
List<BlockPos> positions = new ArrayList<>();
316+
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
317+
int cx = center.getX();
318+
int cy = center.getY();
319+
int cz = center.getZ();
320+
321+
for (int radius = 0; radius <= maxRadius; radius++) {
322+
int minX = cx - radius;
323+
int maxX = cx + radius;
324+
int minZ = cz - radius;
325+
int maxZ = cz + radius;
326+
327+
if (radius == 0) {
328+
if (scanMarkerColumn(level, markerBlock, cy, yTolerance, cx, cz, cursor, positions,
329+
maxResults)) {
330+
return positions;
331+
}
332+
continue;
333+
}
334+
335+
for (int x = minX; x <= maxX; x++) {
336+
if (scanMarkerColumn(level, markerBlock, cy, yTolerance, x, minZ, cursor, positions,
337+
maxResults)) {
338+
return positions;
339+
}
340+
if (scanMarkerColumn(level, markerBlock, cy, yTolerance, x, maxZ, cursor, positions,
341+
maxResults)) {
342+
return positions;
343+
}
344+
}
345+
346+
for (int z = minZ + 1; z <= maxZ - 1; z++) {
347+
if (scanMarkerColumn(level, markerBlock, cy, yTolerance, minX, z, cursor, positions,
348+
maxResults)) {
349+
return positions;
350+
}
351+
if (scanMarkerColumn(level, markerBlock, cy, yTolerance, maxX, z, cursor, positions,
352+
maxResults)) {
353+
return positions;
354+
}
355+
}
356+
}
357+
return positions;
358+
}
359+
360+
private static boolean scanMarkerColumn(ServerLevel level, Block markerBlock, int centerY, int yTolerance, int x, int z,
361+
BlockPos.MutableBlockPos cursor, List<BlockPos> positions, int maxResults) {
362+
for (int dy = 0; dy <= yTolerance; dy++) {
363+
int y1 = centerY + dy;
364+
cursor.set(x, y1, z);
365+
if (level.hasChunkAt(cursor) && level.getBlockState(cursor).getBlock() == markerBlock) {
366+
positions.add(cursor.immutable());
367+
if (positions.size() >= maxResults) {
368+
return true;
369+
}
370+
}
371+
372+
if (dy == 0) {
373+
continue;
374+
}
375+
376+
int y2 = centerY - dy;
377+
cursor.set(x, y2, z);
378+
if (level.hasChunkAt(cursor) && level.getBlockState(cursor).getBlock() == markerBlock) {
379+
positions.add(cursor.immutable());
380+
if (positions.size() >= maxResults) {
381+
return true;
382+
}
383+
}
384+
}
385+
return false;
386+
}
224387
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package com.box3lab.register;
2+
3+
import static com.box3lab.Box3.MOD_ID;
4+
5+
import java.io.IOException;
6+
import java.io.OutputStream;
7+
import java.nio.charset.StandardCharsets;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.zip.GZIPOutputStream;
13+
14+
import com.box3lab.util.BlockIdResolver;
15+
import com.google.gson.JsonArray;
16+
import com.google.gson.JsonObject;
17+
18+
import net.fabricmc.loader.api.FabricLoader;
19+
import net.minecraft.core.BlockPos;
20+
import net.minecraft.core.Direction;
21+
import net.minecraft.server.level.ServerLevel;
22+
import net.minecraft.world.level.block.Rotation;
23+
import net.minecraft.world.level.block.state.BlockState;
24+
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
25+
26+
public final class VoxelExport {
27+
28+
private VoxelExport() {
29+
}
30+
31+
public static ExportResult exportRegion(ServerLevel level, BlockPos from, BlockPos to, String fileName) throws IOException {
32+
BlockPos min = new BlockPos(
33+
Math.min(from.getX(), to.getX()),
34+
Math.min(from.getY(), to.getY()),
35+
Math.min(from.getZ(), to.getZ()));
36+
BlockPos max = new BlockPos(
37+
Math.max(from.getX(), to.getX()),
38+
Math.max(from.getY(), to.getY()),
39+
Math.max(from.getZ(), to.getZ()));
40+
41+
int sizeX = max.getX() - min.getX() + 1;
42+
int sizeY = max.getY() - min.getY() + 1;
43+
int sizeZ = max.getZ() - min.getZ() + 1;
44+
45+
List<Integer> indices = new ArrayList<>();
46+
List<Integer> data = new ArrayList<>();
47+
List<Integer> rot = new ArrayList<>();
48+
49+
for (int z = 0; z < sizeZ; z++) {
50+
for (int y = 0; y < sizeY; y++) {
51+
for (int x = 0; x < sizeX; x++) {
52+
BlockPos pos = min.offset(x, y, z);
53+
BlockState state = level.getBlockState(pos);
54+
int id = BlockIdResolver.getIdByBlock(state.getBlock());
55+
if (id == 0) {
56+
continue;
57+
}
58+
59+
int idx = x + y * sizeX + z * sizeX * sizeY;
60+
indices.add(idx);
61+
data.add(id);
62+
rot.add(toRotationIndex(state));
63+
}
64+
}
65+
}
66+
67+
JsonObject root = new JsonObject();
68+
root.add("shape", intArray(sizeX, sizeY, sizeZ));
69+
root.add("dir", intArray(1, 1, 1));
70+
root.add("indices", toJsonArray(indices));
71+
root.add("data", toJsonArray(data));
72+
root.add("rot", toJsonArray(rot));
73+
74+
Path output = resolveOutput(fileName);
75+
Files.createDirectories(output.getParent());
76+
writeGzipJson(output, root.toString());
77+
78+
return new ExportResult(output, sizeX * sizeY * sizeZ, indices.size());
79+
}
80+
81+
private static int toRotationIndex(BlockState state) {
82+
Direction dir = null;
83+
if (state.hasProperty(BlockStateProperties.HORIZONTAL_FACING)) {
84+
dir = state.getValue(BlockStateProperties.HORIZONTAL_FACING);
85+
} else if (state.hasProperty(BlockStateProperties.FACING)) {
86+
Direction facing = state.getValue(BlockStateProperties.FACING);
87+
if (facing.getAxis().isHorizontal()) {
88+
dir = facing;
89+
}
90+
}
91+
92+
if (dir == null) {
93+
return 0;
94+
}
95+
96+
Rotation rotation = switch (dir) {
97+
case EAST -> Rotation.CLOCKWISE_90;
98+
case SOUTH -> Rotation.CLOCKWISE_180;
99+
case WEST -> Rotation.COUNTERCLOCKWISE_90;
100+
default -> Rotation.NONE;
101+
};
102+
103+
return switch (rotation) {
104+
case CLOCKWISE_90 -> 1;
105+
case CLOCKWISE_180 -> 2;
106+
case COUNTERCLOCKWISE_90 -> 3;
107+
default -> 0;
108+
};
109+
}
110+
111+
private static JsonArray intArray(int a, int b, int c) {
112+
JsonArray array = new JsonArray();
113+
array.add(a);
114+
array.add(b);
115+
array.add(c);
116+
return array;
117+
}
118+
119+
private static JsonArray toJsonArray(List<Integer> values) {
120+
JsonArray array = new JsonArray();
121+
for (Integer value : values) {
122+
array.add(value);
123+
}
124+
return array;
125+
}
126+
127+
private static Path resolveOutput(String fileName) {
128+
String cleaned = (fileName == null || fileName.isBlank()) ? "export" : fileName.trim();
129+
if (!cleaned.endsWith(".gz")) {
130+
cleaned = cleaned + ".gz";
131+
}
132+
return FabricLoader.getInstance()
133+
.getConfigDir()
134+
.resolve(MOD_ID)
135+
.resolve(cleaned);
136+
}
137+
138+
private static void writeGzipJson(Path outputPath, String json) throws IOException {
139+
try (OutputStream fos = Files.newOutputStream(outputPath);
140+
GZIPOutputStream gos = new GZIPOutputStream(fos)) {
141+
gos.write(json.getBytes(StandardCharsets.UTF_8));
142+
gos.finish();
143+
}
144+
}
145+
146+
public record ExportResult(Path output, int scannedBlocks, int exportedBlocks) {
147+
}
148+
}

0 commit comments

Comments
 (0)