diff --git a/src/main/java/com/ldtteam/structurize/api/Utils.java b/src/main/java/com/ldtteam/structurize/api/Utils.java index 7e7b6b969..b653f0525 100644 --- a/src/main/java/com/ldtteam/structurize/api/Utils.java +++ b/src/main/java/com/ldtteam/structurize/api/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. + * + * 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 9209d2c54..53e4d76fa 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.api.RotationMirror; import com.ldtteam.structurize.api.Shape; +import com.ldtteam.structurize.api.Utils; import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; @@ -332,13 +333,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 d285c467e..32e2c7d61 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java +++ b/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java @@ -4,6 +4,7 @@ import com.google.gson.JsonObject; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.Utils; import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.network.messages.NotifyServerAboutStructurePacksMessage; import com.ldtteam.structurize.network.messages.SyncSettingsToServer; @@ -101,7 +102,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); @@ -345,12 +346,12 @@ public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) throws IOEx */ public static void handleSaveScanMessage(final CompoundTag compound, final String fileName, final HolderLookup.Provider provider) { - 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), provider)); RenderingCache.getOrCreateBlueprintPreviewData("blueprint").setPos(null); Minecraft.getInstance().player.displayClientMessage(Component.translatable("Scan successfully saved as %s", fileName), false);