CustomMap #40
@ -27,8 +27,7 @@ import de.steamwar.lobby.command.PortalCommand;
|
||||
import de.steamwar.lobby.jumpandrun.JumpAndRun;
|
||||
import de.steamwar.lobby.jumpandrun.JumpAndRunCommand;
|
||||
import de.steamwar.lobby.listener.*;
|
||||
import de.steamwar.lobby.map.CustomMapCommand;
|
||||
import de.steamwar.lobby.map.CustomMapNew;
|
||||
import de.steamwar.lobby.map.CustomMap;
|
||||
import de.steamwar.lobby.particle.ParticleListener;
|
||||
import de.steamwar.lobby.special.advent.AdventsCalendar;
|
||||
import de.steamwar.lobby.team.TeamPlayer;
|
||||
@ -54,7 +53,7 @@ public class LobbySystem extends JavaPlugin {
|
||||
entityServer = new REntityServer();
|
||||
debugEntityServer = new REntityServer();
|
||||
|
||||
CustomMapNew.init();
|
||||
CustomMap.init();
|
||||
|
||||
Fightserver.init();
|
||||
new Portals();
|
||||
@ -62,7 +61,6 @@ public class LobbySystem extends JavaPlugin {
|
||||
new HologramCommand();
|
||||
new FlyCommand();
|
||||
new ModifyCommand();
|
||||
new CustomMapCommand();
|
||||
|
||||
new JumpAndRun();
|
||||
new JumpAndRunCommand();
|
||||
|
@ -1,110 +1,356 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.lobby.map;
|
||||
|
||||
import net.minecraft.world.level.saveddata.maps.WorldMap;
|
||||
import de.steamwar.lobby.LobbySystem;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.map.CraftMapView;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.MapMeta;
|
||||
import org.bukkit.map.MapCanvas;
|
||||
import org.bukkit.map.MapPalette;
|
||||
import org.bukkit.map.MapRenderer;
|
||||
import org.bukkit.map.MapView;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.Field;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class CustomMap {
|
||||
public class CustomMap implements Listener {
|
||||
|
||||
public CustomMap(Player player, BufferedImage image) {
|
||||
if (image.getWidth() % 128 != 0) {
|
||||
throw new IllegalArgumentException("Image width must be a multiple of 128");
|
||||
}
|
||||
if (image.getHeight() % 128 != 0) {
|
||||
throw new IllegalArgumentException("Image height must be a multiple of 128");
|
||||
public static void init() {
|
||||
}
|
||||
|
||||
private static final CustomMap LEFT = new CustomMap(new File(System.getProperty("user.home") + "/lobbyBanner/left.png"),
|
||||
new Vector(2346, 48, 1297), new Vector(2345, 48, 1297), new Vector(2344, 48, 1297), new Vector(2343, 48, 1297), new Vector(2342, 48, 1297), new Vector(2341, 48, 1297), new Vector(2340, 48, 1297),
|
||||
new Vector(2346, 47, 1297), new Vector(2345, 47, 1297), new Vector(2344, 47, 1297), new Vector(2343, 47, 1297), new Vector(2342, 47, 1297), new Vector(2341, 47, 1297), new Vector(2340, 47, 1297),
|
||||
new Vector(2346, 46, 1297), new Vector(2345, 46, 1297), new Vector(2344, 46, 1297), new Vector(2343, 46, 1297), new Vector(2342, 46, 1297), new Vector(2341, 46, 1297), new Vector(2340, 46, 1297),
|
||||
new Vector(2346, 45, 1297), new Vector(2345, 45, 1297), new Vector(2344, 45, 1297), new Vector(2343, 45, 1297), new Vector(2342, 45, 1297), new Vector(2341, 45, 1297), new Vector(2340, 45, 1297)
|
||||
);
|
||||
|
||||
private static final CustomMap RIGHT = new CustomMap(new File(System.getProperty("user.home") + "/lobbyBanner/right.png"),
|
||||
new Vector(2330, 48, 1297), new Vector(2329, 48, 1297), new Vector(2328, 48, 1297), new Vector(2327, 48, 1297), new Vector(2326, 48, 1297), new Vector(2325, 48, 1297), new Vector(2324, 48, 1297),
|
||||
new Vector(2330, 47, 1297), new Vector(2329, 47, 1297), new Vector(2328, 47, 1297), new Vector(2327, 47, 1297), new Vector(2326, 47, 1297), new Vector(2325, 47, 1297), new Vector(2324, 47, 1297),
|
||||
new Vector(2330, 46, 1297), new Vector(2329, 46, 1297), new Vector(2328, 46, 1297), new Vector(2327, 46, 1297), new Vector(2326, 46, 1297), new Vector(2325, 46, 1297), new Vector(2324, 46, 1297),
|
||||
new Vector(2330, 45, 1297), new Vector(2329, 45, 1297), new Vector(2328, 45, 1297), new Vector(2327, 45, 1297), new Vector(2326, 45, 1297), new Vector(2325, 45, 1297), new Vector(2324, 45, 1297)
|
||||
);
|
||||
|
||||
private File mapFile;
|
||||
private Map<Vector, Integer> itemFrameIndex = new HashMap<>();
|
||||
private ItemFrame[] itemFrames;
|
||||
private long lastModified = Long.MAX_VALUE;
|
||||
|
||||
public CustomMap(File mapFile, Vector... itemFrames) {
|
||||
this.mapFile = mapFile;
|
||||
this.itemFrames = new ItemFrame[itemFrames.length];
|
||||
for (int i = 0; i < itemFrames.length; i++) {
|
||||
itemFrameIndex.put(itemFrames[i], i);
|
||||
}
|
||||
|
||||
BufferedImage bufferedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
Color oldPixel = new Color(image.getRGB(x, y));
|
||||
Color newPixel = getNearest(oldPixel);
|
||||
bufferedImage.setRGB(x, y, newPixel.getRGB());
|
||||
int quantErrorRed = oldPixel.getRed() - newPixel.getRed();
|
||||
int quantErrorGreen = oldPixel.getGreen() - newPixel.getGreen();
|
||||
int quantErrorBlue = oldPixel.getBlue() - newPixel.getBlue();
|
||||
if (x < image.getWidth() - 1) {
|
||||
image.setRGB(x + 1, y, updateViaQuantError(new Color(image.getRGB(x + 1, y)), quantErrorRed, quantErrorGreen, quantErrorBlue, 7 / 16.0).getRGB());
|
||||
}
|
||||
if (x > 0) {
|
||||
image.setRGB(x - 1, y, updateViaQuantError(new Color(image.getRGB(x - 1, y)), quantErrorRed, quantErrorGreen, quantErrorBlue, 3 / 16.0).getRGB());
|
||||
}
|
||||
if (y < image.getHeight() - 1) {
|
||||
image.setRGB(x, y + 1, updateViaQuantError(new Color(image.getRGB(x, y + 1)), quantErrorRed, quantErrorGreen, quantErrorBlue, 5 / 16.0).getRGB());
|
||||
if (x < image.getWidth() - 1) {
|
||||
image.setRGB(x + 1, y + 1, updateViaQuantError(new Color(image.getRGB(x + 1, y + 1)), quantErrorRed, quantErrorGreen, quantErrorBlue, 1 / 16.0).getRGB());
|
||||
Bukkit.getScheduler().runTaskTimer(LobbySystem.getPlugin(), () -> {
|
||||
long modified = mapFile.lastModified();
|
||||
if (modified > lastModified) {
|
||||
lastModified = modified;
|
||||
System.out.println("Updating Banner: " + mapFile.getName());
|
||||
Bukkit.getScheduler().runTaskAsynchronously(LobbySystem.getPlugin(), () -> {
|
||||
try {
|
||||
run();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 200L, 200L);
|
||||
Bukkit.getPluginManager().registerEvents(this, LobbySystem.getPlugin());
|
||||
}
|
||||
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x += 128) {
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y += 128) {
|
||||
BufferedImage subImage = bufferedImage.getSubimage(x, y, 128, 128);
|
||||
CraftMapView craftMapView = (CraftMapView) Bukkit.createMap(player.getWorld());
|
||||
try {
|
||||
Field field = CraftMapView.class.getDeclaredField("worldMap");
|
||||
field.setAccessible(true);
|
||||
WorldMap worldMap = (WorldMap) field.get(craftMapView);
|
||||
for (int sx = 0; sx < 128; sx++) {
|
||||
for (int sy = 0; sy < 128; sy++) {
|
||||
byte color = MapPalette.matchColor(new Color(subImage.getRGB(sx, sy)));
|
||||
worldMap.g[sx + sy * 128] = color;
|
||||
@EventHandler
|
||||
public void onChunkLoad(ChunkLoadEvent event) {
|
||||
for (Entity entity : event.getChunk().getEntities()) {
|
||||
if (!(entity instanceof ItemFrame)) continue;
|
||||
ItemFrame itemFrame = (ItemFrame) entity;
|
||||
ItemStack itemStack = itemFrame.getItem();
|
||||
if (itemStack.getType() != Material.FILLED_MAP) continue;
|
||||
Vector vector = itemFrame.getLocation().getBlock().getLocation().toVector();
|
||||
if (itemFrameIndex.containsKey(vector)) {
|
||||
if (itemFrames[itemFrameIndex.get(vector)] != null) continue;
|
||||
itemFrames[itemFrameIndex.get(vector)] = itemFrame;
|
||||
lastModified = 0;
|
||||
MapView mapView = ((MapMeta) itemFrame.getItem().getItemMeta()).getMapView();
|
||||
new ArrayList<>(mapView.getRenderers()).forEach(mapView::removeRenderer);
|
||||
mapView.addRenderer(new MapRenderer() {
|
||||
@Override
|
||||
public void render(MapView map, MapCanvas canvas, Player player) {
|
||||
for (int x = 0; x < 128; x++) {
|
||||
for (int y = 0; y < 128; y++) {
|
||||
canvas.setPixel(x, y, (byte) 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
craftMapView.setLocked(true);
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new SecurityException(e.getMessage(), e);
|
||||
}
|
||||
ItemStack itemStack = new ItemStack(Material.FILLED_MAP, 1);
|
||||
MapMeta mapMeta = (MapMeta) itemStack.getItemMeta();
|
||||
mapMeta.setMapView(craftMapView);
|
||||
itemStack.setItemMeta(mapMeta);
|
||||
player.getInventory().addItem(itemStack);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Color updateViaQuantError(Color color, int quantErrorRed, int quantErrorGreen, int quantErrorBlue, double multiplier) {
|
||||
int newRed = (int) (color.getRed() + quantErrorRed * multiplier);
|
||||
int newGreen = (int) (color.getGreen() + quantErrorGreen * multiplier);
|
||||
int newBlue = (int) (color.getBlue() + quantErrorBlue * multiplier);
|
||||
return new Color(clamp(newRed), clamp(newGreen), clamp(newBlue));
|
||||
private void run() throws IOException {
|
||||
BufferedImage bufferedImage = ImageIO.read(mapFile);
|
||||
|
||||
Set<Point>[] patches = new Set[256];
|
||||
for (int patch = 0; patch < patches.length; patch++) {
|
||||
patches[patch] = new HashSet<>();
|
||||
}
|
||||
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y++) {
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x++) {
|
||||
Color color = new Color(bufferedImage.getRGB(x, y));
|
||||
double red = color.getRed() / 255.0;
|
||||
double green = color.getGreen() / 255.0;
|
||||
double blue = color.getBlue() / 255.0;
|
||||
double luminance = Math.sqrt(0.299 * red * red + 0.587 * green * green + 0.114 * blue * blue);
|
||||
luminance *= 255;
|
||||
patches[(int) luminance].add(new Point(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
for (int patch = 0; patch < patches.length; patch++) {
|
||||
Set<Point> points = patches[patch];
|
||||
if (points.isEmpty()) continue;
|
||||
BufferedImage patchImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
points.forEach(point -> {
|
||||
patchImage.setRGB(point.getX(), point.getY(), bufferedImage.getRGB(point.getX(), point.getY()));
|
||||
});
|
||||
floodFill(patchImage);
|
||||
dither(patchImage);
|
||||
points.forEach(point -> {
|
||||
bufferedImage.setRGB(point.getX(), point.getY(), patchImage.getRGB(point.getX(), point.getY()));
|
||||
});
|
||||
}
|
||||
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y += 128) {
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x += 128) {
|
||||
ItemFrame itemFrame = itemFrames[y / 128 * 7 + x / 128];
|
||||
if (itemFrame == null) continue;
|
||||
int finalX = x;
|
||||
int finalY = y;
|
||||
Bukkit.getScheduler().runTaskLater(LobbySystem.getPlugin(), () -> {
|
||||
ItemStack itemStack = itemFrame.getItem();
|
||||
MapMeta mapMeta = (MapMeta) itemStack.getItemMeta();
|
||||
MapView mapView = mapMeta.getMapView();
|
||||
new ArrayList<>(mapView.getRenderers()).forEach(mapView::removeRenderer);
|
||||
mapView.addRenderer(new MapRenderer() {
|
||||
@Override
|
||||
public void render(MapView map, MapCanvas canvas, Player player) {
|
||||
for (int dy = 0; dy < 128; dy++) {
|
||||
for (int dx = 0; dx < 128; dx++) {
|
||||
int ax = dx + finalX;
|
||||
int ay = dy + finalY;
|
||||
Color color = new Color(bufferedImage.getRGB(ax, ay));
|
||||
canvas.setPixel(dx, dy, ColorInit.getColorByte(color.getRed(), color.getGreen(), color.getBlue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mapMeta.setMapView(mapView);
|
||||
itemStack.setItemMeta(mapMeta);
|
||||
itemFrame.setItem(itemStack);
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int clamp(int value) {
|
||||
return Math.max(0, Math.min(255, value));
|
||||
private static void floodFill(BufferedImage bufferedImage) {
|
||||
WritableRaster alpha = bufferedImage.getAlphaRaster();
|
||||
int[] data = alpha.getPixels(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), new int[bufferedImage.getWidth() * bufferedImage.getHeight()]);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Set<Point> changes = new HashSet<>();
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y++) {
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x++) {
|
||||
if (data[y * bufferedImage.getWidth() + x] == 0) continue;
|
||||
int color = bufferedImage.getRGB(x, y);
|
||||
if (x > 0 && data[y * bufferedImage.getWidth() + x - 1] == 0) {
|
||||
bufferedImage.setRGB(x - 1, y, color);
|
||||
changes.add(new Point(x - 1, y));
|
||||
}
|
||||
if (x < bufferedImage.getWidth() - 1 && data[y * bufferedImage.getWidth() + x + 1] == 0) {
|
||||
bufferedImage.setRGB(x + 1, y, color);
|
||||
changes.add(new Point(x + 1, y));
|
||||
}
|
||||
if (y > 0 && data[(y - 1) * bufferedImage.getWidth() + x] == 0) {
|
||||
bufferedImage.setRGB(x, y - 1, color);
|
||||
changes.add(new Point(x, y - 1));
|
||||
}
|
||||
if (y < bufferedImage.getHeight() - 1 && data[(y + 1) * bufferedImage.getWidth() + x] == 0) {
|
||||
bufferedImage.setRGB(x, y + 1, color);
|
||||
changes.add(new Point(x, y + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changes.isEmpty()) return;
|
||||
changes.forEach(point -> {
|
||||
data[point.getY() * bufferedImage.getWidth() + point.getX()] = 255;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Color getNearest(Color color) {
|
||||
return MapPalette.getColor(MapPalette.matchColor(color));
|
||||
private static BufferedImage dither(BufferedImage image) {
|
||||
final double multiplier1 = 7 / 48.0;
|
||||
final double multiplier2 = 3 / 48.0;
|
||||
final double multiplier3 = 5 / 48.0;
|
||||
final double multiplier4 = 1 / 48.0;
|
||||
WritableRaster alphaRaster = image.getAlphaRaster();
|
||||
WritableRaster raster = image.getRaster();
|
||||
int numBands = raster.getNumBands();
|
||||
int[] pixels = raster.getPixels(0, 0, image.getWidth(), image.getHeight(), new int[image.getWidth() * image.getHeight() * numBands]);
|
||||
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (alphaRaster.getPixel(x, y, new int[1])[0] == 0) continue;
|
||||
|
||||
int red = pixels[(y * width + x) * numBands];
|
||||
int i2 = (y * width + x) * numBands + 1;
|
||||
int green = pixels[i2];
|
||||
int i3 = (y * width + x) * numBands + 2;
|
||||
int blue = pixels[i3];
|
||||
Color nearest = MapPalette.getColor(ColorInit.getColorByte(red, green, blue));
|
||||
|
||||
pixels[(y * width + x) * numBands] = nearest.getRed();
|
||||
pixels[i2] = nearest.getGreen();
|
||||
pixels[i3] = nearest.getBlue();
|
||||
|
||||
int quantErrorRed = red - nearest.getRed();
|
||||
int quantErrorGreen = green - nearest.getGreen();
|
||||
int quantErrorBlue = blue - nearest.getBlue();
|
||||
|
||||
int mr1 = (int) (quantErrorRed * multiplier1);
|
||||
int mg1 = (int) (quantErrorGreen * multiplier1);
|
||||
int mb1 = (int) (quantErrorBlue * multiplier1);
|
||||
|
||||
int mr2 = (int) (quantErrorRed * multiplier2);
|
||||
int mg2 = (int) (quantErrorGreen * multiplier2);
|
||||
int mb2 = (int) (quantErrorBlue * multiplier2);
|
||||
|
||||
int mr3 = (int) (quantErrorRed * multiplier3);
|
||||
int mg3 = (int) (quantErrorGreen * multiplier3);
|
||||
int mb3 = (int) (quantErrorBlue * multiplier3);
|
||||
|
||||
int mr4 = (int) (quantErrorRed * multiplier4);
|
||||
int mg4 = (int) (quantErrorGreen * multiplier4);
|
||||
int mb4 = (int) (quantErrorBlue * multiplier4);
|
||||
|
||||
if (x < width - 1) {
|
||||
int i = (y * width + x + 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr1);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg1);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb1);
|
||||
}
|
||||
|
||||
if (x < width - 2) {
|
||||
int i = (y * width + x + 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
|
||||
if (y < height - 1) {
|
||||
if (x > 1) {
|
||||
int i = ((y + 1) * width + x - 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
if (x > 0) {
|
||||
int i = ((y + 1) * width + x - 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
if (x < width - 1) {
|
||||
int i = ((y + 1) * width + x + 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
if (x < width - 2) {
|
||||
int i = ((y + 1) * width + x + 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
int i = (y * width + x) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr1);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg1);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb1);
|
||||
}
|
||||
|
||||
if (y < height - 2) {
|
||||
if (x > 1) {
|
||||
int i = ((y + 2) * width + x - 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr4);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg4);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb4);
|
||||
}
|
||||
if (x > 0) {
|
||||
int i = ((y + 2) * width + x - 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
if (x < width - 1) {
|
||||
int i = ((y + 2) * width + x + 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
if (x < width - 2) {
|
||||
int i = ((y + 2) * width + x + 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr4);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg4);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb4);
|
||||
}
|
||||
int i = (y * width + x) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
}
|
||||
}
|
||||
raster.setPixels(0, 0, width, height, pixels);
|
||||
return image;
|
||||
}
|
||||
|
||||
private static int clamp(int value) {
|
||||
return Math.max(0, Math.min(value, 255));
|
||||
}
|
||||
}
|
||||
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.lobby.map;
|
||||
|
||||
import de.steamwar.command.SWCommand;
|
||||
import de.steamwar.command.TypeMapper;
|
||||
import de.steamwar.command.TypeValidator;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.sql.UserPerm;
|
||||
import lombok.SneakyThrows;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class CustomMapCommand extends SWCommand {
|
||||
|
||||
public CustomMapCommand() {
|
||||
super("map");
|
||||
}
|
||||
|
||||
@Register
|
||||
@SneakyThrows
|
||||
public void render(@Validator Player p, File file) {
|
||||
if (!SteamwarUser.get(p.getUniqueId()).hasPerm(UserPerm.ADMINISTRATION)) {
|
||||
return;
|
||||
}
|
||||
if (!file.exists()) {
|
||||
p.sendMessage("§cDiese Datei existiert nicht!");
|
||||
return;
|
||||
}
|
||||
new CustomMap(p, ImageIO.read(file));
|
||||
}
|
||||
|
||||
@ClassValidator(value = Player.class, local = true)
|
||||
public TypeValidator<Player> getGuardChecker() {
|
||||
return (commandSender, player, messageSender) -> SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.ADMINISTRATION);
|
||||
}
|
||||
|
||||
@ClassMapper(value = File.class, local = true)
|
||||
public TypeMapper<File> getTypeMapper() {
|
||||
return new TypeMapper<File>() {
|
||||
@Override
|
||||
public List<String> tabCompletes(CommandSender commandSender, String[] strings, String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File map(CommandSender commandSender, String[] previousArguments, String s) {
|
||||
return new File(s);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,354 +0,0 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.lobby.map;
|
||||
|
||||
import de.steamwar.lobby.LobbySystem;
|
||||
import net.minecraft.world.level.saveddata.maps.WorldMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.map.CraftMapView;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.MapMeta;
|
||||
import org.bukkit.map.MapPalette;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class CustomMapNew {
|
||||
|
||||
public static void init() {
|
||||
}
|
||||
|
||||
private static final World WORLD = Bukkit.getWorlds().get(0);
|
||||
|
||||
private static final CustomMapNew LEFT = new CustomMapNew(new File(System.getProperty("user.home") + "/lobbyBanner/left.png"),
|
||||
new Vector(2346, 48, 1297), new Vector(2345, 48, 1297), new Vector(2344, 48, 1297), new Vector(2343, 48, 1297), new Vector(2342, 48, 1297), new Vector(2341, 48, 1297), new Vector(2340, 48, 1297),
|
||||
new Vector(2346, 47, 1297), new Vector(2345, 47, 1297), new Vector(2344, 47, 1297), new Vector(2343, 47, 1297), new Vector(2342, 47, 1297), new Vector(2341, 47, 1297), new Vector(2340, 47, 1297),
|
||||
new Vector(2346, 46, 1297), new Vector(2345, 46, 1297), new Vector(2344, 46, 1297), new Vector(2343, 46, 1297), new Vector(2342, 46, 1297), new Vector(2341, 46, 1297), new Vector(2340, 46, 1297),
|
||||
new Vector(2346, 45, 1297), new Vector(2345, 45, 1297), new Vector(2344, 45, 1297), new Vector(2343, 45, 1297), new Vector(2342, 45, 1297), new Vector(2341, 45, 1297), new Vector(2340, 45, 1297)
|
||||
);
|
||||
|
||||
private File mapFile;
|
||||
private ItemFrame[] itemFrames = null;
|
||||
|
||||
public CustomMapNew(File mapFile, Vector... itemFrames) {
|
||||
this.mapFile = mapFile;
|
||||
|
||||
Bukkit.getScheduler().runTaskTimer(LobbySystem.getPlugin(), () -> {
|
||||
long lastModified = 0;
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (this.itemFrames == null) {
|
||||
loadItemFrames(itemFrames);
|
||||
continue;
|
||||
}
|
||||
|
||||
long modified = mapFile.lastModified();
|
||||
System.out.println(lastModified + " " + modified);
|
||||
if (modified > lastModified) {
|
||||
lastModified = modified;
|
||||
System.out.println("Updating Banner: " + mapFile.getName());
|
||||
Bukkit.getScheduler().runTaskAsynchronously(LobbySystem.getPlugin(), () -> {
|
||||
try {
|
||||
run();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 200L, 200L);
|
||||
}
|
||||
|
||||
private void loadItemFrames(Vector... itemFrames) {
|
||||
for (int i = 0; i < itemFrames.length; i++) {
|
||||
itemFrames[i] = itemFrames[i].toLocation(WORLD).getBlock().getLocation().toVector();
|
||||
}
|
||||
Map<Vector, ItemFrame> worldMaps = new HashMap<>();
|
||||
WORLD.getEntitiesByClass(ItemFrame.class).forEach(itemFrame -> {
|
||||
ItemStack itemStack = itemFrame.getItem();
|
||||
System.out.println(itemFrame.getLocation().getBlock() + ": " + itemFrame + " " + itemStack);
|
||||
if (itemStack.getType() != Material.FILLED_MAP) return;
|
||||
worldMaps.put(itemFrame.getLocation().getBlock().getLocation().toVector(), itemFrame);
|
||||
});
|
||||
|
||||
ItemFrame[] frames = new ItemFrame[itemFrames.length];
|
||||
for (int i = 0; i < frames.length; i++) {
|
||||
if (worldMaps.get(itemFrames[i]) == null) {
|
||||
System.out.println("ItemFrame at location " + itemFrames[i] + " is missing!");
|
||||
return;
|
||||
}
|
||||
frames[i] = worldMaps.get(itemFrames[i]);
|
||||
}
|
||||
this.itemFrames = frames;
|
||||
}
|
||||
|
||||
private void run() throws IOException {
|
||||
BufferedImage bufferedImage = ImageIO.read(mapFile);
|
||||
|
||||
Set<Point>[] patches = new Set[256];
|
||||
for (int patch = 0; patch < patches.length; patch++) {
|
||||
patches[patch] = new HashSet<>();
|
||||
}
|
||||
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y++) {
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x++) {
|
||||
Color color = new Color(bufferedImage.getRGB(x, y));
|
||||
double red = color.getRed() / 255.0;
|
||||
double green = color.getGreen() / 255.0;
|
||||
double blue = color.getBlue() / 255.0;
|
||||
double luminance = Math.sqrt(0.299 * red * red + 0.587 * green * green + 0.114 * blue * blue);
|
||||
luminance *= 255;
|
||||
patches[(int) luminance].add(new Point(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
for (int patch = 0; patch < patches.length; patch++) {
|
||||
Set<Point> points = patches[patch];
|
||||
if (points.isEmpty()) continue;
|
||||
BufferedImage patchImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
points.forEach(point -> {
|
||||
patchImage.setRGB(point.getX(), point.getY(), bufferedImage.getRGB(point.getX(), point.getY()));
|
||||
});
|
||||
floodFill(patchImage);
|
||||
dither(patchImage);
|
||||
points.forEach(point -> {
|
||||
bufferedImage.setRGB(point.getX(), point.getY(), patchImage.getRGB(point.getX(), point.getY()));
|
||||
});
|
||||
}
|
||||
|
||||
for (int y = 0; y < bufferedImage.getHeight() / 128; y += 128) {
|
||||
for (int x = 0; x < bufferedImage.getWidth() / 128; x += 128) {
|
||||
ItemFrame itemFrame = itemFrames[y / 128 * 7 + x / 128];
|
||||
int finalX = x;
|
||||
int finalY = y;
|
||||
Bukkit.getScheduler().runTaskLater(LobbySystem.getPlugin(), () -> {
|
||||
ItemStack itemStack = itemFrame.getItem();
|
||||
MapMeta mapMeta = (MapMeta) itemStack.getItemMeta();
|
||||
CraftMapView mapView = (CraftMapView) mapMeta.getMapView();
|
||||
|
||||
try {
|
||||
Field field = CraftMapView.class.getDeclaredField("worldMap");
|
||||
field.setAccessible(true);
|
||||
WorldMap worldMap = (WorldMap) field.get(mapView);
|
||||
|
||||
for (int dy = 0; dy < 128; dy++) {
|
||||
for (int dx = 0; dx < 128; dx++) {
|
||||
int ax = dx + finalX;
|
||||
int ay = dy + finalY;
|
||||
Color color = new Color(bufferedImage.getRGB(ax, ay));
|
||||
worldMap.g[dx + dy * 128] = ColorInit.getColorByte(color.getRed(), color.getGreen(), color.getBlue());
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new SecurityException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
mapMeta.setMapView(mapView);
|
||||
itemStack.setItemMeta(mapMeta);
|
||||
itemFrame.setItem(itemStack);
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void floodFill(BufferedImage bufferedImage) {
|
||||
WritableRaster alpha = bufferedImage.getAlphaRaster();
|
||||
int[] data = alpha.getPixels(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), new int[bufferedImage.getWidth() * bufferedImage.getHeight()]);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Set<Point> changes = new HashSet<>();
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y++) {
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x++) {
|
||||
if (data[y * bufferedImage.getWidth() + x] == 0) continue;
|
||||
int color = bufferedImage.getRGB(x, y);
|
||||
if (x > 0 && data[y * bufferedImage.getWidth() + x - 1] == 0) {
|
||||
bufferedImage.setRGB(x - 1, y, color);
|
||||
changes.add(new Point(x - 1, y));
|
||||
}
|
||||
if (x < bufferedImage.getWidth() - 1 && data[y * bufferedImage.getWidth() + x + 1] == 0) {
|
||||
bufferedImage.setRGB(x + 1, y, color);
|
||||
changes.add(new Point(x + 1, y));
|
||||
}
|
||||
if (y > 0 && data[(y - 1) * bufferedImage.getWidth() + x] == 0) {
|
||||
bufferedImage.setRGB(x, y - 1, color);
|
||||
changes.add(new Point(x, y - 1));
|
||||
}
|
||||
if (y < bufferedImage.getHeight() - 1 && data[(y + 1) * bufferedImage.getWidth() + x] == 0) {
|
||||
bufferedImage.setRGB(x, y + 1, color);
|
||||
changes.add(new Point(x, y + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changes.isEmpty()) return;
|
||||
changes.forEach(point -> {
|
||||
data[point.getY() * bufferedImage.getWidth() + point.getX()] = 255;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static BufferedImage dither(BufferedImage image) {
|
||||
final double multiplier1 = 7 / 48.0;
|
||||
final double multiplier2 = 3 / 48.0;
|
||||
final double multiplier3 = 5 / 48.0;
|
||||
final double multiplier4 = 1 / 48.0;
|
||||
WritableRaster alphaRaster = image.getAlphaRaster();
|
||||
WritableRaster raster = image.getRaster();
|
||||
int numBands = raster.getNumBands();
|
||||
int[] pixels = raster.getPixels(0, 0, image.getWidth(), image.getHeight(), new int[image.getWidth() * image.getHeight() * numBands]);
|
||||
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (alphaRaster.getPixel(x, y, new int[1])[0] == 0) continue;
|
||||
|
||||
int red = pixels[(y * width + x) * numBands];
|
||||
int i2 = (y * width + x) * numBands + 1;
|
||||
int green = pixels[i2];
|
||||
int i3 = (y * width + x) * numBands + 2;
|
||||
int blue = pixels[i3];
|
||||
Color nearest = MapPalette.getColor(ColorInit.getColorByte(red, green, blue));
|
||||
|
||||
pixels[(y * width + x) * numBands] = nearest.getRed();
|
||||
pixels[i2] = nearest.getGreen();
|
||||
pixels[i3] = nearest.getBlue();
|
||||
|
||||
int quantErrorRed = red - nearest.getRed();
|
||||
int quantErrorGreen = green - nearest.getGreen();
|
||||
int quantErrorBlue = blue - nearest.getBlue();
|
||||
|
||||
int mr1 = (int) (quantErrorRed * multiplier1);
|
||||
int mg1 = (int) (quantErrorGreen * multiplier1);
|
||||
int mb1 = (int) (quantErrorBlue * multiplier1);
|
||||
|
||||
int mr2 = (int) (quantErrorRed * multiplier2);
|
||||
int mg2 = (int) (quantErrorGreen * multiplier2);
|
||||
int mb2 = (int) (quantErrorBlue * multiplier2);
|
||||
|
||||
int mr3 = (int) (quantErrorRed * multiplier3);
|
||||
int mg3 = (int) (quantErrorGreen * multiplier3);
|
||||
int mb3 = (int) (quantErrorBlue * multiplier3);
|
||||
|
||||
int mr4 = (int) (quantErrorRed * multiplier4);
|
||||
int mg4 = (int) (quantErrorGreen * multiplier4);
|
||||
int mb4 = (int) (quantErrorBlue * multiplier4);
|
||||
|
||||
if (x < width - 1) {
|
||||
int i = (y * width + x + 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr1);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg1);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb1);
|
||||
}
|
||||
|
||||
if (x < width - 2) {
|
||||
int i = (y * width + x + 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
|
||||
if (y < height - 1) {
|
||||
if (x > 1) {
|
||||
int i = ((y + 1) * width + x - 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
if (x > 0) {
|
||||
int i = ((y + 1) * width + x - 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
if (x < width - 1) {
|
||||
int i = ((y + 1) * width + x + 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
if (x < width - 2) {
|
||||
int i = ((y + 1) * width + x + 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
int i = (y * width + x) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr1);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg1);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb1);
|
||||
}
|
||||
|
||||
if (y < height - 2) {
|
||||
if (x > 1) {
|
||||
int i = ((y + 2) * width + x - 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr4);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg4);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb4);
|
||||
}
|
||||
if (x > 0) {
|
||||
int i = ((y + 2) * width + x - 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
if (x < width - 1) {
|
||||
int i = ((y + 2) * width + x + 1) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr2);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg2);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb2);
|
||||
}
|
||||
if (x < width - 2) {
|
||||
int i = ((y + 2) * width + x + 2) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr4);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg4);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb4);
|
||||
}
|
||||
int i = (y * width + x) * numBands;
|
||||
pixels[i] = clamp(pixels[i] + mr3);
|
||||
pixels[i + 1] = clamp(pixels[i + 1] + mg3);
|
||||
pixels[i + 2] = clamp(pixels[i + 2] + mb3);
|
||||
}
|
||||
}
|
||||
}
|
||||
raster.setPixels(0, 0, width, height, pixels);
|
||||
return image;
|
||||
}
|
||||
|
||||
private static int clamp(int value) {
|
||||
return Math.max(0, Math.min(value, 255));
|
||||
}
|
||||
}
|
In neuem Issue referenzieren
Einen Benutzer sperren