wire up rendering + build config
This commit is contained in:
1 parent
211c7e8479
commit
f1844a9dd9
10 files changed
+665
-155
No files matched your search
@@ -1,8 +1,14 @@
|
||||
package eu.mhsl.minecraft.pixelpics.commands;
|
||||
|
||||
import eu.mhsl.minecraft.pixelpics.utils.ImageMapRenderer;
|
||||
import eu.mhsl.minecraft.pixelpics.Main;
|
||||
import eu.mhsl.minecraft.pixelpics.render.render.DefaultScreenRenderer;
|
||||
import eu.mhsl.minecraft.pixelpics.render.render.RenderJob;
|
||||
import eu.mhsl.minecraft.pixelpics.render.render.Resolution;
|
||||
import eu.mhsl.minecraft.pixelpics.utils.ImageMapRenderer;
|
||||
import eu.mhsl.minecraft.pixelpics.utils.MapImageDither;
|
||||
import eu.mhsl.minecraft.pixelpics.utils.MapManager;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
@@ -15,50 +21,128 @@ import org.bukkit.map.MapView;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PixelPicsCommand implements CommandExecutor {
|
||||
|
||||
private static final int DEFAULT_CLEANUP_DAYS = 30;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) {
|
||||
if(!(sender instanceof Player player))
|
||||
throw new IllegalStateException("Dieser Command kann nur von einem Spieler ausgeführt werden!");
|
||||
|
||||
if(args.length > 0)
|
||||
return false;
|
||||
|
||||
// render image
|
||||
Resolution resolution = new Resolution(Resolution.Pixels._128P, Resolution.AspectRatio._1_1);
|
||||
BufferedImage image = Main.getInstance().getScreenRenderer().render(player.getEyeLocation(), resolution);
|
||||
|
||||
// save image
|
||||
UUID imageId = UUID.randomUUID();
|
||||
File imageFolder = new File(Main.getInstance().getDataFolder(), "images");
|
||||
try {
|
||||
if(!imageFolder.exists() && !imageFolder.mkdirs())
|
||||
throw new IOException("Failed to create folders for image output!");
|
||||
|
||||
ImageIO.write(image, "png", new File(imageFolder, String.format("%s.png", imageId)));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save image to disk!");
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
@NotNull String @NotNull [] args) {
|
||||
if (args.length >= 1 && args[0].equalsIgnoreCase("cleanup")) {
|
||||
return cleanup(sender, args);
|
||||
}
|
||||
|
||||
// image item
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(Component.text("Dieser Command kann nur von einem Spieler ausgeführt werden!",
|
||||
NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
if (args.length > 0) return false;
|
||||
|
||||
DefaultScreenRenderer renderer = Main.getInstance().getScreenRenderer();
|
||||
if (renderer == null) {
|
||||
player.sendMessage(Component.text("PixelPics ist nicht einsatzbereit: es wurde kein Resource-Pack geladen.",
|
||||
NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
Resolution resolution = new Resolution(Resolution.Pixels._128P, Resolution.AspectRatio._1_1);
|
||||
|
||||
// Capture the world snapshot on the main thread.
|
||||
RenderJob job = renderer.prepare(player.getEyeLocation(), resolution, player.getUniqueId());
|
||||
|
||||
// Hand the map over immediately, showing blank "film"; it develops once the render is ready.
|
||||
ImageMapRenderer mapRenderer = new ImageMapRenderer();
|
||||
MapView mapView = Bukkit.createMap(player.getWorld());
|
||||
int id = mapView.getId();
|
||||
MapManager.attachView(mapView, mapRenderer);
|
||||
|
||||
ItemStack map = new ItemStack(Material.FILLED_MAP, 1);
|
||||
MapMeta meta = (MapMeta) map.getItemMeta();
|
||||
meta.getPersistentDataContainer().set(Main.getInstance().pictureIdFlag, PersistentDataType.STRING, imageId.toString());
|
||||
|
||||
// display image
|
||||
MapView mapView = Bukkit.createMap(Bukkit.getWorlds().getFirst());
|
||||
mapView.getRenderers().forEach(mapView::removeRenderer);
|
||||
mapView.addRenderer(new ImageMapRenderer(image));
|
||||
meta.getPersistentDataContainer().set(Main.getInstance().pictureIdFlag,
|
||||
PersistentDataType.STRING, UUID.randomUUID().toString());
|
||||
meta.setMapView(mapView);
|
||||
|
||||
map.setItemMeta(meta);
|
||||
player.getInventory().addItem(map);
|
||||
player.sendMessage(Component.text("📸 Aufnahme wird entwickelt …", NamedTextColor.GRAY));
|
||||
|
||||
// Trace + dither off-thread, then start the developing animation on the main thread.
|
||||
Main plugin = Main.getInstance();
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
BufferedImage image;
|
||||
byte[] indices;
|
||||
try {
|
||||
image = renderer.execute(job);
|
||||
indices = MapImageDither.dither(image);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Render failed: " + e.getMessage());
|
||||
Bukkit.getScheduler().runTask(plugin, () ->
|
||||
player.sendMessage(Component.text("Rendern fehlgeschlagen.", NamedTextColor.RED)));
|
||||
return;
|
||||
}
|
||||
BufferedImage finalImage = image;
|
||||
byte[] finalIndices = indices;
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
MapManager.saveImage(finalImage, id);
|
||||
MapManager.saveIndices(finalIndices, id);
|
||||
mapRenderer.develop(finalIndices);
|
||||
player.sendMessage(Component.text("✅ Aufnahme erstellt!", NamedTextColor.GREEN));
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@code /pixelPic cleanup [days]} dry-run; {@code /pixelPic cleanup confirm [days]} deletes orphans. */
|
||||
private boolean cleanup(CommandSender sender, String[] args) {
|
||||
if (!sender.hasPermission("pixelpic.admin") && !sender.isOp()) {
|
||||
sender.sendMessage(Component.text("Dafür fehlt dir die Berechtigung (pixelpic.admin).", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean confirm = false;
|
||||
int days = DEFAULT_CLEANUP_DAYS;
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
if (args[i].equalsIgnoreCase("confirm")) confirm = true;
|
||||
else {
|
||||
try {
|
||||
days = Math.max(0, Integer.parseInt(args[i]));
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<Integer> inUse = MapManager.collectInUseMapIds();
|
||||
long cutoff = System.currentTimeMillis() - days * 86_400_000L;
|
||||
List<MapManager.StoredMap> candidates = MapManager.listStored().stream()
|
||||
.filter(s -> !inUse.contains(s.id()) && s.lastModified() < cutoff)
|
||||
.toList();
|
||||
|
||||
if (candidates.isEmpty()) {
|
||||
sender.sendMessage(Component.text("Keine aufräumbaren Aufnahmen gefunden (älter als " + days
|
||||
+ " Tage und nicht in Benutzung).", NamedTextColor.YELLOW));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!confirm) {
|
||||
sender.sendMessage(Component.text(candidates.size() + " Aufnahme(n) könnten gelöscht werden "
|
||||
+ "(älter als " + days + " Tage, nicht in geladenen Itemframes/Online-Inventaren).",
|
||||
NamedTextColor.YELLOW));
|
||||
sender.sendMessage(Component.text("Achtung: Maps in lange ungeladenen Bereichen werden hierbei nicht "
|
||||
+ "erkannt. Zum Löschen: /pixelPic cleanup confirm " + days, NamedTextColor.GRAY));
|
||||
return true;
|
||||
}
|
||||
|
||||
int deleted = 0;
|
||||
for (MapManager.StoredMap s : candidates) {
|
||||
if (MapManager.delete(s.id())) deleted++;
|
||||
}
|
||||
sender.sendMessage(Component.text(deleted + " Aufnahme(n) gelöscht.", NamedTextColor.GREEN));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user