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;
|
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();
|
RenderManager manager = plugin.getRenderManager();
|
||||||
UUID user = player.getUniqueId();
|
UUID user = player.getUniqueId();
|
||||||
RenderManager.Outcome outcome = manager.tryReserve(user);
|
RenderManager.Outcome outcome = manager.tryReserve(user);
|
||||||
@@ -118,7 +125,12 @@ public final class PhotoService {
|
|||||||
},
|
},
|
||||||
out -> {
|
out -> {
|
||||||
chunks.release(); // already released by prepare
|
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);
|
MapManager.saveIndices(out.indices(), id);
|
||||||
plugin.getPendingPhotos().remove(id);
|
plugin.getPendingPhotos().remove(id);
|
||||||
mapRenderer.develop(out.indices());
|
mapRenderer.develop(out.indices());
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package eu.mhsl.minecraft.pixelpics.utils;
|
|||||||
|
|
||||||
import eu.mhsl.minecraft.pixelpics.Main;
|
import eu.mhsl.minecraft.pixelpics.Main;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.AtomicMoveNotSupportedException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -173,28 +176,58 @@ public class MapManager {
|
|||||||
+ backup + ".");
|
+ backup + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Blank film indices for a photo that has not developed (yet): never a vanilla world map after a restart. */
|
/** Whether new photos can be stored: the folder exists (or was created) and is writable. */
|
||||||
public static void savePlaceholder(int id) {
|
public static boolean canStore() {
|
||||||
byte[] blank = new byte[INDEX_BYTES];
|
return ensureFolder() && Files.isWritable(imageFolder().toPath());
|
||||||
Arrays.fill(blank, ImageMapRenderer.filmColor());
|
|
||||||
saveIndices(blank, id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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) {
|
} 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) {
|
/** Stores the index cache (atomically); false if it could not be written. */
|
||||||
if (indices == null || indices.length != INDEX_BYTES || !ensureFolder()) return;
|
public static boolean saveIndices(byte[] indices, int id) {
|
||||||
|
if (indices == null || indices.length != INDEX_BYTES || !ensureFolder()) return false;
|
||||||
try {
|
try {
|
||||||
Files.write(getIndexPath(id).toPath(), indices);
|
writeAtomically(getIndexPath(id), indices);
|
||||||
|
return true;
|
||||||
} catch (IOException e) {
|
} 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);
|
File path = getImagePath(id);
|
||||||
if (!path.isFile()) return null;
|
if (!path.isFile()) return null;
|
||||||
try {
|
try {
|
||||||
return ImageIO.read(path);
|
BufferedImage image = ImageIO.read(path);
|
||||||
} catch (IOException e) {
|
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());
|
Main.getInstance().getLogger().warning("Failed to read map image " + id + ": " + e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user