Merge pull request #119 from zDevelopers/feature_give_command

Add give command
This commit is contained in:
Vlammar 2020-12-09 21:59:58 +01:00 committed by GitHub
commit 509e379386
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 302 additions and 157 deletions

View File

@ -124,6 +124,7 @@ public final class ImageOnMap extends QuartzPlugin
RenameCommand.class,
DeleteCommand.class,
DeleteOtherCommand.class,
GiveCommand.class,
GetRemainingCommand.class,
ExploreCommand.class,
ExploreOtherCommand.class,
@ -133,6 +134,7 @@ public final class ImageOnMap extends QuartzPlugin
Commands.registerShortcut("maptool", NewCommand.class, "tomap");
Commands.registerShortcut("maptool", ExploreCommand.class, "maps");
Commands.registerShortcut("maptool", GiveCommand.class, "mapgive");
if (PluginConfiguration.CHECK_FOR_UPDATES.get())
{

View File

@ -53,9 +53,8 @@ public enum Permissions
UPDATE("imageonmap.update"),
UPDATEOTHER("imageonmap.updateother"),
ADMINISTRATIVE("imageonmap.administrative"),
BYPASS_SIZE("imageonmap.bypasssize")
BYPASS_SIZE("imageonmap.bypasssize"),
GIVE("imageonmap.give")
;
private final String permission;

View File

@ -0,0 +1,141 @@
/*
* Copyright or © or Copr. Moribus (2013)
* Copyright or © or Copr. ProkopyL <prokopylmc@gmail.com> (2015)
* Copyright or © or Copr. Amaury Carrade <amaury@carrade.eu> (2016 2020)
* Copyright or © or Copr. Vlammar <valentin.jabre@gmail.com> (2019 2020)
*
* This software is a computer program whose purpose is to allow insertion of
* custom images in a Minecraft world.
*
* This software is governed by the CeCILL-B license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL-B
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-B license and that you accept its terms.
*/
package fr.moribus.imageonmap.commands.maptool;
import fr.moribus.imageonmap.Permissions;
import fr.moribus.imageonmap.commands.IoMCommand;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.zcraft.zlib.components.commands.CommandException;
import fr.zcraft.zlib.components.commands.CommandInfo;
import fr.zcraft.zlib.components.i18n.I;
import fr.zcraft.zlib.tools.PluginLogger;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@CommandInfo(name = "give", usageParameters = "<Player> <MapName> or <Player> <MapName> <Player where to find the map>")
public class GiveCommand extends IoMCommand {
/**
* Parse an argument given at a specific index, it will return a player depending on the given prefixe. Can be player:<username> or uuid:<uuid>
*
* @param index The index.
* @return The retrieved player.
* @throws CommandException If the value is invalid.
* @throws InterruptedException .
* @throws IOException
*/
private OfflinePlayer parse(int index) throws CommandException {
String s = args[index].trim();
String[] subs = s.split(":");
try {
//
if (subs.length == 1) {
return getOfflinePlayerParameter(index);
}
switch (subs[0]) {
case "player":
return getOfflinePlayerParameter(subs[1]);
case "uuid":
StringBuffer string = new StringBuffer(subs[1].toLowerCase());
//if there are no '-'
if (string.length() == 32) {
//we try to fix it by adding - at pos 8,12,16,20
Integer[] pos={20,16,12,8};
for(int i:pos)
string = string.insert(i, "-");
}
//if the given uuid is well formed with 8-4-4-4-12 = 36 chars in length (including '-')
if (string.length() == 36)
return Bukkit.getOfflinePlayer(UUID.fromString(string.toString()));
throwInvalidArgument(I.t("Invalid uuid, please provide an uuid of this form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx or xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
case "bank":
throwInvalidArgument(I.t("Not supported yet"));
break;
default:
throwInvalidArgument(I.t("Invalid prefix, valid one are: player | uuid"));
}
} catch (InterruptedException | ExecutionException e) {
PluginLogger.warning(I.t("Can't access to mojang API to check the player UUID"));
}
return null;
}
@Override
protected void run() throws CommandException {
if (args.length < 2) throwInvalidArgument(I.t("You must give a valid player name and a map name."));
final Player p = getPlayerParameter(0);
ImageMap map;
//TODO add support for map name with spaces "cool name" or name or "name" "cool name with a \" and some stuff" should work
OfflinePlayer player = null;
if (args.length < 4) {
if (args.length == 2) {
player = playerSender();
}
if (args.length == 3) {
player = parse(2);
}
map = MapManager.getMap(player.getUniqueId(), args[1]);
if (map == null) {
throwInvalidArgument(I.t("Map not found"));
}
map.give(p);
}
}
@Override
public boolean canExecute(CommandSender sender) {
return Permissions.GIVE.grantedTo(sender);
}
}

View File

@ -61,6 +61,7 @@ public class ImageIOExecutor extends Worker
{
BufferedImage image = ImageIO.read(file);
mapRenderer.setImage(image);
image.flush();//Safe to free
return null;
}
});
@ -88,7 +89,9 @@ public class ImageIOExecutor extends Worker
{
for(int i = 0, c = mapsIDs.length; i < c; i++)
{
ImageIOExecutor.saveImage(ImageOnMap.getPlugin().getImageFile(mapsIDs[i]), image.getImageAt(i));
BufferedImage img=image.getImageAt(i);
ImageIOExecutor.saveImage(ImageOnMap.getPlugin().getImageFile(mapsIDs[i]), img);
img.flush();//Safe to free
}
}

View File

@ -59,20 +59,16 @@ import java.util.concurrent.Callable;
import java.util.concurrent.Future;
@WorkerAttributes(name = "Image Renderer", queriesMainThread = true)
public class ImageRendererExecutor extends Worker
{
private static URLConnection connecting(URL url)throws IOException{
public class ImageRendererExecutor extends Worker {
private static URLConnection connecting(URL url) throws IOException {
final URLConnection connection = url.openConnection();
connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0");
connection.connect();
if (connection instanceof HttpURLConnection)
{
if (connection instanceof HttpURLConnection) {
final HttpURLConnection httpConnection = (HttpURLConnection) connection;
final int httpCode = httpConnection.getResponseCode();
if ((httpCode / 100) != 2)
{
if ((httpCode / 100) != 2) {
throw new IOException(I.t("HTTP error: {0} {1}", httpCode, httpConnection.getResponseMessage()));
}
}
@ -106,13 +102,12 @@ public class ImageRendererExecutor extends Worker
@Override
public ImageMap run() throws Throwable {
BufferedImage image=null;
BufferedImage image = null;
//If the link is an imgur one
if (url.toString().contains("https://imgur.com/")) {
if (url.toString().toLowerCase().startsWith("https://imgur.com/")) {
//Not handled, can't with the hash only access the image in i.imgur.com/<hash>.<extension>
if (url.toString().contains("gallery/")) {
throw new IOException("We do not support imgur gallery yet, please use direct link to image instead. Right click on the picture you want to use then select copy picture link:) ");
}
@ -133,7 +128,6 @@ public class ImageRendererExecutor extends Worker
}
}
//If not an Imgur link
else {
@ -148,16 +142,17 @@ public class ImageRendererExecutor extends Worker
// Limits are in place and the player does NOT have rights to avoid them.
checkSizeLimit(playerUUID, image);
if (scaling != ImageUtils.ScalingType.NONE && height <= 1 && width <= 1) {
return renderSingle(scaling.resize(image, ImageMap.WIDTH, ImageMap.HEIGHT), playerUUID);
ImageMap ret = renderSingle(scaling.resize(image, ImageMap.WIDTH, ImageMap.HEIGHT), playerUUID);
image.flush();//Safe to free
return ret;
}
final BufferedImage resizedImage = scaling.resize(image, ImageMap.WIDTH * width, ImageMap.HEIGHT * height);
image.flush();
image.flush();//Safe to free
return renderPoster(resizedImage, playerUUID);
}
}, callback);
}
public static void update(final URL url, final ImageUtils.ScalingType scaling, final UUID playerUUID, final ImageMap map, final int width, final int height, WorkerCallback<ImageMap> callback) {
submitQuery(new WorkerRunnable<ImageMap>()
{
@ -210,42 +205,32 @@ public class ImageRendererExecutor extends Worker
static private ImageMap renderSingle(final BufferedImage image, final UUID playerUUID) throws Throwable
{
MapManager.checkMapLimit(1, playerUUID);
final Future<Integer> futureMapID = submitToMainThread(new Callable<Integer>()
{
final Future<Integer> futureMapID = submitToMainThread(new Callable<Integer>() {
@Override
public Integer call() throws Exception
{
public Integer call() throws Exception {
return MapManager.getNewMapsIds(1)[0];
}
});
final int mapID = futureMapID.get();
ImageIOExecutor.saveImage(mapID, image);
submitToMainThread(new Callable<Void>()
{
submitToMainThread(new Callable<Void>() {
@Override
public Void call() throws Exception
{
public Void call() throws Exception {
Renderer.installRenderer(image, mapID);
image.flush();
return null;
}
});
image.flush();
return MapManager.createMap(playerUUID, mapID);
}
static private ImageMap renderPoster(final BufferedImage image, final UUID playerUUID) throws Throwable
{
static private ImageMap renderPoster(final BufferedImage image, final UUID playerUUID) throws Throwable {
final PosterImage poster = new PosterImage(image);
final int mapCount = poster.getImagesCount();
MapManager.checkMapLimit(mapCount, playerUUID);
final Future<int[]> futureMapsIds = submitToMainThread(new Callable<int[]>()
{
final Future<int[]> futureMapsIds = submitToMainThread(new Callable<int[]>() {
@Override
public int[] call() throws Exception
{
public int[] call() throws Exception {
return MapManager.getNewMapsIds(mapCount);
}
});
@ -253,27 +238,26 @@ public class ImageRendererExecutor extends Worker
final int[] mapsIDs = futureMapsIds.get();
ImageIOExecutor.saveImage(mapsIDs, poster);
final int[] mapsIDs = futureMapsIds.get();
if (PluginConfiguration.SAVE_FULL_IMAGE.get())
{
ImageIOExecutor.saveImage(mapsIDs, poster);
if (PluginConfiguration.SAVE_FULL_IMAGE.get()) {
ImageIOExecutor.saveImage(ImageMap.getFullImageFile(mapsIDs[0], mapsIDs[mapsIDs.length - 1]), image);
}
submitToMainThread(new Callable<Void>()
{
submitToMainThread(new Callable<Void>() {
@Override
public Void call() throws Exception
{
public Void call() throws Exception {
Renderer.installRenderer(poster, mapsIDs);
return null;
}
});
image.flush();
poster.getImage().flush();//Safe to free
return MapManager.createMap(poster, playerUUID, mapsIDs);
}
private enum extension {
png, jpg, jpeg, gif
}
}

View File

@ -36,6 +36,8 @@
package fr.moribus.imageonmap.image;
import fr.zcraft.zlib.tools.PluginLogger;
import java.awt.*;
import java.awt.image.BufferedImage;
@ -44,45 +46,25 @@ import java.awt.image.BufferedImage;
*/
public class ImageUtils {
public enum ScalingType {
NONE,
CONTAINED,
COVERED,
STRETCHED,
;
public BufferedImage resize(BufferedImage source, int destinationW, int destinationH) {
switch(this) {
case CONTAINED: return ImageUtils.resize(source, destinationW, destinationH, false);
case COVERED: return ImageUtils.resize(source, destinationW, destinationH, true);
case STRETCHED: return resizeStretched(source, destinationW, destinationH);
default: return source;
}
}
}
/**
* Generates a resized buffer of the given source
*
* @param source
* @param destinationW
* @param destinationH
* @return
*/
static private BufferedImage resize(BufferedImage source, int destinationW, int destinationH, boolean covered)
{
float ratioW = (float)destinationW / (float)source.getWidth();
float ratioH = (float)destinationH / (float)source.getHeight();
static private BufferedImage resize(BufferedImage source, int destinationW, int destinationH, boolean covered) {
float ratioW = (float) destinationW / (float) source.getWidth();
float ratioH = (float) destinationH / (float) source.getHeight();
int finalW, finalH;
int x, y;
if(covered ? ratioW > ratioH : ratioW < ratioH)
{
if (covered ? ratioW > ratioH : ratioW < ratioH) {
finalW = destinationW;
finalH = (int)(source.getHeight() * ratioW);
}
else
{
finalW = (int)(source.getWidth() * ratioH);
finalH = (int) (source.getHeight() * ratioW);
} else {
finalW = (int) (source.getWidth() * ratioH);
finalH = destinationH;
}
@ -95,7 +77,6 @@ public class ImageUtils {
}
/**
*
* @param source
* @param destinationW
* @param destinationH
@ -110,24 +91,57 @@ public class ImageUtils {
/**
* Draws the source image on a new buffer, and returns it.
* The source buffer can be drawn at any size and position in the new buffer.
* @param source The source buffer to draw
*
* @param source The source buffer to draw
* @param bufferW The width of the new buffer
* @param bufferH The height of the new buffer
* @param posX The X position of the source buffer
* @param posY The Y position of the source buffer
* @param posX The X position of the source buffer
* @param posY The Y position of the source buffer
* @param sourceW The width of the source buffer
* @param sourceH The height of the source buffer
* @return The new buffer, with the source buffer drawn on it
*/
static private BufferedImage drawImage(BufferedImage source,
int bufferW, int bufferH,
int posX, int posY,
int sourceW, int sourceH) {
BufferedImage newImage = new BufferedImage(bufferW, bufferH, BufferedImage.TYPE_INT_ARGB);
int bufferW, int bufferH,
int posX, int posY,
int sourceW, int sourceH) {
Graphics graphics = null;
BufferedImage newImage = null;
try {
newImage = new BufferedImage(bufferW, bufferH, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = newImage.getGraphics();
graphics.drawImage(source, posX, posY, sourceW, sourceH, null);
graphics.dispose();
return newImage;
graphics = newImage.getGraphics();
graphics.drawImage(source, posX, posY, sourceW, sourceH, null);
return newImage;
} catch (final Throwable e) {
PluginLogger.warning("Exception/error at drawImage");
if (newImage != null)
newImage.flush();//Safe to free
throw e;
}
}
public enum ScalingType {
NONE,
CONTAINED,
COVERED,
STRETCHED,
;
public BufferedImage resize(BufferedImage source, int destinationW, int destinationH) {
switch (this) {
case CONTAINED:
return ImageUtils.resize(source, destinationW, destinationH, false);
case COVERED:
return ImageUtils.resize(source, destinationW, destinationH, true);
case STRETCHED:
return resizeStretched(source, destinationW, destinationH);
default:
return source;
}
}
}
}

View File

@ -36,156 +36,150 @@
package fr.moribus.imageonmap.image;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
/**
* This class represents an image split into pieces
*/
public class PosterImage
{
public class PosterImage {
static private final int WIDTH = 128;
static private final int HEIGHT = 128;
private BufferedImage originalImage;
private BufferedImage[] cutImages;
private int lines;
private int columns;
private int cutImagesCount;
private int remainderX, remainderY;
/**
* Creates a new Poster from an entire image
*
* @param originalImage the original image
*/
public PosterImage(BufferedImage originalImage)
{
public PosterImage(BufferedImage originalImage) {
this.originalImage = originalImage;
calculateDimensions();
}
private void calculateDimensions()
{
private void calculateDimensions() {
int originalWidth = originalImage.getWidth();
int originalHeight = originalImage.getHeight();
columns = (int) Math.ceil(originalWidth / WIDTH);
lines = (int) Math.ceil(originalHeight / HEIGHT);
remainderX = originalWidth % WIDTH;
remainderY = originalHeight % HEIGHT;
if(remainderX > 0) columns++;
if(remainderY > 0) lines++;
if (remainderX > 0) columns++;
if (remainderY > 0) lines++;
cutImagesCount = columns * lines;
}
public void splitImages()
{
cutImages = new BufferedImage[cutImagesCount];
int imageX;
int imageY = remainderY == 0 ? 0 :(remainderY - HEIGHT) / 2;
for(int i = 0; i < lines; i++)
{
imageX = remainderX == 0 ? 0 :(remainderX - WIDTH) / 2;
for(int j = 0; j < columns; j++)
{
cutImages[i * columns + j] = makeSubImage(originalImage, imageX, imageY);
imageX += WIDTH;
public void splitImages() {
try {
cutImages = new BufferedImage[cutImagesCount];
int imageX;
int imageY = remainderY == 0 ? 0 : (remainderY - HEIGHT) / 2;
for (int i = 0; i < lines; i++) {
imageX = remainderX == 0 ? 0 : (remainderX - WIDTH) / 2;
for (int j = 0; j < columns; j++) {
cutImages[i * columns + j] = makeSubImage(originalImage, imageX, imageY);
imageX += WIDTH;
}
imageY += HEIGHT;
}
imageY += HEIGHT;
} catch (final Throwable e) {
if (cutImages != null)
for (BufferedImage bi : cutImages) {
if (bi != null) {
bi.flush();//Safe to free
}
}
throw e;
}
originalImage = null;
}
/**
* Generates the subimage that intersects with the given map rectangle.
*
* @param x X coordinate of top-left point of the map.
* @param y Y coordinate of top-left point of the map.
* @return the requested subimage.
*/
private BufferedImage makeSubImage(BufferedImage originalImage, int x, int y)
{
private BufferedImage makeSubImage(BufferedImage originalImage, int x, int y) {
BufferedImage newImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = newImage.getGraphics();
graphics.drawImage(originalImage, -x, -y, null);
graphics.dispose();
return newImage;
}
/**
*
* @return the split images
*/
public BufferedImage[] getImages()
{
public BufferedImage[] getImages() {
return cutImages;
}
public BufferedImage getImageAt(int i)
{
public BufferedImage getImageAt(int i) {
return cutImages[i];
}
public int getColumnAt(int i)
{
public BufferedImage getImage() {
return originalImage;
}
public int getColumnAt(int i) {
return i % columns;
}
public int getLineAt(int i)
{
public int getLineAt(int i) {
return i / columns;
}
/**
*
* @return the number of lines of the poster
*/
public int getLines()
{
public int getLines() {
return lines;
}
/**
*
* @return the number of columns of the poster
*/
public int getColumns()
{
public int getColumns() {
return columns;
}
/**
*
* @return the number of split images
*/
public int getImagesCount()
{
public int getImagesCount() {
return cutImagesCount;
}
public int getRemainderX()
{
public int getRemainderX() {
return remainderX;
}
public void setRemainderX(int remainderX)
{
public void setRemainderX(int remainderX) {
this.remainderX = remainderX;
}
public int getRemainderY()
{
public int getRemainderY() {
return remainderY;
}
public void setRemainderY(int remainderY)
{
public void setRemainderY(int remainderY) {
this.remainderY = remainderY;
}
}

View File

@ -12,6 +12,9 @@ commands:
description: Manage maps
maps:
description: Manage maps through a GUI
mapgive:
description: give a map to a player from a player map store, by default take from the player that make the command
usage: /<command> player_to map_name [player_from]
permissions:
imageonmap.*:
@ -31,6 +34,7 @@ permissions:
imageonmap.delete: true
imageonmap.deleteother: false
imageonmap.bypasssize: false
imageonmap.give: false
imageonmap.update: true
imageonmap.updateother: false
@ -74,6 +78,10 @@ permissions:
description: "Allows you to delete a map you rendered in the past."
default: true
imageonmap.give:
description: "Allows you to give a map to a specified player."
default: op
imageonmap.deleteother:
description: "Allows you to delete a map a player rendered in the past."
default: false