Merge between master and legacy/nms
This commit is contained in:
parent
040a5014a4
commit
fd225647f5
89 changed files with 5775 additions and 874 deletions
56
API/src/main/java/com/bergerkiller/bukkit/common/LICENSE
Normal file
56
API/src/main/java/com/bergerkiller/bukkit/common/LICENSE
Normal 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.
|
|
@ -0,0 +1,8 @@
|
|||
# BKCommonLib
|
||||
|
||||
These classes are from [BKCommonLib](https://github.com/bergerhealer/BKCommonLib). Only the required classes and methods
|
||||
are extracted.
|
||||
|
||||
**Current version:** master#3fc97d5981742f43348cac1f752404f95daa6c07
|
||||
|
||||
_Last update: 16-03-2023_
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.function.IntPredicate;
|
||||
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 (MCSDBubbleFormat.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() {
|
||||
// As we'll be processing pretty much every element, allocate the full space (60MB)
|
||||
// The range of the buffer we process shrinks as we spread
|
||||
StrandBuffer buf;
|
||||
{
|
||||
final int[] buffer = new int[1 << 24];
|
||||
int count = -1;
|
||||
for (int z = 0; z < 256; z++) {
|
||||
boolean[] layerStrands = this.strands[z];
|
||||
int indexOffset = z << 16;
|
||||
for (int i = 0; i < (1 << 16); i++) {
|
||||
if (!layerStrands[i]) {
|
||||
buffer[++count] = indexOffset + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
count++;
|
||||
buf = new StrandBuffer(buffer, count);
|
||||
}
|
||||
|
||||
// Process all until no more changes remain
|
||||
buf.process(index -> {
|
||||
byte color;
|
||||
|
||||
boolean col = ((index & 0xFF) < 0xFF);
|
||||
boolean row = ((index & 0xFF00) < 0xFF00);
|
||||
|
||||
if (col && row) {
|
||||
if ((color = this.get(index)) != 0) {
|
||||
this.set(index + 1, color);
|
||||
this.set(index + 256, color);
|
||||
return true;
|
||||
} else if ((color = this.get(index + 1)) != 0) {
|
||||
this.set(index, color);
|
||||
this.set(index + 256, color);
|
||||
return true;
|
||||
} else if ((color = this.get(index + 256)) != 0) {
|
||||
this.set(index, color);
|
||||
this.set(index + 1, color);
|
||||
return true;
|
||||
}
|
||||
} else if (col) {
|
||||
if ((color = this.get(index)) != 0) {
|
||||
this.set(index + 1, color);
|
||||
return true;
|
||||
} else if ((color = this.get(index + 1)) != 0) {
|
||||
this.set(index, color);
|
||||
return true;
|
||||
}
|
||||
} else if (row) {
|
||||
if ((color = this.get(index)) != 0) {
|
||||
this.set(index + 256, color);
|
||||
return true;
|
||||
} else if ((color = this.get(index + 256)) != 0) {
|
||||
this.set(index, color);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private static class StrandBuffer {
|
||||
private final int[] buf;
|
||||
private int start, end;
|
||||
|
||||
public StrandBuffer(int[] buffer, int count) {
|
||||
this.buf = buffer;
|
||||
this.start = 0;
|
||||
this.end = count - 1;
|
||||
}
|
||||
|
||||
public void process(IntPredicate strandIndexProc) {
|
||||
while (forward(strandIndexProc) && reverse(strandIndexProc)) {
|
||||
// Process alternating over and over until there are no more changes
|
||||
}
|
||||
}
|
||||
|
||||
public boolean forward(IntPredicate strandIndexProc) {
|
||||
int[] buf = this.buf;
|
||||
int writeIdx = start - 1;
|
||||
int endIdx = end;
|
||||
boolean changed = false;
|
||||
for (int i = start; i <= endIdx; ++i) {
|
||||
int strandIndex = buf[i];
|
||||
if (strandIndexProc.test(strandIndex)) {
|
||||
changed = true;
|
||||
} else {
|
||||
buf[++writeIdx] = strandIndex;
|
||||
}
|
||||
}
|
||||
this.end = writeIdx;
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean reverse(IntPredicate strandIndexProc) {
|
||||
int[] buf = this.buf;
|
||||
int writeIdx = end + 1;
|
||||
int startIdx = start;
|
||||
boolean changed = false;
|
||||
for (int i = end; i >= startIdx; --i) {
|
||||
int strandIndex = buf[i];
|
||||
if (strandIndexProc.test(strandIndex)) {
|
||||
changed = true;
|
||||
} else {
|
||||
buf[--writeIdx] = strandIndex;
|
||||
}
|
||||
}
|
||||
this.start = writeIdx;
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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) + "}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi;
|
||||
|
||||
import com.bergerkiller.bukkit.common.map.MapColorPalette;
|
||||
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;
|
||||
import tech.sbdevelopment.mapreflectionapi.api.MapManager;
|
||||
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;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class MapReflectionAPI extends JavaPlugin {
|
||||
private static MapReflectionAPI instance;
|
||||
private static MapManager mapManager;
|
||||
private static PacketListener packetListener;
|
||||
|
||||
/**
|
||||
* Get the plugin instance
|
||||
*
|
||||
* @return The {@link MapReflectionAPI} instance
|
||||
*/
|
||||
public static MapReflectionAPI getInstance() {
|
||||
if (instance == null) throw new IllegalStateException("The plugin is not enabled yet!");
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MapManager}
|
||||
*
|
||||
* @return The manager
|
||||
*/
|
||||
public static MapManager getMapManager() {
|
||||
if (mapManager == null) throw new IllegalStateException("The plugin is not enabled yet!");
|
||||
return mapManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
|
||||
getLogger().info("----------------");
|
||||
getLogger().info("MapReflectionAPI v" + getDescription().getVersion());
|
||||
getLogger().info("Made by © Copyright SBDevelopment 2023");
|
||||
|
||||
if (!ReflectionUtil.supports(12)) {
|
||||
getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.19.4!");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
getLogger().info("Loading the configuration...");
|
||||
Configuration.init(this);
|
||||
|
||||
getLogger().info("Loading the commands...");
|
||||
getCommand("mapmanager").setExecutor(new MapManagerCMD());
|
||||
|
||||
getLogger().info("Loading the packet listener...");
|
||||
try {
|
||||
packetListener = PacketListener.construct(this);
|
||||
} catch (IllegalStateException e) {
|
||||
getLogger().log(Level.SEVERE, e.getMessage(), e);
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
packetListener.init(this);
|
||||
|
||||
getLogger().info("Loading the map manager...");
|
||||
try {
|
||||
mapManager = new MapManager(this);
|
||||
} catch (IllegalStateException e) {
|
||||
getLogger().log(Level.SEVERE, e.getMessage(), e);
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Configuration.getInstance().isAllowVanilla()) {
|
||||
getLogger().info("Vanilla Maps are allowed. Discovering occupied Map IDs...");
|
||||
int occupiedIDs = 0;
|
||||
for (int s = 0; s < Short.MAX_VALUE; s++) {
|
||||
try {
|
||||
MapView view = Bukkit.getMap(s);
|
||||
if (view != null) {
|
||||
mapManager.registerOccupiedID(s);
|
||||
occupiedIDs++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (!e.getMessage().toLowerCase().contains("invalid map dimension")) {
|
||||
getLogger().log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
getLogger().info("Found " + occupiedIDs + " occupied Map IDs" + (occupiedIDs > 0 ? ", these will not be used." : "."));
|
||||
}
|
||||
|
||||
getLogger().info("Registering the listeners...");
|
||||
Bukkit.getPluginManager().registerEvents(new MapListener(), this);
|
||||
|
||||
getLogger().info("Loading metrics...");
|
||||
Metrics metrics = new Metrics(this, 16033);
|
||||
metrics.addCustomChart(new SingleLineChart("managed_maps", () -> mapManager.getManagedMapsCount()));
|
||||
|
||||
if (Configuration.getInstance().isUpdaterCheck()) {
|
||||
try {
|
||||
UpdateManager updateManager = new UpdateManager(this, 103011);
|
||||
|
||||
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();
|
||||
} catch (IllegalStateException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
getLogger().info("MapReflectionAPI is enabled!");
|
||||
getLogger().info("----------------");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().info("Disabling the packet handler...");
|
||||
if (packetListener != null) Bukkit.getOnlinePlayers().forEach(p -> packetListener.removePlayer(p));
|
||||
|
||||
getLogger().info("MapReflectionAPI is disabled!");
|
||||
|
||||
instance = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api;
|
||||
|
||||
/**
|
||||
* The abstract class of a wrapped map.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api;
|
||||
|
||||
import com.bergerkiller.bukkit.common.map.MapColorPalette;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* An {@link ArrayImage} contains an image converted to a Minecraft byte array.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public class ArrayImage {
|
||||
public final byte[] array;
|
||||
public int minX = 0;
|
||||
public int minY = 0;
|
||||
public int maxX = 128;
|
||||
public int maxY = 128;
|
||||
private int width;
|
||||
private int height;
|
||||
private int imageType = BufferedImage.TYPE_4BYTE_ABGR;
|
||||
|
||||
/**
|
||||
* Convert a {@link BufferedImage} to an ArrayImage
|
||||
*
|
||||
* @param image image to convert
|
||||
*/
|
||||
public ArrayImage(BufferedImage image) {
|
||||
this.imageType = image.getType();
|
||||
|
||||
this.width = image.getWidth();
|
||||
this.height = image.getHeight();
|
||||
|
||||
BufferedImage temp = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics = temp.createGraphics();
|
||||
graphics.drawImage(image, 0, 0, null);
|
||||
graphics.dispose();
|
||||
|
||||
int[] pixels = new int[temp.getWidth() * temp.getHeight()];
|
||||
temp.getRGB(0, 0, temp.getWidth(), temp.getHeight(), pixels, 0, temp.getWidth());
|
||||
|
||||
byte[] result = new byte[temp.getWidth() * temp.getHeight()];
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
result[i] = MapColorPalette.getColor(new Color(pixels[i], true));
|
||||
}
|
||||
|
||||
this.array = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link BufferedImage} of this ArrayImage
|
||||
*
|
||||
* @return The converted image
|
||||
*/
|
||||
public BufferedImage toBuffered() {
|
||||
BufferedImage img = new BufferedImage(width, height, this.imageType);
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
img.setRGB(x, y, MapColorPalette.getRealColor(array[y * width + x]).getRGB());
|
||||
}
|
||||
}
|
||||
return img;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* This interface contains the methods for both the {@link MapController} and the {@link MultiMapController}.
|
||||
*/
|
||||
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();
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api;
|
||||
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* A {@link MapController} controls one {@link MapWrapper}.
|
||||
*/
|
||||
public interface MapController extends IMapController {
|
||||
/**
|
||||
* Get the map ID for a player
|
||||
*
|
||||
* @param player {@link OfflinePlayer} to get the ID for
|
||||
* @return the ID, or <code>-1</code> if no ID exists (i.e. the player is not viewing)
|
||||
*/
|
||||
int getMapId(OfflinePlayer player);
|
||||
|
||||
/**
|
||||
* Show in a player's inventory
|
||||
*
|
||||
* @param player {@link Player}
|
||||
* @param slot slot to show the map in
|
||||
* @param force if <code>false</code>, the map will not be shown if the player is in creative mode
|
||||
*/
|
||||
void showInInventory(Player player, int slot, boolean force);
|
||||
|
||||
/**
|
||||
* Show in a player's inventory
|
||||
*
|
||||
* @param player {@link Player}
|
||||
* @param slot slot to show the map in
|
||||
*/
|
||||
void showInInventory(Player player, int slot);
|
||||
|
||||
/**
|
||||
* Show in a player's hand
|
||||
*
|
||||
* @param player {@link Player}
|
||||
* @param force if <code>false</code>, the map will not be shown if the player is not holding a map, or is in creative mode
|
||||
* @see #showInFrame(Player, ItemFrame, boolean)
|
||||
*/
|
||||
void showInHand(Player player, boolean force);
|
||||
|
||||
/**
|
||||
* Show in a player's hand
|
||||
*
|
||||
* @param player {@link Player}
|
||||
*/
|
||||
void showInHand(Player player);
|
||||
|
||||
/**
|
||||
* Show in an {@link ItemFrame}
|
||||
*
|
||||
* @param player {@link Player} that will be able to see the map
|
||||
* @param frame {@link ItemFrame} to show the map in
|
||||
*/
|
||||
void showInFrame(Player player, ItemFrame frame);
|
||||
|
||||
/**
|
||||
* Show in an {@link ItemFrame}
|
||||
*
|
||||
* @param player {@link Player} that will be able to see the map
|
||||
* @param frame {@link ItemFrame} to show the map in
|
||||
* @param force if <code>false</code>, the map will not be shown if there is not Map-Item in the ItemFrame
|
||||
*/
|
||||
void showInFrame(Player player, ItemFrame frame, boolean force);
|
||||
|
||||
/**
|
||||
* Show in an {@link ItemFrame}
|
||||
*
|
||||
* @param player {@link Player} that will be able to see the map
|
||||
* @param entityId Entity-ID of the {@link ItemFrame} to show the map in
|
||||
*/
|
||||
void showInFrame(Player player, int entityId);
|
||||
|
||||
/**
|
||||
* Show in an {@link ItemFrame}
|
||||
*
|
||||
* @param player {@link Player} that will be able to see the map
|
||||
* @param entityId Entity-ID of the {@link ItemFrame} to show the map in
|
||||
* @param debugInfo {@link String} to show when a player looks at the map, or <code>null</code>
|
||||
*/
|
||||
void showInFrame(Player player, int entityId, String debugInfo);
|
||||
|
||||
/**
|
||||
* Clear a frame
|
||||
*
|
||||
* @param player {@link Player} that will be able to see the cleared frame
|
||||
* @param entityId Entity-ID of the {@link ItemFrame} to clear
|
||||
*/
|
||||
void clearFrame(Player player, int entityId);
|
||||
|
||||
/**
|
||||
* Clear a frame
|
||||
*
|
||||
* @param player {@link Player} that will be able to see the cleared frame
|
||||
* @param frame {@link ItemFrame} to clear
|
||||
*/
|
||||
void clearFrame(Player player, ItemFrame frame);
|
||||
|
||||
/**
|
||||
* Get an {@link ItemFrame} by its entity ID
|
||||
*
|
||||
* @param world The world the {@link ItemFrame} is in
|
||||
* @param entityId Entity-ID of the {@link ItemFrame}
|
||||
* @return The found {@link ItemFrame}, or <code>null</code>
|
||||
*/
|
||||
ItemFrame getItemFrameById(World world, int entityId);
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
||||
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The {@link MapManager} manages all the maps. It also contains functions for wrapping.
|
||||
*/
|
||||
public class MapManager {
|
||||
protected final Set<Integer> occupiedIds = new HashSet<>();
|
||||
protected final List<MapWrapper> managedMaps = new CopyOnWriteArrayList<>();
|
||||
private final Class<?> wrapperClass;
|
||||
|
||||
public MapManager(JavaPlugin plugin) throws IllegalStateException {
|
||||
String packageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
|
||||
MapReflectionAPI.getInstance().getLogger().info("Initializing the map manager for Minecraft version " + version + "...");
|
||||
|
||||
try {
|
||||
final Class<?> clazz = Class.forName("tech.sbdevelopment.mapreflectionapi.nms.MapWrapper_" + version);
|
||||
if (MapWrapper.class.isAssignableFrom(clazz)) {
|
||||
wrapperClass = clazz;
|
||||
} else {
|
||||
throw new IllegalStateException("Plugin corrupted! Detected invalid MapWrapper class.");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException("This Spigot version (" + version + ") is not supported! Contact the developer to get support.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*
|
||||
* @param image The image to wrap
|
||||
* @return The wrapper
|
||||
*/
|
||||
public MapWrapper wrapImage(BufferedImage image) {
|
||||
return wrapImage(new ArrayImage(image));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a {@link ArrayImage} in a {@link MapWrapper}
|
||||
*
|
||||
* @param image The image to wrap
|
||||
* @return The wrapper
|
||||
*/
|
||||
public MapWrapper wrapImage(ArrayImage image) {
|
||||
if (Configuration.getInstance().isImageCache()) {
|
||||
for (MapWrapper wrapper : managedMaps) {
|
||||
if (wrapper.getContent().equals(image)) return wrapper;
|
||||
}
|
||||
}
|
||||
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
|
||||
*
|
||||
* @param image The image to wrap
|
||||
* @return The wrapper
|
||||
*/
|
||||
private MapWrapper wrapNewImage(ArrayImage image) {
|
||||
try {
|
||||
MapWrapper wrapper = (MapWrapper) wrapperClass.getDeclaredConstructor(ArrayImage.class).newInstance(image);
|
||||
managedMaps.add(wrapper);
|
||||
return wrapper;
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap an image (will remove the wrapper)
|
||||
*
|
||||
* @param wrapper The {@link MapWrapper} to unwrap
|
||||
*/
|
||||
public void unwrapImage(MapWrapper wrapper) {
|
||||
wrapper.unwrap();
|
||||
managedMaps.remove(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maps a player can see
|
||||
*
|
||||
* @param player The {@link Player} to check for
|
||||
* @return A {@link Set} with the {@link MapWrapper}s
|
||||
*/
|
||||
public Set<MapWrapper> getMapsVisibleTo(OfflinePlayer player) {
|
||||
Set<MapWrapper> visible = new HashSet<>();
|
||||
for (MapWrapper wrapper : managedMaps) {
|
||||
if (wrapper.getController().isViewing(player)) {
|
||||
visible.add(wrapper);
|
||||
}
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapper by a player and map id
|
||||
*
|
||||
* @param player The {@link OfflinePlayer} to check for
|
||||
* @param id The ID of the map
|
||||
* @return The {@link MapWrapper} for that map or null
|
||||
*/
|
||||
@Nullable
|
||||
public MapWrapper getWrapperForId(OfflinePlayer player, int id) {
|
||||
for (MapWrapper wrapper : getMapsVisibleTo(player)) {
|
||||
if (wrapper.getController().getMapId(player) == id) {
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an occupied map ID
|
||||
*
|
||||
* @param id The map ID to register
|
||||
*/
|
||||
public void registerOccupiedID(int id) {
|
||||
occupiedIds.add(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an occupied map ID
|
||||
*
|
||||
* @param id The map ID to unregister
|
||||
*/
|
||||
public void unregisterOccupiedID(int id) {
|
||||
occupiedIds.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the occupied IDs for a player
|
||||
*
|
||||
* @param player The {@link OfflinePlayer} to check for
|
||||
* @return A {@link Set} with the found map IDs
|
||||
*/
|
||||
public Set<Integer> getOccupiedIdsFor(OfflinePlayer player) {
|
||||
Set<Integer> ids = new HashSet<>();
|
||||
for (MapWrapper wrapper : managedMaps) {
|
||||
int s = wrapper.getController().getMapId(player);
|
||||
if (s >= 0) {
|
||||
ids.add(s);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player uses a map ID
|
||||
*
|
||||
* @param player The {@link OfflinePlayer} to check for
|
||||
* @param id The map ID to check for
|
||||
* @return true/false
|
||||
*/
|
||||
public boolean isIdUsedBy(OfflinePlayer player, int id) {
|
||||
return id > 0 && getOccupiedIdsFor(player).contains(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next ID that can be used for this player
|
||||
*
|
||||
* @param player The {@link Player} to check for
|
||||
* @return The next ID
|
||||
* @throws MapLimitExceededException If no IDs are available
|
||||
*/
|
||||
public int getNextFreeIdFor(Player player) throws MapLimitExceededException {
|
||||
Set<Integer> occupied = getOccupiedIdsFor(player);
|
||||
//Add the 'default' occupied IDs
|
||||
occupied.addAll(occupiedIds);
|
||||
|
||||
int largest = 0;
|
||||
for (Integer s : occupied) {
|
||||
if (s > largest) {
|
||||
largest = s;
|
||||
}
|
||||
}
|
||||
|
||||
//Simply increase the maximum id if it's still small enough
|
||||
if (largest + 1 < Integer.MAX_VALUE) {
|
||||
return largest + 1;
|
||||
}
|
||||
|
||||
//Otherwise iterate through all options until there is an unused id
|
||||
for (int s = 0; s < Integer.MAX_VALUE; s++) {
|
||||
if (!occupied.contains(s)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
//If we end up here, this player has no more free ids. Let's hope nobody uses this many Maps.
|
||||
throw new MapLimitExceededException("'" + player + "' reached the maximum amount of available Map-IDs");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all the maps of a player
|
||||
* This makes them no longer viewable for this player
|
||||
*
|
||||
* @param player The {@link OfflinePlayer} to clear for
|
||||
*/
|
||||
public void clearAllMapsFor(OfflinePlayer player) {
|
||||
for (MapWrapper wrapper : getMapsVisibleTo(player)) {
|
||||
wrapper.getController().removeViewer(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a MapWrapper exists for this image
|
||||
* If so, the same MapWrapper can be used
|
||||
*
|
||||
* @param image The {@link ArrayImage} to check for
|
||||
* @return A {@link MapWrapper} if duplicate, or null if not
|
||||
*/
|
||||
@Nullable
|
||||
public MapWrapper getDuplicate(ArrayImage image) {
|
||||
for (MapWrapper wrapper : managedMaps) {
|
||||
if (image.equals(wrapper.getContent())) {
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package tech.sbdevelopment.mapreflectionapi.api;
|
||||
|
||||
public abstract class MapWrapper extends AbstractMapWrapper {
|
||||
protected ArrayImage content;
|
||||
|
||||
public MapWrapper(ArrayImage image) {
|
||||
this.content = image;
|
||||
}
|
||||
|
||||
public ArrayImage getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract MapController getController();
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* A {@link MultiMapController} controls multiple {@link MapWrapper}s (a.k.a. the {@link MultiMapWrapper}).
|
||||
*/
|
||||
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, Integer[][] 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, Integer[][] 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, Integer[][] 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);
|
||||
|
||||
/**
|
||||
* The DebugCallable can be used to get debug information for a frame
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
import static tech.sbdevelopment.mapreflectionapi.utils.MainUtil.validateArrayDimensions;
|
||||
|
||||
/**
|
||||
* A {@link MultiMapWrapper} wraps one image split in pieces.
|
||||
*/
|
||||
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, Integer[][] entityIdMatrix) {
|
||||
validateArrayDimensions(wrapperMatrix, 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, Integer[][] entityIdMatrix, DebugCallable callable) {
|
||||
validateArrayDimensions(wrapperMatrix, 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], callable.call(wrapperMatrix[y][x].getController(), x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force) {
|
||||
validateArrayDimensions(wrapperMatrix, itemFrameMatrix);
|
||||
|
||||
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, Integer[][] entityIdMatrix) {
|
||||
validateArrayDimensions(wrapperMatrix, 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) {
|
||||
validateArrayDimensions(wrapperMatrix, 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
||||
|
||||
/**
|
||||
* This event gets fired when a map in the creative inventory gets updated
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class CreateInventoryMapUpdateEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlerList = new HandlerList();
|
||||
@Setter
|
||||
private boolean cancelled;
|
||||
|
||||
private final Player player;
|
||||
private final int slot;
|
||||
private final ItemStack item;
|
||||
private MapWrapper mapWrapper;
|
||||
|
||||
/**
|
||||
* Construct a new {@link CreateInventoryMapUpdateEvent}
|
||||
*
|
||||
* @param player The player whose inventory is updated
|
||||
* @param slot The new slot
|
||||
* @param item The item in the new slot
|
||||
* @param isAsync Is this event called async?
|
||||
*/
|
||||
public CreateInventoryMapUpdateEvent(Player player, int slot, ItemStack item, boolean isAsync) {
|
||||
super(isAsync);
|
||||
this.player = player;
|
||||
this.slot = slot;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MapWrapper} of the map of this event
|
||||
*
|
||||
* @return The {@link MapWrapper}
|
||||
*/
|
||||
@Nullable
|
||||
public MapWrapper getMapWrapper() {
|
||||
if (mapWrapper == null) {
|
||||
if (item == null) return null;
|
||||
if (item.getType() != Material.MAP) return null;
|
||||
mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, item.getDurability());
|
||||
}
|
||||
|
||||
return mapWrapper;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event gets fired when a map creation is cancelled
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class MapCancelEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlerList = new HandlerList();
|
||||
@Setter
|
||||
private boolean cancelled;
|
||||
|
||||
private final Player player;
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* Construct a new {@link MapCancelEvent}
|
||||
*
|
||||
* @param player The player who tried to create the map
|
||||
* @param id The ID of the map
|
||||
* @param isAsync Is this event called async?
|
||||
*/
|
||||
public MapCancelEvent(Player player, int id, boolean isAsync) {
|
||||
super(isAsync);
|
||||
this.player = player;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlerList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
||||
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
|
||||
|
||||
/**
|
||||
* This event gets fired when the content of a {@link MapWrapper} is updated
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class MapContentUpdateEvent extends Event {
|
||||
private static final HandlerList handlerList = new HandlerList();
|
||||
|
||||
private final MapWrapper wrapper;
|
||||
private final ArrayImage content;
|
||||
@Setter
|
||||
private boolean sendContent = true;
|
||||
|
||||
/**
|
||||
* Construct a new {@link MapContentUpdateEvent}
|
||||
*
|
||||
* @param wrapper The wrapper that will be updated
|
||||
* @param content The content that will be shown
|
||||
* @param isAsync Is this event called async?
|
||||
*/
|
||||
public MapContentUpdateEvent(MapWrapper wrapper, ArrayImage content, boolean isAsync) {
|
||||
super(isAsync);
|
||||
this.wrapper = wrapper;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlerList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
||||
|
||||
/**
|
||||
* This event gets fired when a player interact with a map
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class MapInteractEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlerList = new HandlerList();
|
||||
@Setter
|
||||
private boolean cancelled;
|
||||
|
||||
private final Player player;
|
||||
private final int entityID;
|
||||
private final int action;
|
||||
private final Vector vector;
|
||||
private final int hand;
|
||||
private ItemFrame frame;
|
||||
private MapWrapper mapWrapper;
|
||||
|
||||
/**
|
||||
* Construct a new {@link MapInteractEvent}
|
||||
*
|
||||
* @param player The player who interacted
|
||||
* @param entityID The ID of the entity the map is in
|
||||
* @param action The interact action
|
||||
* @param vector The location of the entity
|
||||
* @param hand The hand the player clicked with
|
||||
* @param isAsync Is this event called async?
|
||||
*/
|
||||
public MapInteractEvent(Player player, int entityID, int action, Vector vector, int hand, boolean isAsync) {
|
||||
super(isAsync);
|
||||
this.player = player;
|
||||
this.entityID = entityID;
|
||||
this.action = action;
|
||||
this.vector = vector;
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ItemFrame} the map is in
|
||||
*
|
||||
* @return The frame the map is in, or null if it's not a map
|
||||
*/
|
||||
@Nullable
|
||||
public ItemFrame getFrame() {
|
||||
if (getMapWrapper() == null) return null;
|
||||
|
||||
if (frame == null) {
|
||||
frame = getMapWrapper().getController().getItemFrameById(player.getWorld(), entityID);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MapWrapper} of the map
|
||||
*
|
||||
* @return The wrapper
|
||||
*/
|
||||
@Nullable
|
||||
public MapWrapper getMapWrapper() {
|
||||
if (mapWrapper == null) {
|
||||
mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, entityID);
|
||||
}
|
||||
|
||||
return mapWrapper;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Events package contains all the custom events the API fires.
|
||||
*/
|
||||
package tech.sbdevelopment.mapreflectionapi.api.events;
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.api.exceptions;
|
||||
|
||||
import lombok.experimental.StandardException;
|
||||
|
||||
/**
|
||||
* This exception gets thrown if no map IDs are available
|
||||
*/
|
||||
@StandardException
|
||||
public class MapLimitExceededException extends Exception {
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Exceptions package contains all the custom exceptions the API may throw.
|
||||
*/
|
||||
package tech.sbdevelopment.mapreflectionapi.api.exceptions;
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The API package contains all the API you can interact with.
|
||||
*/
|
||||
package tech.sbdevelopment.mapreflectionapi.api;
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.cmd;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.util.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MapManagerCMD implements TabExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("mapmanager.reload")) {
|
||||
sender.sendMessage(ChatColor.RED + "You don't have the permissions to use this command!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
|
||||
Configuration.getInstance().reload();
|
||||
|
||||
sender.sendMessage(ChatColor.GREEN + "The configuration has been reloaded!");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.GREEN + "Usage: " + ChatColor.WHITE + "/mapmanager reload");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) {
|
||||
if (args.length == 1) return StringUtil.copyPartialMatches(args[0], List.of("reload"), new ArrayList<>());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.listeners;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.server.MapInitializeEvent;
|
||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
||||
|
||||
public class MapListener implements Listener {
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent e) {
|
||||
MapReflectionAPI.getMapManager().clearAllMapsFor(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onMapInitialize(MapInitializeEvent e) {
|
||||
if (Configuration.getInstance().isAllowVanilla()) {
|
||||
int id = e.getMap().getId();
|
||||
if (id > 0) {
|
||||
MapReflectionAPI.getInstance().getLogger().info("Detected that the Map ID " + id + " got occupied. It will now not be used.");
|
||||
MapReflectionAPI.getMapManager().registerOccupiedID(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.listeners;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public abstract class PacketListener implements Listener {
|
||||
protected JavaPlugin plugin;
|
||||
|
||||
public static PacketListener construct(JavaPlugin plugin) throws IllegalStateException {
|
||||
String packageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
|
||||
plugin.getLogger().info("Initializing the packet handler for Minecraft version " + version + "...");
|
||||
|
||||
try {
|
||||
final Class<?> clazz = Class.forName("tech.sbdevelopment.mapreflectionapi.nms.PacketListener_" + version);
|
||||
if (PacketListener.class.isAssignableFrom(clazz)) {
|
||||
return (PacketListener) clazz.getDeclaredConstructor().newInstance();
|
||||
} else {
|
||||
throw new IllegalStateException("Plugin corrupted! Detected invalid PacketListener class.");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException("This Minecraft version (" + version + ") is not supported! Contact the developer to get support.");
|
||||
}
|
||||
}
|
||||
|
||||
public void init(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
injectPlayer(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent e) {
|
||||
removePlayer(e.getPlayer());
|
||||
}
|
||||
|
||||
protected abstract void injectPlayer(Player p);
|
||||
|
||||
public abstract void removePlayer(Player p);
|
||||
|
||||
protected abstract Vector vec3DToVector(Object vec3d);
|
||||
|
||||
protected boolean hasField(Object packet, String field) {
|
||||
try {
|
||||
packet.getClass().getDeclaredField(field);
|
||||
return true;
|
||||
} catch (NoSuchFieldException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.managers;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import tech.sbdevelopment.mapreflectionapi.utils.YamlFile;
|
||||
|
||||
public class Configuration {
|
||||
private static Configuration instance;
|
||||
private final YamlFile file;
|
||||
|
||||
@Getter
|
||||
private boolean allowVanilla = true;
|
||||
@Getter
|
||||
private boolean imageCache = true;
|
||||
@Getter
|
||||
private boolean updaterCheck = true;
|
||||
@Getter
|
||||
private boolean updaterDownload = true;
|
||||
|
||||
private Configuration(JavaPlugin plugin) {
|
||||
this.file = new YamlFile(plugin, "config");
|
||||
reload();
|
||||
}
|
||||
|
||||
public static void init(JavaPlugin plugin) {
|
||||
instance = new Configuration(plugin);
|
||||
}
|
||||
|
||||
public static Configuration getInstance() {
|
||||
if (instance == null) throw new IllegalStateException("The plugin is not enabled yet!");
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
allowVanilla = this.file.getFile().getBoolean("allowVanilla");
|
||||
imageCache = this.file.getFile().getBoolean("imageCache");
|
||||
updaterCheck = this.file.getFile().getBoolean("updater.check");
|
||||
updaterDownload = this.file.getFile().getBoolean("updater.download");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public static <A, B> void validateArrayDimensions(A[][] arrayOne, B[][] arrayTwo) {
|
||||
if (arrayOne.length != arrayTwo.length || arrayOne[0].length != arrayTwo[0].length) {
|
||||
throw new IllegalArgumentException("The dimensions of two provided arrays (" + arrayOne.getClass().getName() + ", " + arrayTwo.getClass().getName() + ") do not match!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,537 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.utils;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* <b>ReflectionUtil</b> - Reflection handler for NMS and CraftBukkit.<br>
|
||||
* Caches the packet related methods and is asynchronous.
|
||||
* <p>
|
||||
* This class does not handle null checks as most of the requests are from the
|
||||
* other utility classes that already handle null checks.
|
||||
* <p>
|
||||
* <a href="https://wiki.vg/Protocol">Clientbound Packets</a> are considered fake
|
||||
* updates to the client without changing the actual data. Since all the data is handled
|
||||
* by the server.
|
||||
*
|
||||
* @author Crypto Morin, Stijn Bannink
|
||||
* @version 2.1
|
||||
*/
|
||||
public class ReflectionUtil {
|
||||
/**
|
||||
* We use reflection mainly to avoid writing a new class for version barrier.
|
||||
* The version barrier is for NMS that uses the Minecraft version as the main package name.
|
||||
* <p>
|
||||
* E.g. EntityPlayer in 1.15 is in the class {@code net.minecraft.server.v1_15_R1}
|
||||
* but in 1.14 it's in {@code net.minecraft.server.v1_14_R1}
|
||||
* In order to maintain cross-version compatibility we cannot import these classes.
|
||||
* <p>
|
||||
* Performance is not a concern for these specific statically initialized values.
|
||||
*/
|
||||
public static final String VERSION;
|
||||
|
||||
static { // This needs to be right below VERSION because of initialization order.
|
||||
// This package loop is used to avoid implementation-dependant strings like Bukkit.getVersion() or Bukkit.getBukkitVersion()
|
||||
// which allows easier testing as well.
|
||||
String found = null;
|
||||
for (Package pack : Package.getPackages()) {
|
||||
String name = pack.getName();
|
||||
|
||||
// .v because there are other packages.
|
||||
if (name.startsWith("org.bukkit.craftbukkit.v")) {
|
||||
found = pack.getName().split("\\.")[3];
|
||||
|
||||
// Just a final guard to make sure it finds this important class.
|
||||
// As a protection for forge+bukkit implementation that tend to mix versions.
|
||||
// The real CraftPlayer should exist in the package.
|
||||
// Note: Doesn't seem to function properly. Will need to separate the version
|
||||
// handler for NMS and CraftBukkit for softwares like catmc.
|
||||
try {
|
||||
Class.forName("org.bukkit.craftbukkit." + found + ".entity.CraftPlayer");
|
||||
break;
|
||||
} catch (ClassNotFoundException e) {
|
||||
found = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found == null)
|
||||
throw new IllegalArgumentException("Failed to parse server version. Could not find any package starting with name: 'org.bukkit.craftbukkit.v'");
|
||||
VERSION = found;
|
||||
}
|
||||
|
||||
/**
|
||||
* The raw minor version number.
|
||||
* E.g. {@code v1_17_R1} to {@code 17}
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public static final int VER = Integer.parseInt(VERSION.substring(1).split("_")[1]);
|
||||
/**
|
||||
* The raw minor version number.
|
||||
* E.g. {@code v1_18_R2} to {@code 2}
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public static final int VER_MINOR = toInt(VERSION.substring(1).split("_")[2].substring(1), 0);
|
||||
/**
|
||||
* Mojang remapped their NMS in 1.17 https://www.spigotmc.org/threads/spigot-bungeecord-1-17.510208/#post-4184317
|
||||
*/
|
||||
public static final String
|
||||
CRAFTBUKKIT = "org.bukkit.craftbukkit." + VERSION + '.',
|
||||
NMS = v(17, "net.minecraft.").orElse("net.minecraft.server." + VERSION + '.');
|
||||
/**
|
||||
* A nullable public accessible field only available in {@code EntityPlayer}.
|
||||
* This can be null if the player is offline.
|
||||
*/
|
||||
private static final MethodHandle PLAYER_CONNECTION;
|
||||
/**
|
||||
* Responsible for getting the NMS handler {@code EntityPlayer} object for the player.
|
||||
* {@code CraftPlayer} is simply a wrapper for {@code EntityPlayer}.
|
||||
* Used mainly for handling packet related operations.
|
||||
* <p>
|
||||
* This is also where the famous player {@code ping} field comes from!
|
||||
*/
|
||||
private static final MethodHandle GET_HANDLE;
|
||||
private static final MethodHandle GET_HANDLE_WORLD;
|
||||
/**
|
||||
* Sends a packet to the player's client through a {@code NetworkManager} which
|
||||
* is where {@code ProtocolLib} controls packets by injecting channels!
|
||||
*/
|
||||
private static final MethodHandle SEND_PACKET;
|
||||
|
||||
static {
|
||||
Class<?> entityPlayer = getNMSClass("server.level", "EntityPlayer");
|
||||
Class<?> worldServer = getNMSClass("server.level", "WorldServer");
|
||||
Class<?> craftPlayer = getCraftClass("entity.CraftPlayer");
|
||||
Class<?> craftWorld = getCraftClass("CraftWorld");
|
||||
Class<?> playerConnection = getNMSClass("server.network", "PlayerConnection");
|
||||
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
MethodHandle sendPacket = null;
|
||||
MethodHandle getHandle = null;
|
||||
MethodHandle getHandleWorld = null;
|
||||
MethodHandle connection = null;
|
||||
|
||||
try {
|
||||
connection = lookup.findGetter(entityPlayer,
|
||||
supports(20) ? "c" : supports(17) ? "b" : "playerConnection", playerConnection);
|
||||
getHandle = lookup.findVirtual(craftPlayer, "getHandle", MethodType.methodType(entityPlayer));
|
||||
getHandleWorld = lookup.findVirtual(craftWorld, "getHandle", MethodType.methodType(worldServer));
|
||||
sendPacket = lookup.findVirtual(playerConnection,
|
||||
v(18, "a").orElse("sendPacket"),
|
||||
MethodType.methodType(void.class, getNMSClass("network.protocol", "Packet")));
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
PLAYER_CONNECTION = connection;
|
||||
SEND_PACKET = sendPacket;
|
||||
GET_HANDLE = getHandle;
|
||||
GET_HANDLE_WORLD = getHandleWorld;
|
||||
}
|
||||
|
||||
private ReflectionUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is purely for readability.
|
||||
* No performance is gained.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public static <T> VersionHandler<T> v(int version, T handle) {
|
||||
return new VersionHandler<>(version, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the server version is equal or greater than the given version.
|
||||
*
|
||||
* @param version the version to compare the server version with.
|
||||
* @return true if the version is equal or newer, otherwise false.
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public static boolean supports(int version) {
|
||||
return VER >= version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the server version is equal or greater than the given version.
|
||||
* <p>
|
||||
* PAY ATTENTION! The minor version is based on the NMS version.
|
||||
* This means that v1_19_R3 has major version 19 and minor version 3.
|
||||
*
|
||||
* @param major the major version to compare the server version with.
|
||||
* @param minor the minor version to compare the server version with.
|
||||
* @return true if the version is equal or newer, otherwise false.
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public static boolean supports(int major, int minor) {
|
||||
return VER >= major && VER_MINOR >= minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class converted to {@link List}
|
||||
*
|
||||
* @param <E> The storage type
|
||||
*/
|
||||
public static class ListParam<E> extends ArrayList<E> {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class converted to {@link Collection}
|
||||
*
|
||||
* @param <E> The storage type
|
||||
*/
|
||||
public static class CollectionParam<E> extends ArrayList<E> {
|
||||
|
||||
}
|
||||
|
||||
private static Class<?> wrapperToPrimitive(Class<?> clazz) {
|
||||
if (clazz == Boolean.class) return boolean.class;
|
||||
if (clazz == Integer.class) return int.class;
|
||||
if (clazz == Double.class) return double.class;
|
||||
if (clazz == Float.class) return float.class;
|
||||
if (clazz == Long.class) return long.class;
|
||||
if (clazz == Short.class) return short.class;
|
||||
if (clazz == Byte.class) return byte.class;
|
||||
if (clazz == Void.class) return void.class;
|
||||
if (clazz == Character.class) return char.class;
|
||||
if (clazz == CollectionParam.class) return Collection.class;
|
||||
if (clazz == ListParam.class) return List.class;
|
||||
if (clazz == ArrayList.class) return Collection.class; //LEGACY!
|
||||
if (clazz == HashMap.class) return Map.class;
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private static Class<?>[] toParamTypes(Object... params) {
|
||||
return Arrays.stream(params)
|
||||
.map(obj -> obj != null ? wrapperToPrimitive(obj.getClass()) : null)
|
||||
.toArray(Class<?>[]::new);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Class<?> getClass(@NotNull String name) {
|
||||
try {
|
||||
return Class.forName(name);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object callConstructorNull(Class<?> clazz, Class<?> paramClass) {
|
||||
try {
|
||||
Constructor<?> con = clazz.getConstructor(paramClass);
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(clazz.cast(null));
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||
InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object callFirstConstructor(Class<?> clazz, Object... params) {
|
||||
try {
|
||||
Constructor<?> con = clazz.getConstructors()[0];
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(params);
|
||||
} catch (IllegalAccessException | InstantiationException |
|
||||
InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object callConstructor(Class<?> clazz, Object... params) {
|
||||
try {
|
||||
Constructor<?> con = clazz.getConstructor(toParamTypes(params));
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(params);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||
InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object callDeclaredConstructor(Class<?> clazz, Object... params) {
|
||||
try {
|
||||
Constructor<?> con = clazz.getDeclaredConstructor(toParamTypes(params));
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(params);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||
InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object callMethod(Class<?> clazz, String method, Object... params) {
|
||||
try {
|
||||
Method m = clazz.getMethod(method, toParamTypes(params));
|
||||
m.setAccessible(true);
|
||||
return m.invoke(null, params);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object callMethod(Object obj, String method, Object... params) {
|
||||
try {
|
||||
Method m = obj.getClass().getMethod(method, toParamTypes(params));
|
||||
m.setAccessible(true);
|
||||
return m.invoke(obj, params);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object callDeclaredMethod(Object obj, String method, Object... params) {
|
||||
try {
|
||||
Method m = obj.getClass().getDeclaredMethod(method, toParamTypes(params));
|
||||
m.setAccessible(true);
|
||||
return m.invoke(obj, params);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasField(Object packet, String field) {
|
||||
try {
|
||||
packet.getClass().getDeclaredField(field);
|
||||
return true;
|
||||
} catch (NoSuchFieldException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object getField(Object object, String field) {
|
||||
try {
|
||||
Field f = object.getClass().getField(field);
|
||||
f.setAccessible(true);
|
||||
return f.get(object);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object getDeclaredField(Class<?> clazz, String field) {
|
||||
try {
|
||||
Field f = clazz.getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
return f.get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object getDeclaredField(Object object, String field) {
|
||||
try {
|
||||
Field f = object.getClass().getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
return f.get(object);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setDeclaredField(Object object, String field, Object value) {
|
||||
try {
|
||||
Field f = object.getClass().getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
f.set(object, value);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a NMS (net.minecraft.server) class which accepts a package for 1.17 compatibility.
|
||||
*
|
||||
* @param newPackage the 1.17 package name.
|
||||
* @param name the name of the class.
|
||||
* @return the NMS class or null if not found.
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@javax.annotation.Nullable
|
||||
public static Class<?> getNMSClass(@Nonnull String newPackage, @Nonnull String name) {
|
||||
if (supports(17)) name = newPackage + '.' + name;
|
||||
return getNMSClass(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a NMS (net.minecraft.server) class.
|
||||
*
|
||||
* @param name the name of the class.
|
||||
* @return the NMS class or null if not found.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@javax.annotation.Nullable
|
||||
public static Class<?> getNMSClass(@Nonnull String name) {
|
||||
try {
|
||||
return Class.forName(NMS + name);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to the player asynchronously if they're online.
|
||||
* Packets are thread-safe.
|
||||
*
|
||||
* @param player the player to send the packet to.
|
||||
* @param packets the packets to send.
|
||||
* @return the async thread handling the packet.
|
||||
* @see #sendPacketSync(Player, Object...)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public static CompletableFuture<Void> sendPacket(@Nonnull Player player, @Nonnull Object... packets) {
|
||||
return CompletableFuture.runAsync(() -> sendPacketSync(player, packets))
|
||||
.exceptionally(ex -> {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to the player synchronously if they're online.
|
||||
*
|
||||
* @param player the player to send the packet to.
|
||||
* @param packets the packets to send.
|
||||
* @see #sendPacket(Player, Object...)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public static void sendPacketSync(@Nonnull Player player, @Nonnull Object... packets) {
|
||||
try {
|
||||
Object handle = GET_HANDLE.invoke(player);
|
||||
Object connection = PLAYER_CONNECTION.invoke(handle);
|
||||
|
||||
// Checking if the connection is not null is enough. There is no need to check if the player is online.
|
||||
if (connection != null) {
|
||||
for (Object packet : packets) SEND_PACKET.invoke(connection, packet);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@javax.annotation.Nullable
|
||||
public static Object getHandle(@Nonnull Player player) {
|
||||
Objects.requireNonNull(player, "Cannot get handle of null player");
|
||||
try {
|
||||
return GET_HANDLE.invoke(player);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@javax.annotation.Nullable
|
||||
public static Object getHandle(@Nonnull World world) {
|
||||
Objects.requireNonNull(world, "Cannot get handle of null world");
|
||||
try {
|
||||
return GET_HANDLE_WORLD.invoke(world);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a CraftBukkit (org.bukkit.craftbukkit) class.
|
||||
*
|
||||
* @param name the name of the class to load.
|
||||
* @return the CraftBukkit class or null if not found.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@javax.annotation.Nullable
|
||||
public static Class<?> getCraftClass(@Nonnull String name) {
|
||||
try {
|
||||
return Class.forName(CRAFTBUKKIT + name);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class VersionHandler<T> {
|
||||
private int version;
|
||||
private T handle;
|
||||
|
||||
private VersionHandler(int version, T handle) {
|
||||
if (supports(version)) {
|
||||
this.version = version;
|
||||
this.handle = handle;
|
||||
}
|
||||
}
|
||||
|
||||
public VersionHandler<T> v(int version, T handle) {
|
||||
if (version == this.version)
|
||||
throw new IllegalArgumentException("Cannot have duplicate version handles for version: " + version);
|
||||
if (version > this.version && supports(version)) {
|
||||
this.version = version;
|
||||
this.handle = handle;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public T orElse(T handle) {
|
||||
return this.version == 0 ? handle : this.handle;
|
||||
}
|
||||
}
|
||||
|
||||
private static int toInt(String string, int def) {
|
||||
return string.isBlank() ? def : Integer.parseInt(string);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Update checker class
|
||||
*
|
||||
* @author Stijn [SBDeveloper]
|
||||
* @version 2.3 [27-09-2022] - Added Polymart support ; fixed Spigot support
|
||||
* @since 05-03-2020
|
||||
*/
|
||||
public class UpdateManager {
|
||||
private static final String SPIGOT_API = "https://api.spigotmc.org/legacy/update.php?resource=%d";
|
||||
private static final String SPIGOT_DOWNLOAD = "https://api.spiget.org/v2/resources/%s/download";
|
||||
|
||||
private static final String POLYMART_API = "https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=%d&key=version";
|
||||
private static final String POLYMART_DOWNLOAD = "https://api.polymart.org/v1/requestUpdateURL/?inject_version=%d&resource_id=%d&user_id=%d&nonce=%d&download_agent=%d&download_time=%d&download_token=%s";
|
||||
|
||||
private final Plugin plugin;
|
||||
private final Version currentVersion;
|
||||
private final CheckType type;
|
||||
|
||||
//Spigot & Polymart
|
||||
private final int resourceID;
|
||||
|
||||
//Polymart only
|
||||
private int injector_version;
|
||||
private int user_id;
|
||||
private int nonce;
|
||||
private int download_agent;
|
||||
private int download_time;
|
||||
private String download_token;
|
||||
|
||||
private BiConsumer<VersionResponse, Version> versionResponse;
|
||||
private BiConsumer<DownloadResponse, String> downloadResponse;
|
||||
|
||||
/**
|
||||
* Construct a new UpdateManager
|
||||
*
|
||||
* @param plugin The plugin instance
|
||||
*/
|
||||
public UpdateManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.currentVersion = new Version(plugin.getDescription().getVersion());
|
||||
this.type = CheckType.POLYMART_PAID;
|
||||
this.resourceID = Integer.parseInt("%%__RESOURCE__%%");
|
||||
this.injector_version = Integer.parseInt("%%__INJECT_VER__%%");
|
||||
this.user_id = Integer.parseInt("%%__USER__%%");
|
||||
this.nonce = Integer.parseInt("%%__NONCE__%%");
|
||||
this.download_agent = Integer.parseInt("%%__AGENT__%%");
|
||||
this.download_time = Integer.parseInt("%%__TIMESTAMP__%%");
|
||||
this.download_token = "%%__VERIFY_TOKEN__%%";
|
||||
}
|
||||
|
||||
public UpdateManager(Plugin plugin, int resourceID) {
|
||||
this.plugin = plugin;
|
||||
this.currentVersion = new Version(plugin.getDescription().getVersion());
|
||||
this.type = CheckType.SPIGOT;
|
||||
this.resourceID = resourceID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the response given by check();
|
||||
*
|
||||
* @param versionResponse The response
|
||||
* @return The updatemanager
|
||||
*/
|
||||
public UpdateManager handleResponse(BiConsumer<VersionResponse, Version> versionResponse) {
|
||||
this.versionResponse = versionResponse;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UpdateManager handleDownloadResponse(BiConsumer<DownloadResponse, String> downloadResponse) {
|
||||
this.downloadResponse = downloadResponse;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a new version
|
||||
*/
|
||||
public void check() {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
||||
try {
|
||||
HttpsURLConnection con;
|
||||
if (type == CheckType.POLYMART_PAID) {
|
||||
con = (HttpsURLConnection) new URL(String.format(POLYMART_API, this.resourceID)).openConnection();
|
||||
} else {
|
||||
con = (HttpsURLConnection) new URL(String.format(SPIGOT_API, this.resourceID)).openConnection();
|
||||
}
|
||||
con.setRequestMethod("GET");
|
||||
con.setRequestProperty("User-Agent", "SBDChecker/2.1");
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuilder response = new StringBuilder();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
}
|
||||
in.close();
|
||||
|
||||
Version onlineVersion = new Version(response.toString());
|
||||
|
||||
VersionResponse verRes = this.currentVersion.check(onlineVersion);
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.versionResponse.accept(verRes, onlineVersion));
|
||||
} catch (IOException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.versionResponse.accept(VersionResponse.UNAVAILABLE, null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void runUpdate() {
|
||||
File pluginFile = getPluginFile(); // /plugins/XXX.jar
|
||||
if (pluginFile == null) {
|
||||
this.downloadResponse.accept(DownloadResponse.ERROR, null);
|
||||
Bukkit.getLogger().info("Pluginfile is null");
|
||||
return;
|
||||
}
|
||||
File updateFolder = Bukkit.getUpdateFolderFile();
|
||||
if (!updateFolder.exists()) {
|
||||
if (!updateFolder.mkdirs()) {
|
||||
this.downloadResponse.accept(DownloadResponse.ERROR, null);
|
||||
Bukkit.getLogger().info("Updatefolder doesn't exists, and can't be made");
|
||||
return;
|
||||
}
|
||||
}
|
||||
final File updateFile = new File(updateFolder, pluginFile.getName());
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
||||
ReadableByteChannel channel;
|
||||
try {
|
||||
//https://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java
|
||||
HttpsURLConnection connection;
|
||||
if (type == CheckType.POLYMART_PAID) {
|
||||
connection = (HttpsURLConnection) new URL(String.format(POLYMART_DOWNLOAD, this.injector_version, this.resourceID, this.user_id, this.nonce, this.download_agent, this.download_time, this.download_token)).openConnection();
|
||||
} else {
|
||||
connection = (HttpsURLConnection) new URL(String.format(SPIGOT_DOWNLOAD, this.resourceID)).openConnection();
|
||||
}
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
|
||||
|
||||
InputStream stream = connection.getInputStream();
|
||||
if (connection.getResponseCode() != 200) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
|
||||
|
||||
String inputLine;
|
||||
StringBuilder responsestr = new StringBuilder();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
responsestr.append(inputLine);
|
||||
}
|
||||
in.close();
|
||||
|
||||
throw new RuntimeException("Download returned status #" + connection.getResponseCode(), new Throwable(responsestr.toString()));
|
||||
}
|
||||
|
||||
channel = Channels.newChannel(stream);
|
||||
} catch (IOException e) {
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.downloadResponse.accept(DownloadResponse.ERROR, null));
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
FileChannel fileChannel = null;
|
||||
try {
|
||||
FileOutputStream fosForDownloadedFile = new FileOutputStream(updateFile);
|
||||
fileChannel = fosForDownloadedFile.getChannel();
|
||||
|
||||
fileChannel.transferFrom(channel, 0, Long.MAX_VALUE);
|
||||
} catch (IOException e) {
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.downloadResponse.accept(DownloadResponse.ERROR, null));
|
||||
e.printStackTrace();
|
||||
return;
|
||||
} finally {
|
||||
if (channel != null) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Error while closing response body channel");
|
||||
}
|
||||
}
|
||||
|
||||
if (fileChannel != null) {
|
||||
try {
|
||||
fileChannel.close();
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Error while closing file channel for downloaded file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.downloadResponse.accept(DownloadResponse.DONE, updateFile.getPath()));
|
||||
});
|
||||
}
|
||||
|
||||
private File getPluginFile() {
|
||||
if (!(this.plugin instanceof JavaPlugin)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Method method = JavaPlugin.class.getDeclaredMethod("getFile");
|
||||
method.setAccessible(true);
|
||||
return (File) method.invoke(this.plugin);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Could not get plugin file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public enum CheckType {
|
||||
SPIGOT, POLYMART_PAID
|
||||
}
|
||||
|
||||
public enum VersionResponse {
|
||||
LATEST, //Latest version
|
||||
FOUND_NEW, //Newer available
|
||||
THIS_NEWER, //Local version is newer?
|
||||
UNAVAILABLE //Error
|
||||
}
|
||||
|
||||
public enum DownloadResponse {
|
||||
DONE, ERROR, UNAVAILABLE
|
||||
}
|
||||
|
||||
public static class Version {
|
||||
|
||||
private final String version;
|
||||
|
||||
public final String get() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
private Version(String version) {
|
||||
if (version == null)
|
||||
throw new IllegalArgumentException("Version can not be null");
|
||||
if (!version.matches("[0-9]+(\\.[0-9]+)*"))
|
||||
throw new IllegalArgumentException("Invalid version format");
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
private VersionResponse check(Version that) {
|
||||
String[] thisParts = this.get().split("\\.");
|
||||
String[] thatParts = that.get().split("\\.");
|
||||
|
||||
int length = Math.max(thisParts.length, thatParts.length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
int thisPart = i < thisParts.length ? Integer.parseInt(thisParts[i]) : 0;
|
||||
int thatPart = i < thatParts.length ? Integer.parseInt(thatParts[i]) : 0;
|
||||
if (thisPart < thatPart)
|
||||
return VersionResponse.FOUND_NEW;
|
||||
if (thisPart > thatPart)
|
||||
return VersionResponse.THIS_NEWER;
|
||||
}
|
||||
return VersionResponse.LATEST;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* This file is part of MapReflectionAPI.
|
||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.sbdevelopment.mapreflectionapi.utils;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class YamlFile {
|
||||
private final JavaPlugin plugin;
|
||||
private final String name;
|
||||
private FileConfiguration fileConfiguration;
|
||||
private File file;
|
||||
|
||||
public YamlFile(JavaPlugin plugin, String name) {
|
||||
this.plugin = plugin;
|
||||
this.name = name;
|
||||
|
||||
saveDefaultFile();
|
||||
}
|
||||
|
||||
public void reloadFile() {
|
||||
if (this.file == null)
|
||||
this.file = new File(this.plugin.getDataFolder(), name + ".yml");
|
||||
|
||||
this.fileConfiguration = YamlConfiguration.loadConfiguration(this.file);
|
||||
|
||||
InputStream defaultStream = this.plugin.getResource(name + ".yml");
|
||||
if (defaultStream != null) {
|
||||
YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defaultStream));
|
||||
this.fileConfiguration.setDefaults(defaultConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public FileConfiguration getFile() {
|
||||
if (this.fileConfiguration == null)
|
||||
reloadFile();
|
||||
|
||||
return this.fileConfiguration;
|
||||
}
|
||||
|
||||
public void saveFile() {
|
||||
if (this.fileConfiguration == null || this.file == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.fileConfiguration.save(this.file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Couldn't save the file " + this.name + ".yml.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveDefaultFile() {
|
||||
if (this.file == null)
|
||||
this.file = new File(this.plugin.getDataFolder(), name + ".yml");
|
||||
|
||||
if (!this.file.exists())
|
||||
this.plugin.saveResource(name + ".yml", false);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue