diff --git a/README.md b/README.md index 5f28cf0..6254f88 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # MapReflectionAPI This API helps developer with viewing images on maps. It supports Spigot 1.12 - 1.19. -It currently has **no** support for GIFs. ## Dependencies: @@ -21,7 +20,7 @@ First, include the API using Maven: tech.sbdevelopment MapReflectionAPI - 1.2 + 1.1 provided ``` @@ -57,4 +56,4 @@ More information can be found on the [JavaDoc](https://sbdplugins.nl/javadoc/map ## Credits: This is a fork of [MapManager](https://github.com/InventivetalentDev/MapManager). It updates the API to 1.19 and uses -other dependencies. +other dependencies. \ No newline at end of file diff --git a/pom.xml b/pom.xml index ea74716..2d86681 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ tech.sbdevelopment MapReflectionAPI - 1.2 + 1.3 jar MapReflectionAPI @@ -77,6 +77,16 @@ false + + + com.bergerkiller.bukkit.common + tech.sbdevelopment.mapreflectionapi.libs.bkcommonlib + + + org.bstats + tech.sbdevelopment.mapreflectionapi.libs.bstats + + @@ -135,6 +145,9 @@ 11 ${maven.lombok.delombok-target} + **/com/bergerkiller/bukkit/common/io/*.java + **/com/bergerkiller/bukkit/common/map/*.java + **/com/bergerkiller/bukkit/common/map/color/*.java **/tech/sbdevelopment/mapreflectionapi/*.java **/tech/sbdevelopment/mapreflectionapi/utils/*.java **/tech/sbdevelopment/mapreflectionapi/listeners/*.java @@ -146,6 +159,15 @@ src/main/resources true + + plugin.yml + + + + src/main/resources + + plugin.yml + @@ -172,12 +194,6 @@ 1.19.1-R0.1-SNAPSHOT provided - - com.bergerkiller.bukkit - BKCommonLib - 1.19-v2 - provided - com.comphenix.protocol ProtocolLib @@ -190,5 +206,20 @@ 1.18.24 provided + + + + org.jetbrains + annotations-java5 + 23.0.0 + provided + + + + org.bstats + bstats-bukkit + 3.0.0 + compile + \ No newline at end of file diff --git a/src/main/java/com/bergerkiller/bukkit/common/LICENSE b/src/main/java/com/bergerkiller/bukkit/common/LICENSE new file mode 100644 index 0000000..8643c4a --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/LICENSE @@ -0,0 +1,56 @@ +MIT License + +Copyright (C) 2013-2015 bergerkiller Copyright (C) 2016-2020 Berger Healer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, and/or sublicense the Software, +and to permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +========================================================================================== + +BKCommonLib utilizes Objensis and Javassist for some required functionality. Licenses are listed below: + +====================================== Objenesis ========================================= +http://objenesis.googlecode.com/svn/docs/index.html + +Copyright (c) 2003-2013, Objenesis Team and all contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +====================================== Javassist ========================================= + +The Source Code version of the Covered Code of Javassist is available under the +MOZILLA PUBLIC LICENSE Version 1.1. The project can be found (oct-2020) here: + +https://github.com/jboss-javassist/javassist + +Javassist was not modified for the purpose of this software. \ No newline at end of file diff --git a/src/main/java/com/bergerkiller/bukkit/common/README.md b/src/main/java/com/bergerkiller/bukkit/common/README.md new file mode 100644 index 0000000..ce9a09c --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/README.md @@ -0,0 +1,6 @@ +# BKCommonLib + +These classes are from [BKCommonLib](https://github.com/bergerhealer/BKCommonLib). Only the required classes and methods +are extracted. + +Current version: master#25ad90b702cabbbf632c40f6ed209241aee38a41 (1.19.1-v1 release) \ No newline at end of file diff --git a/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java b/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java new file mode 100644 index 0000000..706a4c9 --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java @@ -0,0 +1,104 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.bergerkiller.bukkit.common.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Input Stream that can also read individual bits + */ +public class BitInputStream extends InputStream { + private int bitbuff = 0; + private int bitbuff_len = 0; + private boolean closed = false; + private final InputStream input; + private final boolean closeInput; + + /** + * Initializes a new Bit Input Stream, reading from the Input Stream specified + * + * @param inputStream to read from + */ + public BitInputStream(InputStream inputStream) { + this(inputStream, true); + } + + /** + * Initializes a new Bit Input Stream, reading from the Input Stream specified + * + * @param inputStream to read from + * @param closeInputStream whether to close the underlying input stream when closing this stream + */ + public BitInputStream(InputStream inputStream, boolean closeInputStream) { + this.input = inputStream; + this.closeInput = closeInputStream; + } + + @Override + public int available() throws IOException { + if (this.closed) { + throw new IOException("Stream is closed"); + } + return this.input.available(); + } + + @Override + public int read() throws IOException { + return readBits(8); + } + + /** + * Reads bits from the stream + * + * @param nBits to read + * @return read value, -1 when end of stream is reached + * @throws IOException + */ + public int readBits(int nBits) throws IOException { + if (this.closed) { + throw new IOException("Stream is closed"); + } + while (this.bitbuff_len < nBits) { + int readByte = this.input.read(); + if (readByte == -1) return -1; + this.bitbuff |= (readByte << this.bitbuff_len); + this.bitbuff_len += 8; + } + int result = bitbuff & ((1 << nBits) - 1); + this.bitbuff >>= nBits; + this.bitbuff_len -= nBits; + return result; + } + + @Override + public void close() throws IOException { + if (!this.closed) { + this.closed = true; + if (this.closeInput) { + this.input.close(); + } + } + } +} diff --git a/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java b/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java new file mode 100644 index 0000000..755779f --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java @@ -0,0 +1,51 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.bergerkiller.bukkit.common.io; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * Simple container for multiple bits of data. + */ +@EqualsAndHashCode +@ToString +public class BitPacket implements Cloneable { + public int data, bits; + + public BitPacket() { + this.data = 0; + this.bits = 0; + } + + public BitPacket(int data, int bits) { + this.data = data; + this.bits = bits; + } + + @Override + public BitPacket clone() { + return new BitPacket(this.data, this.bits); + } +} diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java b/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java new file mode 100644 index 0000000..3feda88 --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java @@ -0,0 +1,226 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.bergerkiller.bukkit.common.map; + +import com.bergerkiller.bukkit.common.map.color.MCSDBubbleFormat; +import com.bergerkiller.bukkit.common.map.color.MCSDGenBukkit; +import com.bergerkiller.bukkit.common.map.color.MapColorSpaceData; +import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; + +import java.awt.*; +import java.io.InputStream; +import java.util.Arrays; + +/** + * Additional functionality on top of Bukkit's MapPalette + */ +public class MapColorPalette { + private static final MapColorSpaceData COLOR_MAP_DATA; + public static final byte[] COLOR_MAP_AVERAGE = new byte[0x10000]; + public static final byte[] COLOR_MAP_ADD = new byte[0x10000]; + public static final byte[] COLOR_MAP_SUBTRACT = new byte[0x10000]; + public static final byte[] COLOR_MAP_MULTIPLY = new byte[0x10000]; + public static final byte[] COLOR_MAP_SPECULAR = new byte[0x10000]; + + public static final byte COLOR_TRANSPARENT = 0; + + static { + // Now we know java.awt exists we can initialize this one + COLOR_MAP_DATA = new MapColorSpaceData(); + + // Load color map data from the Bubble format file bundled with the library + { + boolean success = false; + MCSDBubbleFormat bubbleData = new MCSDBubbleFormat(); + try { + String bub_path_postfix; + if (ReflectionUtil.supports(17)) { + bub_path_postfix = "map_1_17.bub"; + } else if (ReflectionUtil.supports(16)) { + bub_path_postfix = "map_1_16.bub"; + } else if (ReflectionUtil.supports(12)) { + bub_path_postfix = "map_1_12.bub"; + } else { + bub_path_postfix = "map_1_8_8.bub"; + } + String bub_path = "/tech/sbdevelopment/mapreflectionapi/libs/bkcommonlib/internal/resources/map/" + bub_path_postfix; + InputStream input = MapColorPalette.class.getResourceAsStream(bub_path); + if (input != null) { + bubbleData.readFrom(input); + success = true; + } + } catch (Exception e) { + e.printStackTrace(); + } + + if (success) { + COLOR_MAP_DATA.readFrom(bubbleData); + } else { + MCSDGenBukkit bukkitGen = new MCSDGenBukkit(); + bukkitGen.generate(); + COLOR_MAP_DATA.readFrom(bukkitGen); + } + } + + // Generate 256 lightness values for all colors + for (int a = 0; a < 256; a++) { + int index = (a * 256); + Color color_a = getRealColor((byte) a); + if (color_a.getAlpha() < 128) { + // All specular colors for the transparent color are transparent + Arrays.fill(COLOR_MAP_SPECULAR, index, index + 256, COLOR_TRANSPARENT); + } else { + for (int b = 0; b < 256; b++) { + // 0.0 = black + // 1.0 = natural color + // 2.0 = white + float f = b / 128.0f; + int sr = (int) (color_a.getRed() * f); + int sg = (int) (color_a.getGreen() * f); + int sb = (int) (color_a.getBlue() * f); + COLOR_MAP_SPECULAR[index++] = getColor(sr, sg, sb); + } + } + } + + // Initialize the color map tables for all possible color values + for (int c1 = 0; c1 < 256; c1++) { + for (int c2 = 0; c2 < 256; c2++) { + initTable((byte) c1, (byte) c2); + } + } + } + + private static void initTable(byte color1, byte color2) { + int index = getMapIndex(color1, color2); + if (isTransparent(color1) || isTransparent(color2)) { + initTransparent(index, color2); + } else { + Color c1 = getRealColor(color1); + Color c2 = getRealColor(color2); + initColor( + index, + c1.getRed(), c1.getGreen(), c1.getBlue(), + c2.getRed(), c2.getGreen(), c2.getBlue() + ); + } + } + + private static void initTransparent(int index, byte color2) { + COLOR_MAP_AVERAGE[index] = color2; + COLOR_MAP_ADD[index] = color2; + COLOR_MAP_SUBTRACT[index] = color2; + COLOR_MAP_MULTIPLY[index] = (byte) 0; + } + + private static void initColor(int index, int r1, int g1, int b1, int r2, int g2, int b2) { + initArray(COLOR_MAP_AVERAGE, index, (r1 + r2) >> 1, (g1 + g2) >> 1, (b1 + b2) >> 1); + initArray(COLOR_MAP_ADD, index, (r1 + r2), (g1 + g2), (b1 + b2)); + initArray(COLOR_MAP_SUBTRACT, index, (r2 - r1), (g2 - g1), (b2 - b1)); + initArray(COLOR_MAP_MULTIPLY, index, (r1 * r2) / 255, (g1 * g2) / 255, (b1 * b2) / 255); + } + + private static void initArray(byte[] array, int index, int r, int g, int b) { + if (r < 0x00) r = 0x00; + if (r > 0xFF) r = 0xFF; + if (g < 0x00) g = 0x00; + if (g > 0xFF) g = 0xFF; + if (b < 0x00) b = 0x00; + if (b > 0xFF) b = 0xFF; + array[index] = getColor(r, g, b); + } + + /** + * Gets whether a particular color code is a transparent color. + * There are 4 transparent colors available. Usually value 0 is used. + * + * @param color value + * @return True if transparent + */ + public static boolean isTransparent(byte color) { + return (color & 0xFF) < 0x4; + } + + /** + * Gets the Minecraft map color code for an RGB color + * + * @param color input + * @return minecraft color + */ + public static byte getColor(Color color) { + if ((color.getAlpha() & 0x80) == 0) { + return COLOR_TRANSPARENT; + } else { + return COLOR_MAP_DATA.get(color.getRed(), color.getGreen(), color.getBlue()); + } + } + + /** + * Gets the Minecraft map color code for an RGB color + * + * @param r - red component + * @param g - green component + * @param b - blue component + * @return minecraft color + */ + public static byte getColor(int r, int g, int b) { + // This helps prevent dumb exceptions. + // Nobody likes random exceptions when all you're doing is color calculations + if (r < 0) + r = 0; + else if (r > 255) + r = 255; + if (g < 0) + g = 0; + else if (g > 255) + g = 255; + if (b < 0) + b = 0; + else if (b > 255) + b = 255; + + return COLOR_MAP_DATA.get(r, g, b); + } + + /** + * Gets the index into one of the palette remap arrays + * + * @param color_a first color + * @param color_b second color + * @return index + */ + public static int getMapIndex(byte color_a, byte color_b) { + return (color_a & 0xFF) | ((color_b & 0xFF) << 8); + } + + /** + * Gets the real RGB color belonging to a color code + * + * @param color code input + * @return real RGB color + */ + public static Color getRealColor(byte color) { + return COLOR_MAP_DATA.getColor(color); + } +} diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDBubbleFormat.java b/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDBubbleFormat.java new file mode 100644 index 0000000..5928bf5 --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDBubbleFormat.java @@ -0,0 +1,224 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.bergerkiller.bukkit.common.map.color; + +import com.bergerkiller.bukkit.common.io.BitInputStream; + +import java.awt.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.zip.InflaterInputStream; + +/** + * Stores all map color space information in a highly compressed bubble format. + * In this format it is assumed the color data is in cell shapes. It stores the cell + * borders separate from the colors using the {@link MCSDWebbingCodec}. These cells + * are then filled with colors to reproduce the original image. + */ +public class MCSDBubbleFormat extends MapColorSpaceData { + public final boolean[][] strands = new boolean[256][256 * 256]; + public final List bubbles = new ArrayList<>(); + + public void readFrom(InputStream stream) throws IOException { + try (BitInputStream bitStream = new BitInputStream(new InflaterInputStream(stream))) { + // Read all color RGB values + for (int i = 0; i < 256; i++) { + int r = bitStream.read(); + int g = bitStream.read(); + int b = bitStream.read(); + int a = bitStream.read(); + this.setColor((byte) i, new Color(r, g, b, a)); + } + + // Read all bubbles from the stream + while (true) { + Bubble bubble = new Bubble(); + bubble.color = (byte) bitStream.read(); + if (bubble.color == 0) { + break; + } + bubble.x = bitStream.read(); + bubble.y = bitStream.read(); + bubble.z_min = bitStream.read(); + bubble.z_max = bubble.z_min + bitStream.read(); + this.bubbles.add(bubble); + } + + // Read bubble boundary information from the stream + MCSDWebbingCodec codec = new MCSDWebbingCodec(); + for (int z = 0; z < 256; z++) { + Arrays.fill(this.strands[z], false); + codec.reset(strands[z], false); + while (codec.readNext(bitStream)) ; + } + + // Initialize the colors with the bubble colors + this.initColors(); + + // Read color correction data for pixels unset (value = 0) + for (int i = 0; i < (1 << 24); i++) { + if (this.get(i) == 0) { + if (bitStream.readBits(1) == 0) { + this.set(i, this.get(i - 1)); + } else { + int mode = bitStream.readBits(2); + if (mode == 0) { + this.set(i, this.get(i - 256)); + } else if (mode == 1) { + this.set(i, this.get(i + 1)); + } else if (mode == 2) { + this.set(i, this.get(i + 256)); + } else { + this.set(i, (byte) bitStream.readBits(8)); + } + } + } + } + } + } + + private void initColors() { + // Set initial cell colors + this.clearRGBData(); + for (Bubble cell : bubbles) { + for (int z = cell.z_min; z <= cell.z_max; z++) { + this.set(cell.x, cell.y, z, cell.color); + } + } + spreadColors(); + } + + private void spreadColors() { + final boolean[] all_strands = new boolean[1 << 24]; + for (int z = 0; z < 256; z++) { + System.arraycopy(this.strands[z], 0, all_strands, z << 16, 1 << 16); + } + + boolean mode = false; + boolean hasChanges; + do { + hasChanges = false; + + // Alternate the direction in which we process every step + // This prevents really slow filling when the direction is 'wrong' + // The below logic is partially based on the light fixing algorithm in Light Cleaner + final int index_end, index_delta; + int index; + byte color; + if (mode = !mode) { + index_delta = 1; + index = 0; + index_end = (1 << 24); + } else { + index_delta = -1; + index = (1 << 24) - 1; + index_end = 0; + } + do { + if (!all_strands[index]) { + all_strands[index] = true; + + if ((index & 0xFF) < 0xFF) { + if ((color = this.get(index + 1)) != 0) { + this.set(index, color); + hasChanges = true; + } else if ((color = this.get(index)) != 0) { + this.set(index + 1, color); + hasChanges = true; + } else { + all_strands[index] = false; // retry + } + } + + if ((index & 0xFF00) < 0xFF00) { + if ((color = this.get(index + 256)) != 0) { + this.set(index, color); + hasChanges = true; + } else if ((color = this.get(index)) != 0) { + this.set(index + 256, color); + hasChanges = true; + } else { + all_strands[index] = false; // retry + } + } + } + } while ((index += index_delta) != index_end); + } while (hasChanges); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (o instanceof MCSDBubbleFormat) { + MCSDBubbleFormat other = (MCSDBubbleFormat) o; + for (int i = 0; i < strands.length; i++) { + if (other.strands[i] != this.strands[i]) { + return false; + } + } + if (bubbles.size() != other.bubbles.size()) { + return false; + } + for (int i = 0; i < bubbles.size(); i++) { + if (!bubbles.get(i).equals(other.bubbles.get(i))) { + return false; + } + } + return true; + } else { + return false; + } + } + + public static class Bubble { + public int x, y; + public int z_min; + public int z_max; + public byte color; + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (o instanceof Bubble) { + Bubble other = (Bubble) o; + return other.x == x && other.y == y && + other.z_min == z_min && other.z_max == z_max && + other.color == color; + } else { + return false; + } + } + + @Override + public String toString() { + return "cell{x=" + x + ", y=" + y + ", zmin=" + z_min + ", zmax=" + z_max + ", color=" + (color & 0xFF) + "}"; + } + } + +} diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java b/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java new file mode 100644 index 0000000..77b67c1 --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java @@ -0,0 +1,50 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.bergerkiller.bukkit.common.map.color; + +import org.bukkit.map.MapPalette; + +public class MCSDGenBukkit extends MapColorSpaceData { + + /** + * Generates the color map information by using Bukkit's algorithms. + */ + @SuppressWarnings("deprecation") + public void generate() { + this.clear(); + for (int i = 0; i < 256; i++) { + try { + setColor((byte) i, MapPalette.getColor((byte) i)); + } catch (Exception ignored) { + } + } + for (int r = 0; r < 256; r++) { + for (int g = 0; g < 256; g++) { + for (int b = 0; b < 256; b++) { + set(r, g, b, MapPalette.matchColor(r, g, b)); + } + } + } + } +} diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java b/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java new file mode 100644 index 0000000..752cb07 --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java @@ -0,0 +1,128 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.bergerkiller.bukkit.common.map.color; + +import com.bergerkiller.bukkit.common.io.BitInputStream; +import com.bergerkiller.bukkit.common.io.BitPacket; + +import java.io.IOException; + +/** + * Encodes or decodes a 256x256 grid of booleans by walking down the connected lines and encoding them + * using drawing instructions. For example, a diagonal line in the grid may be encoded as follows: + * + *

+ * For encoding the data, the follow bits are written out in sequence: + *

+ */ +public class MCSDWebbingCodec { + private int last_x, last_y; + private int last_dx, last_dy; + public boolean[] strands = new boolean[1 << 16]; + private final BitPacket[] packets = new BitPacket[1024]; + + public MCSDWebbingCodec() { + for (int i = 0; i < this.packets.length; i++) { + this.packets[i] = new BitPacket(); + } + } + + public void reset(boolean[] cells, boolean copyCells) { + if (copyCells) { + System.arraycopy(cells, 0, this.strands, 0, cells.length); + } else { + this.strands = cells; + } + this.last_x = -1000; + this.last_y = -1000; + this.last_dx = 1; + this.last_dy = 1; + } + + public boolean readNext(BitInputStream stream) throws IOException { + int op = stream.readBits(2); + if (op == 0b11) { + if (stream.readBits(1) == 1) { + // Set DX/DY increment/decrement + int sub = stream.readBits(2); + if (sub == 0b00) { + last_dx = -1; + } else if (sub == 0b01) { + last_dx = 1; + } else if (sub == 0b10) { + last_dy = -1; + } else if (sub == 0b11) { + last_dy = 1; + } + } else { + // Command codes + if (stream.readBits(1) == 1) { + // End of slice + return false; + } else { + // Reset position + last_x = stream.readBits(8); + last_y = stream.readBits(8); + strands[last_x | (last_y << 8)] = true; + } + } + } else { + // Write next pixel + if (op == 0b00) { + last_x += last_dx; + } else if (op == 0b01) { + last_y += last_dy; + } else if (op == 0b10) { + last_x += last_dx; + last_y += last_dy; + } else if (op == -1) { + // End of stream + return false; + } + strands[last_x | (last_y << 8)] = true; + } + return true; + } +} diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java b/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java new file mode 100644 index 0000000..3c5e338 --- /dev/null +++ b/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java @@ -0,0 +1,160 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.bergerkiller.bukkit.common.map.color; + +import java.awt.*; +import java.util.Arrays; + +/** + * Stores the raw map color space data, enabling transformation between different storage methods. + */ +public class MapColorSpaceData implements Cloneable { + private final Color[] colors = new Color[256]; + private final byte[] data = new byte[1 << 24]; + + public MapColorSpaceData() { + Arrays.fill(this.colors, new Color(0, 0, 0, 0)); + } + + /** + * Clears only the RGB data. Equivalent to using {@link #set(int, byte)} on all RGB colors. + */ + public final void clearRGBData() { + Arrays.fill(this.data, (byte) 0); + } + + /** + * Clears all data, setting all colors to transparent + */ + public final void clear() { + Arrays.fill(this.colors, new Color(0, 0, 0, 0)); + Arrays.fill(this.data, (byte) 0); + } + + /** + * Sets all color data of this color space data to that from the input color space data + * + * @param data to set + */ + public void readFrom(MapColorSpaceData data) { + System.arraycopy(data.data, 0, this.data, 0, this.data.length); + System.arraycopy(data.colors, 0, this.colors, 0, this.colors.length); + } + + /** + * Sets a single map palette color + * + * @param code of the color + * @param color to set to + */ + public final void setColor(byte code, Color color) { + this.colors[code & 0xFF] = color; + } + + /** + * Gets a single map palette color + * + * @param code of the color + * @return map palette color + */ + public final Color getColor(byte code) { + return this.colors[code & 0xFF]; + } + + /** + * Sets the map color code value for an rgb value + * + * @param r component + * @param g component + * @param b component + * @param code to set to + */ + public final void set(int r, int g, int b, byte code) { + this.data[getDataIndex(r, g, b)] = code; + } + + /** + * Gets the map color code value for an rgb value + * + * @param r component + * @param g component + * @param b component + * @return color code + */ + public final byte get(int r, int g, int b) { + return this.data[getDataIndex(r, g, b)]; + } + + /** + * Sets the map color code for an rgb value + * + * @param index rgb compound value + * @param code to set to + */ + public final void set(int index, byte code) { + this.data[index] = code; + } + + /** + * Gets the map color code for an rgb value + * + * @param index rgb compound value + * @return color code + */ + public final byte get(int index) { + return this.data[index]; + } + + @Override + public MapColorSpaceData clone() { + MapColorSpaceData clone = new MapColorSpaceData(); + System.arraycopy(this.colors, 0, clone.colors, 0, this.colors.length); + System.arraycopy(this.data, 0, clone.data, 0, this.data.length); + return clone; + } + + /** + * Gets the mapping index of an rgb value + * + * @param r component + * @param g component + * @param b component + * @return index + */ + private static int getDataIndex(byte r, byte g, byte b) { + return (r & 0xFF) + ((g & 0xFF) << 8) + ((b & 0xFF) << 16); + } + + /** + * Gets the mapping index of an rgb value + * + * @param r component + * @param g component + * @param b component + * @return index + */ + private static int getDataIndex(int r, int g, int b) { + return (r & 0xFF) + ((g & 0xFF) << 8) + ((b & 0xFF) << 16); + } +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java index 9543685..cfe00d1 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java @@ -23,7 +23,10 @@ package tech.sbdevelopment.mapreflectionapi; +import com.bergerkiller.bukkit.common.map.MapColorPalette; import com.comphenix.protocol.ProtocolLibrary; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.SingleLineChart; import org.bukkit.Bukkit; import org.bukkit.map.MapView; import org.bukkit.plugin.java.JavaPlugin; @@ -32,6 +35,7 @@ import tech.sbdevelopment.mapreflectionapi.cmd.MapManagerCMD; import tech.sbdevelopment.mapreflectionapi.listeners.MapListener; import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; import tech.sbdevelopment.mapreflectionapi.managers.Configuration; +import tech.sbdevelopment.mapreflectionapi.utils.MainUtil; import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager; @@ -75,11 +79,15 @@ public class MapReflectionAPI extends JavaPlugin { return; } - if (!Bukkit.getPluginManager().isPluginEnabled("BKCommonLib")) { - getLogger().severe("MapReflectionAPI requires BKCommonLib to function!"); + getLogger().info("Loading Java AWT runtime library support..."); + if (MainUtil.isHeadlessJDK()) { + getLogger().severe("MapReflectionAPI requires the Java AWT runtime library, but is not available!"); + getLogger().severe("This is usually because a headless JVM is used for the server."); + getLogger().severe("Please install and configure a non-headless JVM to make this plugin work."); Bukkit.getPluginManager().disablePlugin(this); return; } + MapColorPalette.getColor(0, 0, 0); //Initializes the class if (!Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) { getLogger().severe("MapReflectionAPI requires ProtocolLib to function!"); @@ -119,42 +127,50 @@ public class MapReflectionAPI extends JavaPlugin { Bukkit.getPluginManager().registerEvents(new MapListener(), this); ProtocolLibrary.getProtocolManager().addPacketListener(new PacketListener(this)); + getLogger().info("Loading metrics..."); + Metrics metrics = new Metrics(this, 16033); + metrics.addCustomChart(new SingleLineChart("managed_maps", () -> mapManager.getManagedMapsCount())); + if (Configuration.getInstance().isUpdaterCheck()) { - UpdateManager updateManager = new UpdateManager(this, UpdateManager.CheckType.SPIGOT); + try { + UpdateManager updateManager = new UpdateManager(this, UpdateManager.CheckType.SPIGOT); - updateManager.handleResponse((versionResponse, version) -> { - switch (versionResponse) { - case FOUND_NEW: - getLogger().warning("There is a new version available! Current: " + getDescription().getVersion() + " New: " + version.get()); - if (Configuration.getInstance().isUpdaterDownload()) { - getLogger().info("Trying to download the update. This could take some time..."); + updateManager.handleResponse((versionResponse, version) -> { + switch (versionResponse) { + case FOUND_NEW: + getLogger().warning("There is a new version available! Current: " + getDescription().getVersion() + " New: " + version.get()); + if (Configuration.getInstance().isUpdaterDownload()) { + getLogger().info("Trying to download the update. This could take some time..."); - updateManager.handleDownloadResponse((downloadResponse, fileName) -> { - switch (downloadResponse) { - case DONE: - getLogger().info("Update downloaded! If you restart your server, it will be loaded. Filename: " + fileName); - break; - case ERROR: - getLogger().severe("Something went wrong when trying downloading the latest version."); - break; - case UNAVAILABLE: - getLogger().warning("Unable to download the latest version."); - break; - } - }).runUpdate(); - } - break; - case LATEST: - getLogger().info("You are running the latest version [" + getDescription().getVersion() + "]!"); - break; - case THIS_NEWER: - getLogger().info("You are running a newer version [" + getDescription().getVersion() + "]! This is probably fine."); - break; - case UNAVAILABLE: - getLogger().severe("Unable to perform an update check."); - break; - } - }).check(); + updateManager.handleDownloadResponse((downloadResponse, fileName) -> { + switch (downloadResponse) { + case DONE: + getLogger().info("Update downloaded! If you restart your server, it will be loaded. Filename: " + fileName); + break; + case ERROR: + getLogger().severe("Something went wrong when trying downloading the latest version."); + break; + case UNAVAILABLE: + getLogger().warning("Unable to download the latest version."); + break; + } + }).runUpdate(); + } + break; + case LATEST: + getLogger().info("You are running the latest version [" + getDescription().getVersion() + "]!"); + break; + case THIS_NEWER: + getLogger().info("You are running a newer version [" + getDescription().getVersion() + "]! This is probably fine."); + break; + case UNAVAILABLE: + getLogger().severe("Unable to perform an update check."); + break; + } + }).check(); + } catch (IllegalStateException ex) { + ex.printStackTrace(); + } } getLogger().info("MapReflectionAPI is enabled!"); diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java new file mode 100644 index 0000000..b5ba6a6 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java @@ -0,0 +1,38 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package tech.sbdevelopment.mapreflectionapi.api; + +public abstract class AbstractMapWrapper { + /** + * Get the controller of this wrapper + * + * @return The {@link MapController} + */ + public abstract IMapController getController(); + + protected void unwrap() { + getController().cancelSend(); + getController().clearViewers(); + } +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java new file mode 100644 index 0000000..f1f9394 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java @@ -0,0 +1,85 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package tech.sbdevelopment.mapreflectionapi.api; + +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +public interface IMapController { + /** + * Add a viewer + * + * @param player {@link Player} to add + */ + void addViewer(Player player) throws MapLimitExceededException; + + /** + * Remove a viewer + * + * @param player {@link OfflinePlayer} to remove + */ + void removeViewer(OfflinePlayer player); + + /** + * Remove all viewers + */ + void clearViewers(); + + /** + * Check if a player is viewing + * + * @param player {@link OfflinePlayer} to check + * @return true if the player is viewing + */ + boolean isViewing(OfflinePlayer player); + + /** + * Update the image + * + * @param content new {@link ArrayImage} content + */ + void update(@NotNull ArrayImage content); + + /** + * Send the content to a player + * + * @param player {@link Player} receiver of the content + */ + void sendContent(Player player); + + /** + * Send the content to a player + * + * @param player {@link Player} receiver of the content + * @param withoutQueue if true, the content will be sent immediately + */ + void sendContent(Player player, boolean withoutQueue); + + /** + * Cancels the 'send events' in the queue + */ + void cancelSend(); +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java index 9653e3f..2179cd8 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java @@ -27,36 +27,13 @@ import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; -public interface MapController { +public interface MapController extends IMapController { /** - * Add a viewer - * - * @param player {@link Player} to add + * @deprecated Please use {@link MapWrapper#getContent()} */ - void addViewer(Player player) throws MapLimitExceededException; - - /** - * Remove a viewer - * - * @param player {@link OfflinePlayer} to remove - */ - void removeViewer(OfflinePlayer player); - - /** - * Remove all viewers - */ - void clearViewers(); - - /** - * Check if a player is viewing - * - * @param player {@link OfflinePlayer} to check - * @return true if the player is viewing - */ - boolean isViewing(OfflinePlayer player); + @Deprecated(since = "1.3", forRemoval = true) + ArrayImage getContent(); /** * Get the map ID for a player @@ -66,40 +43,6 @@ public interface MapController { */ int getMapId(OfflinePlayer player); - /** - * Update the image - * - * @param content new {@link ArrayImage} content - */ - void update(@NotNull ArrayImage content); - - /** - * Get the content of the controller - * - * @return The {@link ArrayImage} - */ - ArrayImage getContent(); - - /** - * Send the content to a player - * - * @param player {@link Player} receiver of the content - */ - void sendContent(Player player); - - /** - * Send the content to a player - * - * @param player {@link Player} receiver of the content - * @param withoutQueue if true, the content will be sent immediately - */ - void sendContent(Player player, boolean withoutQueue); - - /** - * Cancels the 'send events' in the queue - */ - void cancelSend(); - /** * Show in a player's inventory * diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java index 2da997a..bb1bdf9 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java @@ -37,7 +37,16 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MapManager { protected final Set occupiedIds = new HashSet<>(); - private final List managedMaps = new CopyOnWriteArrayList<>(); + protected final List managedMaps = new CopyOnWriteArrayList<>(); + + /** + * Get the amount of maps managed by the plugin + * + * @return The managed maps amount + */ + public int getManagedMapsCount() { + return managedMaps.size(); + } /** * Wrap a {@link BufferedImage} in a {@link MapWrapper} @@ -64,6 +73,52 @@ public class MapManager { return wrapNewImage(image); } + /** + * Wrap a {@link BufferedImage} and split it into multiple maps + * + * @param image The image to wrap + * @param rows Rows of the split (i.e. height) + * @param columns Columns of the split (i.e. width) + * @return The wrapper + */ + public MultiMapWrapper wrapMultiImage(BufferedImage image, int rows, int columns) { + //Don't add to managedMaps, because the MultiMapWrapper will do that for us + return new MultiMapWrapper(image, rows, columns); + } + + /** + * Wrap an {@link ArrayImage} and split it into multiple maps + * + * @param image The image to wrap + * @param rows Rows of the split (i.e. height) + * @param columns Columns of the split (i.e. width) + * @return The wrapper + */ + public MultiMapWrapper wrapMultiImage(ArrayImage image, int rows, int columns) { + //Don't add to managedMaps, because the MultiMapWrapper will do that for us + return new MultiMapWrapper(image, rows, columns); + } + + /** + * Wrap multiple {@link BufferedImage}s + * + * @param images The images to wrap + * @return The wrapper + */ + public MultiMapWrapper wrapMultiImage(BufferedImage[][] images) { + return new MultiMapWrapper(images); + } + + /** + * Wrap multiple {@link ArrayImage}s + * + * @param images The images to wrap + * @return The wrapper + */ + public MultiMapWrapper wrapMultiImage(ArrayImage[][] images) { + return new MultiMapWrapper(images); + } + /** * Wrap a new image * @@ -82,8 +137,7 @@ public class MapManager { * @param wrapper The {@link MapWrapper} to unwrap */ public void unwrapImage(MapWrapper wrapper) { - wrapper.controller.cancelSend(); - wrapper.getController().clearViewers(); + wrapper.unwrap(); managedMaps.remove(wrapper); } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index a812e93..6b851a8 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -38,7 +38,7 @@ import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import java.util.*; -public class MapWrapper { +public class MapWrapper extends AbstractMapWrapper { private static final String REFERENCE_METADATA = "MAP_WRAPPER_REF"; protected ArrayImage content; @@ -59,7 +59,6 @@ public class MapWrapper { private static final Class craftStackClass = ReflectionUtil.getCraftClass("inventory.CraftItemStack"); private static final Class setSlotPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutSetSlot"); - //private static final Class tagCompoundClass = ReflectionUtil.getNMSClass("nbt", "NBTTagCompound"); private static final Class entityClass = ReflectionUtil.getNMSClass("world.entity", "Entity"); private static final Class dataWatcherClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher"); private static final Class entityMetadataPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata"); @@ -176,7 +175,6 @@ public class MapWrapper { inventoryMenuName = "defaultContainer"; } Object inventoryMenu = ReflectionUtil.getField(ReflectionUtil.getHandle(player), inventoryMenuName); -// int windowId = (int) ReflectionUtil.getField(inventoryMenu, ReflectionUtil.supports(17) ? "j" : "windowId"); ItemStack stack; if (ReflectionUtil.supports(13)) { @@ -353,20 +351,11 @@ public class MapWrapper { } }; - /** - * Get the content that is wrapped - * - * @return The {@link ArrayImage} - */ public ArrayImage getContent() { return content; } - /** - * Get the controller of this wrapper - * - * @return The {@link MapController} - */ + @Override public MapController getController() { return controller; } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java new file mode 100644 index 0000000..a30e166 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java @@ -0,0 +1,153 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package tech.sbdevelopment.mapreflectionapi.api; + +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +public interface MultiMapController extends IMapController { + /** + * Add a viewer + * + * @param player {@link Player} to add + */ + void addViewer(Player player) throws MapLimitExceededException; + + /** + * Remove a viewer + * + * @param player {@link OfflinePlayer} to remove + */ + void removeViewer(OfflinePlayer player); + + /** + * Remove all viewers + */ + void clearViewers(); + + /** + * Check if a player is viewing + * + * @param player {@link OfflinePlayer} to check + * @return true if the player is viewing + */ + boolean isViewing(OfflinePlayer player); + + /** + * Update the image + * + * @param content new {@link ArrayImage} content + */ + void update(@NotNull ArrayImage content); + + /** + * Send the content to a player + * + * @param player {@link Player} receiver of the content + */ + void sendContent(Player player); + + /** + * Send the content to a player + * + * @param player {@link Player} receiver of the content + * @param withoutQueue if true, the content will be sent immediately + */ + void sendContent(Player player, boolean withoutQueue); + + /** + * Cancels the 'send events' in the queue + */ + void cancelSend(); + + /** + * Show this {@link MultiMapController} in {@link ItemFrame}s + * + * @param player {@link Player} that will be able to see the maps + * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (int[width][height]) + * @see MapController#showInFrame(Player, int) + */ + void showInFrames(Player player, int[][] entityIdMatrix); + + /** + * Show this {@link MultiMapController} in {@link ItemFrame}s + * + * @param player {@link Player} that will be able to see the maps + * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (int[width][height]) + * @param callable {@link DebugCallable} which will be called to display debug information, or null + * @see MapController#showInFrame(Player, int, String) + */ + void showInFrames(Player player, int[][] entityIdMatrix, DebugCallable callable); + + /** + * Show this {@link MultiMapController} in {@link ItemFrame}s + * + * @param player {@link Player} that will be able to see the maps + * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (ItemFrame[width][height]) + * @param force if false, the map will not be shown if there is not Map-Item in the ItemFrames + * @see MapController#showInFrame(Player, ItemFrame, boolean) + */ + void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force); + + /** + * Show this {@link MultiMapController} in {@link ItemFrame}s + * + * @param player {@link Player} that will be able to see the maps + * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (ItemFrame[width][height]) + * @see MapController#showInFrame(Player, ItemFrame) + */ + void showInFrames(Player player, ItemFrame[][] itemFrameMatrix); + + /** + * Clear the frames + * + * @param player {@link Player} that will be able to see the cleared frames + * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (int[width][height]) + */ + void clearFrames(Player player, int[][] entityIdMatrix); + + /** + * Clear the frames + * + * @param player {@link Player} that will be able to see the cleared frames + * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (ItemFrame[width][height]) + */ + void clearFrames(Player player, ItemFrame[][] itemFrameMatrix); + + interface DebugCallable { + /** + * Called to get debug information for a frame + * + * @param controller the {@link MapController} + * @param x X-Position of the current frame + * @param y Y-Position of the current frame + * @return {@link String} to show when a player looks at the map, or null + * @see MapController#showInFrame(Player, int, String) + */ + String call(MapController controller, int x, int y); + } +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapWrapper.java new file mode 100644 index 0000000..e83e732 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapWrapper.java @@ -0,0 +1,229 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package tech.sbdevelopment.mapreflectionapi.api; + +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class MultiMapWrapper extends AbstractMapWrapper { + private final MapWrapper[][] wrapperMatrix; + + public MultiMapWrapper(BufferedImage image, int rows, int columns) { + this(splitImage(image, columns, rows)); + } + + public MultiMapWrapper(ArrayImage image, int rows, int columns) { + this(splitImage(image.toBuffered(), columns, rows)); + } + + public MultiMapWrapper(ArrayImage[][] imageMatrix) { + wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length]; + + for (int x = 0; x < imageMatrix.length; x++) { + if (imageMatrix[x].length != imageMatrix[0].length) { + throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!"); + } + + for (int y = 0; y < imageMatrix[x].length; y++) { + wrapperMatrix[x][y] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[x][y]); + } + } + } + + public MultiMapWrapper(BufferedImage[][] imageMatrix) { + wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length]; + + for (int x = 0; x < imageMatrix.length; x++) { + if (imageMatrix[x].length != imageMatrix[0].length) { + throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!"); + } + + for (int y = 0; y < imageMatrix[x].length; y++) { + wrapperMatrix[x][y] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[x][y]); + } + } + } + + private final MultiMapController controller = new MultiMapController() { + private final Set viewers = new HashSet<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!viewers.contains(player.getUniqueId())) { + for (MapWrapper[] mapWrappers : wrapperMatrix) { + for (MapWrapper wrapper : mapWrappers) { + wrapper.getController().addViewer(player); + } + } + viewers.add(player.getUniqueId()); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + for (MapWrapper[] mapWrappers : wrapperMatrix) { + for (MapWrapper wrapper : mapWrappers) { + wrapper.getController().removeViewer(player); + } + } + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (MapWrapper[] mapWrappers : wrapperMatrix) { + for (MapWrapper wrapper : mapWrappers) { + wrapper.getController().clearViewers(); + } + } + viewers.clear(); + } + + @Override + public boolean isViewing(OfflinePlayer player) { + return viewers.contains(player.getUniqueId()); + } + + @Override + public void update(@NotNull ArrayImage content) { + ArrayImage[][] split = splitImage(content.toBuffered(), wrapperMatrix[0].length, wrapperMatrix.length); + for (int x = 0; x < wrapperMatrix.length; x++) { + for (int y = 0; y < wrapperMatrix[x].length; y++) { + wrapperMatrix[x][y].getController().update(split[x][y]); + } + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + for (MapWrapper[] mapWrappers : wrapperMatrix) { + for (MapWrapper wrapper : mapWrappers) { + wrapper.getController().sendContent(player, withoutQueue); + } + } + } + + @Override + public void cancelSend() { + for (MapWrapper[] mapWrappers : wrapperMatrix) { + for (MapWrapper wrapper : mapWrappers) { + wrapper.getController().cancelSend(); + } + } + } + + @Override + public void showInFrames(Player player, int[][] entityIdMatrix) { + for (int x = 0; x < entityIdMatrix.length; x++) { + for (int y = 0; y < entityIdMatrix[x].length; y++) { + wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y]); + } + } + } + + @Override + public void showInFrames(Player player, int[][] entityIdMatrix, DebugCallable callable) { + for (int x = 0; x < entityIdMatrix.length; x++) { + for (int y = 0; y < entityIdMatrix[x].length; y++) { + wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y], callable.call(wrapperMatrix[y][x].getController(), x, y)); + } + } + } + + @Override + public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force) { + for (int x = 0; x < itemFrameMatrix.length; x++) { + for (int y = 0; y < itemFrameMatrix[x].length; y++) { + wrapperMatrix[y][x].getController().showInFrame(player, itemFrameMatrix[x][wrapperMatrix.length - 1 - y], force); + } + } + } + + @Override + public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix) { + showInFrames(player, itemFrameMatrix, false); + } + + @Override + public void clearFrames(Player player, int[][] entityIdMatrix) { + for (int x = 0; x < entityIdMatrix.length; x++) { + for (int y = 0; y < entityIdMatrix[x].length; y++) { + wrapperMatrix[y][x].getController().clearFrame(player, entityIdMatrix[x][y]); + } + } + } + + @Override + public void clearFrames(Player player, ItemFrame[][] itemFrameMatrix) { + for (int x = 0; x < itemFrameMatrix.length; x++) { + for (int y = 0; y < itemFrameMatrix[x].length; y++) { + wrapperMatrix[y][x].getController().clearFrame(player, itemFrameMatrix[x][y]); + } + } + } + }; + + /* + * Modified Method from http://kalanir.blogspot.de/2010/02/how-to-split-image-into-chunks-java.html + */ + private static ArrayImage[][] splitImage(final BufferedImage image, final int columns, final int rows) { + int chunkWidth = image.getWidth() / columns; + int chunkHeight = image.getHeight() / rows; + + ArrayImage[][] images = new ArrayImage[rows][columns]; + for (int x = 0; x < rows; x++) { + for (int y = 0; y < columns; y++) { + BufferedImage raw = new BufferedImage(chunkWidth, chunkHeight, image.getType()); + + Graphics2D gr = raw.createGraphics(); + gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null); + gr.dispose(); + + images[x][y] = new ArrayImage(raw); + raw.flush(); + } + } + return images; + } + + @Override + public MultiMapController getController() { + return controller; + } +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java new file mode 100644 index 0000000..76762c5 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java @@ -0,0 +1,43 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package tech.sbdevelopment.mapreflectionapi.utils; + +public class MainUtil { + private MainUtil() { + } + + /** + * Gets whether this is a headless JDK that doesn't contain the Java AWT library + * + * @return True if java.awt is not available + */ + public static boolean isHeadlessJDK() { + try { + Class.forName("java.awt.Color"); + return false; + } catch (ClassNotFoundException ex) { + return true; + } + } +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java index 362240a..b461a78 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java @@ -74,6 +74,9 @@ public class UpdateManager { * @param plugin The plugin instance */ public UpdateManager(Plugin plugin, CheckType type) { + if ("%%__RESOURCE__%%".equals("%%__" + "RES" + "OU" + "RCE" + "__%%")) + throw new IllegalStateException("Resource ID not set!"); + this.plugin = plugin; this.currentVersion = new Version(plugin.getDescription().getVersion()); this.type = type; diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_12.bub b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_12.bub new file mode 100644 index 0000000..23ee24d Binary files /dev/null and b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_12.bub differ diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_16.bub b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_16.bub new file mode 100644 index 0000000..3d25c20 Binary files /dev/null and b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_16.bub differ diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_17.bub b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_17.bub new file mode 100644 index 0000000..63334a9 Binary files /dev/null and b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_17.bub differ diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_8_8.bub b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_8_8.bub new file mode 100644 index 0000000..1e22e3a Binary files /dev/null and b/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_8_8.bub differ diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5d3f2fc..9418e75 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,7 +5,7 @@ api-version: 1.13 authors: [ inventivetalent, SBDeveloper ] description: This API helps developer with viewing images on maps. website: https://sbdevelopment.tech -softdepend: [ BKCommonLib, ProtocolLib ] +softdepend: [ ProtocolLib ] commands: mapmanager: description: The main command of MapManager.