|
1 | 1 | package com.box3lab.command; |
2 | 2 |
|
3 | 3 | import java.io.IOException; |
| 4 | +import java.util.ArrayList; |
4 | 5 | import java.util.List; |
5 | 6 |
|
6 | 7 | import com.box3lab.block.BarrierVoxelBlock; |
| 8 | +import com.box3lab.register.VoxelExport; |
7 | 9 | import com.box3lab.register.VoxelImport; |
8 | 10 | import com.box3lab.util.Box3ImportFiles; |
9 | 11 | import com.mojang.brigadier.arguments.BoolArgumentType; |
|
16 | 18 | import static net.minecraft.commands.Commands.argument; |
17 | 19 | import static net.minecraft.commands.Commands.literal; |
18 | 20 | import net.minecraft.core.BlockPos; |
| 21 | +import net.minecraft.core.registries.BuiltInRegistries; |
19 | 22 | import net.minecraft.network.chat.Component; |
| 23 | +import net.minecraft.resources.Identifier; |
20 | 24 | import net.minecraft.server.level.ServerLevel; |
21 | 25 | import net.minecraft.server.level.ServerPlayer; |
| 26 | +import net.minecraft.world.level.block.Block; |
22 | 27 |
|
23 | 28 | public final class ModCommands { |
24 | 29 | private ModCommands() { |
25 | 30 | } |
26 | 31 |
|
| 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 | + |
27 | 36 | private static final SuggestionProvider<CommandSourceStack> BOX3_FILE_SUGGESTIONS = (context, builder) -> { |
28 | 37 | try { |
29 | 38 | List<String> files = Box3ImportFiles.listJsonFiles(); |
@@ -125,6 +134,16 @@ public static void register() { |
125 | 134 | .then(literal("toggle") |
126 | 135 | .executes(context -> toggleBarrierVisible( |
127 | 136 | 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"))))); |
128 | 147 | }); |
129 | 148 | } |
130 | 149 |
|
@@ -158,6 +177,11 @@ private static int listBox3ImportFiles(CommandSourceStack source) { |
158 | 177 | return 1; |
159 | 178 | } |
160 | 179 |
|
| 180 | + private static int showBox3ExportUsage(CommandSourceStack source) { |
| 181 | + source.sendFailure(Component.translatable("command.box3.box3export.usage")); |
| 182 | + return 0; |
| 183 | + } |
| 184 | + |
161 | 185 | private static String resolveMapName(String fileName) { |
162 | 186 | if (fileName != null && fileName.startsWith("Box3-")) { |
163 | 187 | String suffix = fileName.substring("Box3-".length()); |
@@ -221,4 +245,143 @@ private static int toggleBarrierVisible(CommandSourceStack source) { |
221 | 245 | false); |
222 | 246 | return 1; |
223 | 247 | } |
| 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 | + } |
224 | 387 | } |
0 commit comments