🎉 First commit!

This commit is contained in:
SBDeveloper 2022-06-30 11:06:58 +02:00
commit 4dc99283f4
42 changed files with 4068 additions and 0 deletions

View file

@ -0,0 +1,48 @@
package tech.sbdevelopment.mapreflectionapi;
import com.bergerkiller.bukkit.common.map.MapColorPalette;
import java.awt.*;
import java.awt.image.BufferedImage;
public class ArrayImage {
public 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;
public ArrayImage(byte[] data) {
this.array = data;
}
/**
* 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(null), image.getHeight(null), 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;
}
}

View file

@ -0,0 +1,150 @@
package tech.sbdevelopment.mapreflectionapi;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.exceptions.MapLimitExceededException;
public interface MapController {
/**
* 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);
/**
* 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);
/**
* Update the image
*
* @param content new {@link ArrayImage} content
*/
void update(ArrayImage content);
ArrayImage getContent();
/**
* Send the content to a player
*
* @param player {@link Player} receiver of the content
*/
void sendContent(Player player);
/**
* Send the content to a player
*
* @param player {@link Player} receiver of the content
* @param withoutQueue if <code>true</code>, the content will be sent immediately
*/
void sendContent(Player player, boolean withoutQueue);
/**
* 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);
}

View file

@ -0,0 +1,152 @@
package tech.sbdevelopment.mapreflectionapi;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import tech.sbdevelopment.mapreflectionapi.exceptions.MapLimitExceededException;
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;
public class MapManager {
protected final Set<Integer> OCCUPIED_IDS = new HashSet<>();
private final List<MapWrapper> MANAGED_MAPS = new CopyOnWriteArrayList<>();
private final Class<?> wrapperClass;
public MapManager() throws IllegalStateException {
String packageName = Bukkit.getServer().getClass().getPackage().getName();
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
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 is not supported! Contact the developer to get support.");
}
}
@Nullable
public MapWrapper wrapImage(BufferedImage image) {
return wrapImage(new ArrayImage(image));
}
@Nullable
public MapWrapper wrapImage(ArrayImage image) {
for (MapWrapper wrapper : MANAGED_MAPS) {
if (wrapper.getContent().equals(image)) return wrapper;
}
return wrapNewImage(image);
}
private MapWrapper wrapNewImage(ArrayImage image) {
try {
MapWrapper wrapper = (MapWrapper) wrapperClass.getDeclaredConstructor().newInstance();
MANAGED_MAPS.add(wrapper);
return wrapper;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
return null;
}
}
public void unwrapImage(MapWrapper wrapper) {
//TODO Cancel IDs
wrapper.getController().clearViewers();
MANAGED_MAPS.remove(wrapper);
}
public Set<MapWrapper> getMapsVisibleTo(OfflinePlayer player) {
Set<MapWrapper> visible = new HashSet<>();
for (MapWrapper wrapper : MANAGED_MAPS) {
if (wrapper.getController().isViewing(player)) {
visible.add(wrapper);
}
}
return visible;
}
public MapWrapper getWrapperForId(OfflinePlayer player, int id) {
for (MapWrapper wrapper : getMapsVisibleTo(player)) {
if (wrapper.getController().getMapId(player) == id) {
return wrapper;
}
}
return null;
}
public void registerOccupiedID(int id) {
OCCUPIED_IDS.add(id);
}
public void unregisterOccupiedID(int id) {
OCCUPIED_IDS.remove(id);
}
public Set<Integer> getOccupiedIdsFor(OfflinePlayer player) {
Set<Integer> ids = new HashSet<>();
for (MapWrapper wrapper : MANAGED_MAPS) {
int s = wrapper.getController().getMapId(player);
if (s >= 0) {
ids.add(s);
}
}
return ids;
}
public boolean isIdUsedBy(OfflinePlayer player, int id) {
return id > 0 && getOccupiedIdsFor(player).contains(id);
}
public int getNextFreeIdFor(Player player) throws MapLimitExceededException {
Set<Integer> occupied = getOccupiedIdsFor(player);
//Add the 'default' occupied IDs
occupied.addAll(OCCUPIED_IDS);
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");
}
public void clearAllMapsFor(OfflinePlayer player) {
for (MapWrapper wrapper : getMapsVisibleTo(player)) {
wrapper.getController().removeViewer(player);
}
}
public MapWrapper getDuplicate(ArrayImage image) {
for (MapWrapper wrapper : MANAGED_MAPS) {
if (image.equals(wrapper.getContent())) {
return wrapper;
}
}
return null;
}
}

View file

@ -0,0 +1,45 @@
package tech.sbdevelopment.mapreflectionapi;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class MapReflectionAPI extends JavaPlugin {
private static MapReflectionAPI instance;
private static MapManager mapManager;
public static MapReflectionAPI getInstance() {
if (instance == null) throw new IllegalStateException("The plugin is not enabled yet!");
return instance;
}
public static MapManager getMapManager() {
if (mapManager == null) throw new IllegalStateException("The plugin is not enabled yet!");
return mapManager;
}
@Override
public void onEnable() {
instance = this;
if (!Bukkit.getPluginManager().isPluginEnabled("BKCommonLib")) {
getLogger().severe("MapReflectionAPI requires BKCommonLib to function!");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
try {
mapManager = new MapManager();
} catch (IllegalStateException e) {
e.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this);
return;
}
getLogger().info("MapReflectionAPI is enabled!");
}
@Override
public void onDisable() {
instance = null;
}
}

View file

@ -0,0 +1,7 @@
package tech.sbdevelopment.mapreflectionapi;
public interface MapWrapper {
MapController getController();
ArrayImage getContent();
}

View file

@ -0,0 +1,7 @@
package tech.sbdevelopment.mapreflectionapi.exceptions;
public class MapLimitExceededException extends Exception {
public MapLimitExceededException(String message) {
super(message);
}
}