Merge pull request #7 from SBDPlugins/development

v1.3: Implemented MultiMapWrapper and removed BKCommonLib dependency
This commit is contained in:
Stijn Bannink 2022-08-05 14:18:40 +02:00 committed by GitHub
commit 2dc4338cdb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1709 additions and 121 deletions

View file

@ -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:

45
pom.xml
View file

@ -29,7 +29,7 @@
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI</artifactId>
<version>1.2</version>
<version>1.3</version>
<packaging>jar</packaging>
<name>MapReflectionAPI</name>
@ -77,6 +77,16 @@
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>com.bergerkiller.bukkit.common</pattern>
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bkcommonlib</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
@ -135,6 +145,9 @@
<release>11</release>
<sourcepath>${maven.lombok.delombok-target}</sourcepath>
<sourceFileExcludes>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/io/*.java</sourceFileExclude>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/map/*.java</sourceFileExclude>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/map/color/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/utils/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/listeners/*.java</sourceFileExclude>
@ -146,6 +159,15 @@
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>plugin.yml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>plugin.yml</exclude>
</excludes>
</resource>
</resources>
</build>
@ -172,12 +194,6 @@
<version>1.19.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.bergerkiller.bukkit</groupId>
<artifactId>BKCommonLib</artifactId>
<version>1.19-v2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
@ -190,5 +206,20 @@
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!-- Annotations provided by Spigot -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>23.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View file

@ -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.

View file

@ -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)

View file

@ -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();
}
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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<Bubble> 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) + "}";
}
}
}

View file

@ -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));
}
}
}
}
}

View file

@ -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:
* <ul>
* <li>SET_POSITION(23, 56)</li>
* <li>SET_DX(-1)</li>
* <li>SET_DY(1)</li>
* <li>MOVE DX AND DRAW</li>
* <li>MOVE DX AND DRAW</li>
* <li>MOVE DY AND DRAW</li>
* <li>MOVE DX AND DRAW</li>
* <li>MOVE DX AND DRAW</li>
* <li>MOVE DY AND DRAW</li>
* <li>etc.</li>
* </ul>
* <p>
* For encoding the data, the follow bits are written out in sequence:
* <ul>
* <li>00 -> MOVE DX AND DRAW</li>
* <li>01 -> MOVE DY AND DRAW</li>
* <li>10 -> MOVE DX+DY AND DRAW</li>
* <li>11 100 -> SET DX = -1</li>
* <li>11 101 -> SET DX = 1</li>
* <li>11 110 -> SET DY = -1</li>
* <li>11 111 -> SET DY = 1</li>
* <li>11 00 [byte_x][byte_y] -> SET POSITION AND DRAW</li>
* <li>11 01 -> STOP</li>
* </ul>
*/
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;
}
}

View file

@ -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);
}
}

View file

@ -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!");

View file

@ -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();
}
}

View file

@ -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 <code>true</code> 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 <code>true</code>, the content will be sent immediately
*/
void sendContent(Player player, boolean withoutQueue);
/**
* Cancels the 'send events' in the queue
*/
void cancelSend();
}

View file

@ -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 <code>true</code> 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 <code>true</code>, 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
*

View file

@ -37,7 +37,16 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class MapManager {
protected final Set<Integer> occupiedIds = new HashSet<>();
private final List<MapWrapper> managedMaps = new CopyOnWriteArrayList<>();
protected final List<MapWrapper> 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);
}

View file

@ -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;
}

View file

@ -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 <code>true</code> 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 <code>true</code>, 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 (<code>int[width][height]</code>)
* @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 (<code>int[width][height]</code>)
* @param callable {@link DebugCallable} which will be called to display debug information, or <code>null</code>
* @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 (<code>ItemFrame[width][height]</code>)
* @param force if <code>false</code>, 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 (<code>ItemFrame[width][height]</code>)
* @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 (<code>int[width][height]</code>)
*/
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 (<code>ItemFrame[width][height]</code>)
*/
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 <code>null</code>
* @see MapController#showInFrame(Player, int, String)
*/
String call(MapController controller, int x, int y);
}
}

View file

@ -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<UUID> 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;
}
}

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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.