photo storage failures handled: a shot is refused (film kept) while the image folder is not writable, a photo whose png cannot be saved fails like a render error (film back) instead of becoming a vanilla map after the restart, png/index files written atomically (tmp + move, no truncated png after a crash), one-line warnings instead of imageio stack traces, unreadable png warned
This commit is contained in:
1 parent
bf192588a9
commit
c75c7a4b60
2 files changed
+63
-16
No files matched your search
@@ -53,6 +53,13 @@ public final class PhotoService {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MapManager.canStore()) {
|
||||
player.sendActionBar(Component.text(
|
||||
"Fotos können gerade nicht gespeichert werden — bitte einem Admin melden.", NamedTextColor.RED));
|
||||
plugin.getLogger().warning("Photo of " + player.getName() + " refused: the image folder is not writable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderManager manager = plugin.getRenderManager();
|
||||
UUID user = player.getUniqueId();
|
||||
RenderManager.Outcome outcome = manager.tryReserve(user);
|
||||
@@ -118,7 +125,12 @@ public final class PhotoService {
|
||||
},
|
||||
out -> {
|
||||
chunks.release(); // already released by prepare
|
||||
MapManager.saveImage(out.image(), id);
|
||||
if (!MapManager.saveImage(out.image(), id)) {
|
||||
failed(
|
||||
id, user,
|
||||
usesFilm); // not stored: it would be lost (or a vanilla map) after a restart
|
||||
return;
|
||||
}
|
||||
MapManager.saveIndices(out.indices(), id);
|
||||
plugin.getPendingPhotos().remove(id);
|
||||
mapRenderer.develop(out.indices());
|
||||
|
||||
@@ -2,9 +2,12 @@ package eu.mhsl.minecraft.pixelpics.utils;
|
||||
|
||||
import eu.mhsl.minecraft.pixelpics.Main;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -173,28 +176,58 @@ public class MapManager {
|
||||
+ backup + ".");
|
||||
}
|
||||
|
||||
/** Blank film indices for a photo that has not developed (yet): never a vanilla world map after a restart. */
|
||||
public static void savePlaceholder(int id) {
|
||||
byte[] blank = new byte[INDEX_BYTES];
|
||||
Arrays.fill(blank, ImageMapRenderer.filmColor());
|
||||
saveIndices(blank, id);
|
||||
/** Whether new photos can be stored: the folder exists (or was created) and is writable. */
|
||||
public static boolean canStore() {
|
||||
return ensureFolder() && Files.isWritable(imageFolder().toPath());
|
||||
}
|
||||
|
||||
public static void saveImage(BufferedImage image, int id) {
|
||||
if (!ensureFolder()) return;
|
||||
/**
|
||||
* Blank film indices for a photo that has not developed (yet): never a vanilla world map after a restart. False if
|
||||
* they could not be stored.
|
||||
*/
|
||||
public static boolean savePlaceholder(int id) {
|
||||
byte[] blank = new byte[INDEX_BYTES];
|
||||
Arrays.fill(blank, ImageMapRenderer.filmColor());
|
||||
return saveIndices(blank, id);
|
||||
}
|
||||
|
||||
/** Stores the PNG (atomically: a crash never leaves a truncated one); false if it could not be written. */
|
||||
public static boolean saveImage(BufferedImage image, int id) {
|
||||
if (!ensureFolder()) return false;
|
||||
try {
|
||||
ImageIO.write(image, "png", getImagePath(id));
|
||||
ByteArrayOutputStream png = new ByteArrayOutputStream();
|
||||
if (!ImageIO.write(image, "png", png)) throw new IOException("no PNG writer");
|
||||
writeAtomically(getImagePath(id), png.toByteArray());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Main.getInstance().getLogger().warning("Failed to save map image " + id + ": " + e.getMessage());
|
||||
Main.getInstance().getLogger().warning("Failed to save map image " + id + ": " + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveIndices(byte[] indices, int id) {
|
||||
if (indices == null || indices.length != INDEX_BYTES || !ensureFolder()) return;
|
||||
/** Stores the index cache (atomically); false if it could not be written. */
|
||||
public static boolean saveIndices(byte[] indices, int id) {
|
||||
if (indices == null || indices.length != INDEX_BYTES || !ensureFolder()) return false;
|
||||
try {
|
||||
Files.write(getIndexPath(id).toPath(), indices);
|
||||
writeAtomically(getIndexPath(id), indices);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Main.getInstance().getLogger().warning("Failed to save map index cache " + id + ": " + e.getMessage());
|
||||
Main.getInstance().getLogger().warning("Failed to save map index cache " + id + ": " + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeAtomically(File target, byte[] data) throws IOException {
|
||||
Path tmp = target.toPath().resolveSibling(target.getName() + ".tmp");
|
||||
try {
|
||||
Files.write(tmp, data);
|
||||
try {
|
||||
Files.move(tmp, target.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
Files.move(tmp, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
} finally {
|
||||
Files.deleteIfExists(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,8 +248,10 @@ public class MapManager {
|
||||
File path = getImagePath(id);
|
||||
if (!path.isFile()) return null;
|
||||
try {
|
||||
return ImageIO.read(path);
|
||||
} catch (IOException e) {
|
||||
BufferedImage image = ImageIO.read(path);
|
||||
if (image == null) Main.getInstance().getLogger().warning("Map image " + id + " is no readable image.");
|
||||
return image;
|
||||
} catch (IOException | RuntimeException e) {
|
||||
Main.getInstance().getLogger().warning("Failed to read map image " + id + ": " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user