diff --git a/src/api/java/com/ldtteam/structurize/api/util/Utils.java b/src/api/java/com/ldtteam/structurize/api/util/Utils.java index 259b38753..821291e77 100644 --- a/src/api/java/com/ldtteam/structurize/api/util/Utils.java +++ b/src/api/java/com/ldtteam/structurize/api/util/Utils.java @@ -8,6 +8,8 @@ import org.jetbrains.annotations.NotNull; import java.io.File; +import java.text.Normalizer; +import java.util.Locale; import java.util.Objects; /** @@ -73,4 +75,21 @@ public static boolean nbtContains(final CompoundTag originTag, final CompoundTag } return true; } + + /** + * Get a filename that's probably safe from a player name that might contain problematic characters. + * @param input a player name or other possibly unsafe text. + * @return the safe filename. + * + * @implNote This doesn't protect against Windows reserved filenames. Most servers are Linux anyway + * so this only hurts SP players who will have a lot of Windows things break on them too. + */ + public static String getSafePackName(String input) + { + String s = Normalizer.normalize(input, Normalizer.Form.NFC); + s = s.replaceAll("[\\\\/:*?\"<>|]", "_"); + s = s.replaceAll("\\p{Cntrl}", ""); + s = s.trim(); + return s; + } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java index 628a50ad3..6b81ec44b 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java @@ -8,6 +8,7 @@ import com.ldtteam.blockui.views.View; import com.ldtteam.structurize.Network; import com.ldtteam.structurize.api.util.Shape; +import com.ldtteam.structurize.api.util.Utils; import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; @@ -327,13 +328,13 @@ protected void handlePlacement(final BuildToolPlacementMessage.HandlerType type, final BlueprintPreviewData previewData = RenderingCache.getOrCreateBlueprintPreviewData("shapes"); if (previewData.getBlueprint() != null) { - final String packName = Minecraft.getInstance().getUser().getName(); + final String packName = Utils.getSafePackName(Minecraft.getInstance().getUser().getName()); final Path subpath = Path.of( SHAPES_FOLDER, shape.toString().toLowerCase(Locale.ROOT), mainBlock.getItem().toString().replace(':', '_'), secondaryBlock.getItem().toString().replace(':', '_'), - String.format("%dx%dx%dx%d_%c.blueprint", length, width, height, frequency, hollow ? 'h' : 'f')); + String.format("%sx%sx%sx%s_%c.blueprint", length, width, height, frequency, hollow ? 'h' : 'f')); final Path path = Minecraft.getInstance().gameDirectory.toPath() .resolve(BLUEPRINT_FOLDER) .resolve(packName.toLowerCase(Locale.US)) diff --git a/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java b/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java index b67420b6f..dca9e9bfc 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java +++ b/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java @@ -5,6 +5,7 @@ import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.Utils; import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.network.messages.NotifyServerAboutStructurePacksMessage; import com.ldtteam.structurize.network.messages.SyncSettingsToServer; @@ -102,7 +103,7 @@ public static void onClientLoading() Files.createDirectory(outputPath); } - final Path clientPackPath = outputPath.resolve(Minecraft.getInstance().getUser().getName().toLowerCase(Locale.US)); + final Path clientPackPath = outputPath.resolve(Utils.getSafePackName(Minecraft.getInstance().getUser().getName()).toLowerCase(Locale.US)); if (!Files.exists(clientPackPath)) { Files.createDirectory(clientPackPath); @@ -349,12 +350,12 @@ public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) throws IOEx */ public static void handleSaveScanMessage(final CompoundTag compound, final String fileName) { - final String packName = Minecraft.getInstance().getUser().getName().toLowerCase(Locale.US); - StructurePacks.switchSelectedPack(StructurePacks.getStructurePack(Minecraft.getInstance().getUser().getName())); + final String packName = Utils.getSafePackName(Minecraft.getInstance().getUser().getName()); + StructurePacks.switchSelectedPack(StructurePacks.getStructurePack(Utils.getSafePackName(Minecraft.getInstance().getUser().getName()))); RenderingCache.getOrCreateBlueprintPreviewData("blueprint").setBlueprintFuture( StructurePacks.storeBlueprint(packName, compound, Minecraft.getInstance().gameDirectory.toPath() .resolve(BLUEPRINT_FOLDER) - .resolve(Minecraft.getInstance().getUser().getName().toLowerCase(Locale.US)) + .resolve(packName.toLowerCase(Locale.US)) .resolve(SCANS_FOLDER).resolve(fileName))); RenderingCache.getOrCreateBlueprintPreviewData("blueprint").setPos(null); Minecraft.getInstance().player.displayClientMessage(Component.translatable("Scan successfully saved as %s", fileName), false);