Skip to content

Commit c9a4447

Browse files
committed
Add SableStorageCommands
1 parent 57bb389 commit c9a4447

4 files changed

Lines changed: 175 additions & 0 deletions

File tree

common/src/main/java/dev/ryanhcode/sable/command/SableCommand.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public static void register(final CommandDispatcher<CommandSourceStack> dispatch
4242
SableSpawnCommands.register(sableBuilder, buildContext);
4343
SableSubLevelCommands.register(sableBuilder, buildContext);
4444
SableAssembleCommands.register(sableBuilder, buildContext);
45+
SableStorageCommands.register(sableBuilder, buildContext);
4546

4647
final LiteralArgumentBuilder<CommandSourceStack> debugBuilder = Commands.literal("debug");
4748

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package dev.ryanhcode.sable.command;
2+
3+
import com.mojang.brigadier.arguments.StringArgumentType;
4+
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
5+
import dev.ryanhcode.sable.api.sublevel.ServerSubLevelContainer;
6+
import dev.ryanhcode.sable.companion.math.Pose3d;
7+
import dev.ryanhcode.sable.sublevel.storage.holding.GlobalSavedSubLevelPointer;
8+
import dev.ryanhcode.sable.sublevel.storage.holding.SavedSubLevelPointer;
9+
import dev.ryanhcode.sable.sublevel.storage.holding.SubLevelHoldingChunk;
10+
import dev.ryanhcode.sable.sublevel.storage.holding.SubLevelHoldingChunkMap;
11+
import dev.ryanhcode.sable.sublevel.storage.region.SubLevelRegionFile;
12+
import dev.ryanhcode.sable.sublevel.storage.serialization.SubLevelData;
13+
import dev.ryanhcode.sable.sublevel.storage.serialization.SubLevelStorage;
14+
import net.minecraft.ChatFormatting;
15+
import net.minecraft.commands.CommandBuildContext;
16+
import net.minecraft.commands.CommandSourceStack;
17+
import net.minecraft.commands.Commands;
18+
import net.minecraft.network.chat.*;
19+
import net.minecraft.resources.ResourceLocation;
20+
import net.minecraft.server.level.ServerLevel;
21+
import net.minecraft.world.level.ChunkPos;
22+
import org.joml.Vector3d;
23+
import org.joml.Vector3dc;
24+
25+
import java.io.File;
26+
import java.util.Formatter;
27+
import java.util.Locale;
28+
29+
public class SableStorageCommands {
30+
31+
public static void register(final LiteralArgumentBuilder<CommandSourceStack> sableBuilder, final CommandBuildContext buildContext) {
32+
sableBuilder.then(Commands.literal("storage")
33+
.then(Commands.literal("find_all_sub_levels")
34+
.executes(ctx -> {
35+
final ServerLevel level = ctx.getSource().getLevel();
36+
final ServerSubLevelContainer container = ServerSubLevelContainer.getContainer(level);
37+
final SubLevelHoldingChunkMap holdingChunkMap = container.getHoldingChunkMap();
38+
final SubLevelStorage storage = holdingChunkMap.getStorage();
39+
final CommandSourceStack source = ctx.getSource();
40+
41+
final File[] regionFiles = storage.getFolder().toFile().listFiles((dir, name) -> name.endsWith(SubLevelRegionFile.FILE_EXTENSION));
42+
43+
if (regionFiles != null) {
44+
for (final File regionFile : regionFiles) {
45+
final String fileName = regionFile.getName();
46+
final String withoutExtension = fileName.substring(0, fileName.length() - SubLevelRegionFile.FILE_EXTENSION.length());
47+
final String[] parts = withoutExtension.split("\\.");
48+
if (parts.length != 3) continue;
49+
50+
final int regionX, regionZ;
51+
try {
52+
regionX = Integer.parseInt(parts[1]);
53+
regionZ = Integer.parseInt(parts[2]);
54+
} catch (final NumberFormatException e) {
55+
continue;
56+
}
57+
58+
for (int localX = 0; localX < SubLevelRegionFile.SIDE_LENGTH; localX++) {
59+
for (int localZ = 0; localZ < SubLevelRegionFile.SIDE_LENGTH; localZ++) {
60+
final ChunkPos chunkPos = new ChunkPos(
61+
regionX * SubLevelRegionFile.SIDE_LENGTH + localX,
62+
regionZ * SubLevelRegionFile.SIDE_LENGTH + localZ
63+
);
64+
65+
final SubLevelHoldingChunk holdingChunk = storage.attemptLoadHoldingChunk(chunkPos);
66+
if (holdingChunk == null) continue;
67+
68+
for (final SavedSubLevelPointer pointer : holdingChunk.getSubLevelPointers()) {
69+
final SubLevelData data = storage.attemptLoadSubLevel(chunkPos, pointer);
70+
logFoundSubLevel(pointer, data, chunkPos, source, level);
71+
}
72+
}
73+
}
74+
}
75+
}
76+
return 1;
77+
}))
78+
.then(Commands.literal("find")
79+
.then(Commands.argument("name", StringArgumentType.string())
80+
.executes(ctx -> {
81+
final ServerLevel level = ctx.getSource().getLevel();
82+
final ServerSubLevelContainer container = ServerSubLevelContainer.getContainer(level);
83+
final SubLevelHoldingChunkMap holdingChunkMap = container.getHoldingChunkMap();
84+
final SubLevelStorage storage = holdingChunkMap.getStorage();
85+
final CommandSourceStack source = ctx.getSource();
86+
final String nameArgument = StringArgumentType.getString(ctx, "name");
87+
88+
final File[] regionFiles = storage.getFolder().toFile().listFiles((dir, name) -> name.endsWith(SubLevelRegionFile.FILE_EXTENSION));
89+
90+
if (regionFiles != null) {
91+
for (final File regionFile : regionFiles) {
92+
final String fileName = regionFile.getName();
93+
final String withoutExtension = fileName.substring(0, fileName.length() - SubLevelRegionFile.FILE_EXTENSION.length());
94+
final String[] parts = withoutExtension.split("\\.");
95+
if (parts.length != 3) continue;
96+
97+
final int regionX, regionZ;
98+
try {
99+
regionX = Integer.parseInt(parts[1]);
100+
regionZ = Integer.parseInt(parts[2]);
101+
} catch (final NumberFormatException e) {
102+
continue;
103+
}
104+
105+
for (int localX = 0; localX < SubLevelRegionFile.SIDE_LENGTH; localX++) {
106+
for (int localZ = 0; localZ < SubLevelRegionFile.SIDE_LENGTH; localZ++) {
107+
final ChunkPos chunkPos = new ChunkPos(
108+
regionX * SubLevelRegionFile.SIDE_LENGTH + localX,
109+
regionZ * SubLevelRegionFile.SIDE_LENGTH + localZ
110+
);
111+
112+
final SubLevelHoldingChunk holdingChunk = storage.attemptLoadHoldingChunk(chunkPos);
113+
if (holdingChunk == null) continue;
114+
115+
for (final SavedSubLevelPointer pointer : holdingChunk.getSubLevelPointers()) {
116+
final SubLevelData data = storage.attemptLoadSubLevel(chunkPos, pointer);
117+
118+
final String name = data.fullTag().contains("display_name")
119+
? data.fullTag().getString("display_name")
120+
: data.uuid().toString();
121+
if (name != null && name.equals(nameArgument)) {
122+
logFoundSubLevel(pointer, data, chunkPos, source, level);
123+
}
124+
}
125+
}
126+
}
127+
}
128+
}
129+
return 1;
130+
})))
131+
132+
);
133+
}
134+
135+
private static void logFoundSubLevel(final SavedSubLevelPointer pointer, final SubLevelData data, final ChunkPos chunkPos, final CommandSourceStack source, final ServerLevel level) {
136+
if (data == null) return;
137+
138+
final String name = data.fullTag().contains("display_name")
139+
? data.fullTag().getString("display_name")
140+
: data.uuid().toString();
141+
final GlobalSavedSubLevelPointer globalPointer = new GlobalSavedSubLevelPointer(chunkPos, pointer.storageIndex(), pointer.subLevelIndex());
142+
143+
final Pose3d pose = data.pose();
144+
145+
source.sendSuccess(() -> {
146+
final Vector3dc pos = pose.position();
147+
final MutableComponent component = Component.translatable("commands.sable.info.name", Component.literal(name));
148+
final ResourceLocation dimension = level.dimension().location();
149+
final Component fileId = Component.translatable("commands.sable.info.name.tooltip", globalPointer.toString());
150+
component.setStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, new Formatter().format(Locale.ROOT, "/execute in %s run tp @s %.2f %.2f %.2f", dimension, pos.x(), pos.y(), pos.z()).toString()))
151+
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, fileId))
152+
.withColor(ChatFormatting.GRAY));
153+
return component;
154+
}, false);
155+
156+
source.sendSuccess(() -> {
157+
final Vector3dc pos = pose.position();
158+
return Component.translatable("commands.sable.info.position", pos.x(), pos.y(), pos.z());
159+
}, false);
160+
161+
source.sendSuccess(() -> {
162+
final Vector3d size = data.bounds().size();
163+
return Component.translatable("commands.sable.info.world_bounds", size.x, size.y, size.z);
164+
}, false);
165+
}
166+
}

common/src/main/java/dev/ryanhcode/sable/sublevel/storage/serialization/SubLevelStorage.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import net.minecraft.nbt.CompoundTag;
1313
import net.minecraft.util.ExceptionCollector;
1414
import net.minecraft.world.level.ChunkPos;
15+
import org.jetbrains.annotations.ApiStatus;
1516
import org.jetbrains.annotations.NotNull;
1617

1718
import java.io.IOException;
@@ -244,6 +245,12 @@ public void close() throws IOException {
244245
exceptionCollector.throwIfPresent();
245246
}
246247

248+
@NotNull
249+
@ApiStatus.Internal
250+
public Path getFolder() {
251+
return this.folder;
252+
}
253+
247254
/**
248255
* Flushes all cached region and storage files to disk.
249256
*/

common/src/main/resources/assets/sable/lang/en_us.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"commands.sable.info.linear_velocity": " Linear Velocity: %.2f %.2f %.2f",
7070
"commands.sable.info.angular_velocity": " Angular Velocity: %.2f %.2f %.2f",
7171
"commands.sable.info.mass": " Mass: %.2f",
72+
"commands.sable.info.world_bounds": " World Bounds: %.2f x %.2f x %.2f",
7273

7374
"commands.data.sub_level.get": "%s on sub-level %s after scale factor of %s is %s",
7475
"commands.data.sub_level.modified": "Modified sub-level auxiliary data of %s",

0 commit comments

Comments
 (0)