Merge pull request #219 from zDevelopers/update/1.17

4.2.0
This commit is contained in:
Vlammar 2021-11-15 21:06:01 +01:00 committed by GitHub
commit 5191dca932
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 311 additions and 44 deletions

View File

@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
java: [8, 9, 10, 11, 12, 13, 14, 15]
java: [8, 11, 16, 17]
steps:
- name: Checkout

View File

@ -39,7 +39,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>fr.moribus</groupId>
<artifactId>ImageOnMap</artifactId>
<version>4.1.2</version>
<version>4.2.0</version>
<packaging>jar</packaging>
<properties>

View File

@ -43,7 +43,6 @@ import fr.moribus.imageonmap.commands.maptool.GetCommand;
import fr.moribus.imageonmap.commands.maptool.GetRemainingCommand;
import fr.moribus.imageonmap.commands.maptool.GiveCommand;
import fr.moribus.imageonmap.commands.maptool.ListCommand;
import fr.moribus.imageonmap.commands.maptool.MigrateCommand;
import fr.moribus.imageonmap.commands.maptool.NewCommand;
import fr.moribus.imageonmap.commands.maptool.RenameCommand;
import fr.moribus.imageonmap.commands.maptool.UpdateCommand;
@ -51,8 +50,6 @@ import fr.moribus.imageonmap.image.ImageIOExecutor;
import fr.moribus.imageonmap.image.ImageRendererExecutor;
import fr.moribus.imageonmap.image.MapInitEvent;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.migration.MigratorExecutor;
import fr.moribus.imageonmap.migration.V3Migrator;
import fr.moribus.imageonmap.ui.MapItemManager;
import fr.zcraft.quartzlib.components.commands.CommandWorkers;
import fr.zcraft.quartzlib.components.commands.Commands;
@ -104,7 +101,7 @@ public final class ImageOnMap extends QuartzPlugin {
public void onEnable() {
// Creating the images and maps directories if necessary
try {
imagesDirectory = checkPluginDirectory(imagesDirectory, V3Migrator.getOldImagesDirectory(this));
//imagesDirectory = checkPluginDirectory(imagesDirectory, V3Migrator.getOldImagesDirectory(this));
checkPluginDirectory(mapsDirectory);
} catch (final IOException ex) {
PluginLogger.error("FATAL: " + ex.getMessage());
@ -136,7 +133,7 @@ public final class ImageOnMap extends QuartzPlugin {
GiveCommand.class,
GetRemainingCommand.class,
ExploreCommand.class,
MigrateCommand.class,
//MigrateCommand.class,//Removed for now doesn't work nor is useful, maybe useful later on
UpdateCommand.class
);
@ -161,7 +158,7 @@ public final class ImageOnMap extends QuartzPlugin {
public void onDisable() {
MapManager.exit();
MapItemManager.exit();
MigratorExecutor.waitForMigration();
//MigratorExecutor.waitForMigration();//Removed for now doesn't work nor is useful, maybe useful later on
super.onDisable();
}

View File

@ -37,7 +37,11 @@
package fr.moribus.imageonmap;
import fr.zcraft.quartzlib.components.i18n.I;
import fr.zcraft.quartzlib.tools.PluginLogger;
import java.util.Set;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissionAttachmentInfo;
public enum Permissions {
NEW("imageonmap.new", "imageonmap.userender"),
@ -47,6 +51,7 @@ public enum Permissions {
GETOTHER("imageonmap.getother"),
RENAME("imageonmap.rename"),
PLACE_SPLATTER_MAP("imageonmap.placesplattermap"),
PLACE_INVISIBLE_SPLATTER_MAP("imageonmap.placeinvisiblesplattermap"),
REMOVE_SPLATTER_MAP("imageonmap.removesplattermap"),
DELETE("imageonmap.delete"),
DELETEOTHER("imageonmap.deleteother"),
@ -54,7 +59,10 @@ public enum Permissions {
UPDATEOTHER("imageonmap.updateother"),
ADMINISTRATIVE("imageonmap.administrative"),
BYPASS_SIZE("imageonmap.bypasssize"),
GIVE("imageonmap.give");
BYPASS_IMAGE_LIMIT("imageonmap.bypassimagelimit"),
BYPASS_MAP_LIMIT("imageonmap.bypassmaplimit"),
GIVE("imageonmap.give"),
IGNOREALLOWLIST("imageonmap.ignoreallowlist_hostingsite");
private final String permission;
private final String[] aliases;
@ -83,4 +91,37 @@ public enum Permissions {
return false;
}
/**
* Return the limit of map the user is allowed to make
*
* @param permissible The permissible to check.
* @return the limit
*/
public int getLimitPermission(Permissible permissible, LimitType type) {
Set<PermissionAttachmentInfo> perms = permissible.getEffectivePermissions();
String prefix = String.format("imageonmap.%slimit.", type.name());
for (PermissionAttachmentInfo pai : perms) {
String permString = pai.getPermission().toLowerCase();
if (permString.startsWith(prefix)) {
if (pai.getValue()) {
try {
int limit = Integer.parseInt(permString.split(prefix)[1].trim());
return limit;
} catch (Exception e) {
PluginLogger.warning(
I.t("The correct syntax for setting map limit node is: ImageOnMap.mapLimit.X "
+ "where you can replace X with the limit of map a player is allowed to have"));
}
}
}
}
return 2147483647; //Virtually no limit
}
public enum LimitType {
map,
image
}
}

View File

@ -59,4 +59,6 @@ public final class PluginConfiguration extends Configuration {
public static ConfigurationItem<Integer> LIMIT_SIZE_X = item("limit-map-size-x", 0);
public static ConfigurationItem<Integer> LIMIT_SIZE_Y = item("limit-map-size-y", 0);
public static ConfigurationItem<String> ALLOWLIST_HOSTINGSITE = item("allowlist_hostingsite", "");
}

View File

@ -36,11 +36,14 @@
package fr.moribus.imageonmap.commands;
import fr.moribus.imageonmap.PluginConfiguration;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.zcraft.quartzlib.components.commands.Command;
import fr.zcraft.quartzlib.components.commands.CommandException;
import fr.zcraft.quartzlib.components.i18n.I;
import fr.zcraft.quartzlib.tools.PluginLogger;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@ -53,6 +56,20 @@ import org.bukkit.entity.Player;
public abstract class IoMCommand extends Command {
protected boolean checkHostingSite(URL url) {
String urlsString = PluginConfiguration.ALLOWLIST_HOSTINGSITE.get();
if (urlsString.trim().isEmpty()) {
return true;
}
String[] hosts = urlsString.trim().replaceAll("https://","").split(",");
for (String host : hosts) {
if (url.getHost().equals(host.trim())) {
return true;
}
}
return false;
}
protected void retrieveUUID(String arg, Consumer<UUID> consumer) {
UUID uuid;
OfflinePlayer offlinePlayer;

View File

@ -41,6 +41,7 @@ import fr.moribus.imageonmap.commands.IoMCommand;
import fr.moribus.imageonmap.image.ImageRendererExecutor;
import fr.moribus.imageonmap.image.ImageUtils;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.PosterMap;
import fr.zcraft.quartzlib.components.commands.CommandException;
import fr.zcraft.quartzlib.components.commands.CommandInfo;
@ -87,9 +88,35 @@ public class NewCommand extends IoMCommand {
if (args.length < 1) {
throwInvalidArgument(I.t("You must give an URL to take the image from."));
}
//Checking if the map limit and image limit
if (!Permissions.BYPASS_IMAGE_LIMIT.grantedTo(player)) {
int imageLimit = Permissions.NEW.getLimitPermission(player, Permissions.LimitType.image);
int imageCount = MapManager.getPlayerMapStore(player.getUniqueId()).getImagesCount();
if (imageLimit <= imageCount) {
throwInvalidArgument(
I.t("Your image limit is set to {0} and you currently have {1} loaded image(s)",
imageLimit,
imageCount));
}
}
if (!Permissions.BYPASS_MAP_LIMIT.grantedTo(player)) {
int mapLimit = Permissions.NEW.getLimitPermission(player, Permissions.LimitType.map);
int mapCount = MapManager.getPlayerMapStore(player.getUniqueId()).getMapCount();
if (mapLimit <= mapCount) {
throwInvalidArgument(
I.t("Your map limit is set to {0} and you currently have {1} loaded map(s)",
mapLimit,
mapCount));
}
}
try {
url = new URL(args[0]);
if (!Permissions.IGNOREALLOWLIST.grantedTo(player) && !checkHostingSite(url)) {
throwInvalidArgument(I.t("This hosting website is not trusted, if you think that this is an error "
+ " contact your server administrator"));
return;
}
} catch (MalformedURLException ex) {
throwInvalidArgument(I.t("Invalid URL."));
return;
@ -121,7 +148,7 @@ public class NewCommand extends IoMCommand {
}
scaling = resizeMode();
}
if (width == 0 || height == 0) {
if (width < 0 || height < 0) {
throwInvalidArgument(I.t("You need to specify a valid size. e.g. resize 4 5"));
return;
}

View File

@ -168,6 +168,12 @@ public class UpdateCommand extends IoMCommand {
URL url1;
try {
url1 = new URL(url);
if (!Permissions.IGNOREALLOWLIST.grantedTo(playerSender) && !checkHostingSite(url1)) {
throwInvalidArgument(I.t("This hosting website is not trusted, if you think that this is an error "
+ " contact your server administrator"));
return;
}
//TODO replace by a check of the load status.(if not loaded load the mapmanager)
MapManager.load(false);//we don't want to spam the console each time we reload the mapManager
@ -211,7 +217,7 @@ public class UpdateCommand extends IoMCommand {
ActionBar.removeMessage(playerSender);
}
}
} catch (MalformedURLException ex) {
} catch (MalformedURLException | CommandException ex) {
warning(sender, I.t("Invalid URL."));
}
});

View File

@ -46,6 +46,7 @@ import fr.zcraft.quartzlib.components.gui.Gui;
import fr.zcraft.quartzlib.components.gui.GuiAction;
import fr.zcraft.quartzlib.components.gui.PromptGui;
import fr.zcraft.quartzlib.components.i18n.I;
import fr.zcraft.quartzlib.tools.PluginLogger;
import fr.zcraft.quartzlib.tools.items.ItemStackBuilder;
import fr.zcraft.quartzlib.tools.runners.RunTask;
import org.apache.commons.lang.ArrayUtils;
@ -208,32 +209,35 @@ public class MapDetailGui extends ExplorerGui<Integer> {
return;
}
PromptGui.prompt(getPlayer(), newName -> {
if (!Permissions.RENAME.grantedTo(getPlayer())) {
I.sendT(getPlayer(), "{ce}You are no longer allowed to do that.");
return;
}
try {
PromptGui.prompt(getPlayer(), newName -> {
if (!Permissions.RENAME.grantedTo(getPlayer())) {
I.sendT(getPlayer(), "{ce}You are no longer allowed to do that.");
return;
}
if (newName == null || newName.isEmpty()) {
I.sendT(getPlayer(), "{ce}Map names can't be empty.");
return;
}
if (newName.equals(map.getName())) {
return;
}
if (newName == null || newName.isEmpty()) {
I.sendT(getPlayer(), "{ce}Map names can't be empty.");
return;
}
if (newName.equals(map.getName())) {
return;
}
map.rename(newName);
I.sendT(getPlayer(), "{cs}Map successfully renamed.");
map.rename(newName);
I.sendT(getPlayer(), "{cs}Map successfully renamed.");
if (getParent() != null) {
RunTask.later(() -> Gui.open(getPlayer(), this), 1L);
} else {
close();
}
}, map.getName(), this);
if (getParent() != null) {
RunTask.later(() -> Gui.open(getPlayer(), this), 1L);
} else {
close();
}
}, map.getName(), this);
} catch (IllegalStateException e) {
PluginLogger.error("Error while renaming map: ", e);
}
}
@GuiAction("delete")

View File

@ -98,6 +98,7 @@ public class ImageRendererExecutor extends Worker {
public ImageMap run() throws Throwable {
BufferedImage image = null;
//If the link is an imgur one
if (url.toString().toLowerCase().startsWith("https://imgur.com/")) {
@ -162,7 +163,6 @@ public class ImageRendererExecutor extends Worker {
submitQuery(new WorkerRunnable<ImageMap>() {
@Override
public ImageMap run() throws Throwable {
final URLConnection connection = connecting(url);
final InputStream stream = connection.getInputStream();

View File

@ -38,9 +38,16 @@ package fr.moribus.imageonmap.image;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.zcraft.quartzlib.components.events.FutureEventHandler;
import fr.zcraft.quartzlib.components.events.FutureEvents;
import fr.zcraft.quartzlib.components.events.WrappedEvent;
import fr.zcraft.quartzlib.core.QuartzLib;
import fr.zcraft.quartzlib.tools.PluginLogger;
import fr.zcraft.quartzlib.tools.reflection.Reflection;
import fr.zcraft.quartzlib.tools.runners.RunTask;
import java.io.File;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Entity;
@ -49,7 +56,10 @@ import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.hanging.HangingBreakEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.world.ChunkLoadEvent;
@ -57,8 +67,11 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.map.MapView;
public class MapInitEvent implements Listener {
public static void init() {
QuartzLib.registerEvents(new MapInitEvent());
FutureEvents.registerFutureEvents(new EntitiesLoadListener());
for (World world : Bukkit.getWorlds()) {
for (ItemFrame frame : world.getEntitiesByClass(ItemFrame.class)) {
@ -97,11 +110,13 @@ public class MapInitEvent implements Listener {
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
for (Entity entity : event.getChunk().getEntities()) {
if (entity instanceof ItemFrame) {
initMap(((ItemFrame) entity).getItem());
RunTask.later(() -> {
for (Entity entity : event.getChunk().getEntities()) {
if (entity instanceof ItemFrame) {
initMap(((ItemFrame) entity).getItem());
}
}
}
}, 5L);
}
@EventHandler
@ -131,4 +146,114 @@ public class MapInitEvent implements Listener {
}
}
@EventHandler
public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
//Negate entity interaction with item frame containing IoM maps.
Entity entity = event.getEntity();
if (!(entity instanceof ItemFrame)) {
return;
}
Entity damager = event.getDamager();
if (damager instanceof Player) {
//can solve the dup with the map here by doing a better handling
return;
}
ItemStack item = ((ItemFrame) entity).getItem();
if (item.getType() == Material.FILLED_MAP) {
//if the map exist we canceled the event
if (MapManager.getMap(item) != null) {
event.setCancelled(true);
}
}
}
@EventHandler
public void onEntityDamageByBlockEvent(EntityDamageByBlockEvent event) {
//Negate damage done to IoM maps by some blocks
Entity entity = event.getEntity();
if (!(entity instanceof ItemFrame)) {
return;
}
ItemStack item = ((ItemFrame) entity).getItem();
if (item.getType() == Material.FILLED_MAP) {
//if the map exist we canceled the event
if (MapManager.getMap(item) != null) {
switch (event.getCause()) {
case MAGIC:
case ENTITY_EXPLOSION:
case FIRE_TICK:
case LIGHTNING:
case CRAMMING:
case WITHER:
case SUFFOCATION:
case DROWNING:
case BLOCK_EXPLOSION:
event.setCancelled(true);
break;
default:
}
}
}
}
@EventHandler
public void onHangingBreakEvent(HangingBreakEvent event) {
Entity entity = event.getEntity();
if (!(entity instanceof ItemFrame)) {
return;
}
ItemStack item = ((ItemFrame) entity).getItem();
if (item.getType() == Material.FILLED_MAP) {
//if the map exist we canceled the event
if (MapManager.getMap(item) != null) {
if (event.getCause() == HangingBreakEvent.RemoveCause.EXPLOSION) {
//creeper goes boom
event.setCancelled(true);
}
}
}
}
protected static final class EntitiesLoadListener implements Listener {
@FutureEventHandler(event = "world.EntitiesLoadEvent")
public void onEntitiesLoad(WrappedEvent event) {
//New in 1.17
//Used to make sure map are really loaded in 1.17 on Paper (else some won't render or update properly)
RunTask.later(() -> {
try {
Chunk chunk = (Chunk) Reflection.call(event.getEvent(), "getChunk");
Entity[] entities = chunk.getEntities();
//Not the most efficient method because we go through entity already loaded
//The direct method using getEntities of
// https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/event/world/EntitiesLoadEvent.html
//Return an unmodifiable list of entities.
//Using this make the overall process a bit more efficient but way more complicated and very weak
// to change.
//TODO Investigate if there is a better way to do this.
//Early exit, most are empty entities array
if (entities.length == 0) {
return;
}
for (Entity entity : entities) {
if (entity instanceof ItemFrame) {
initMap(((ItemFrame) entity).getItem());
}
}
} catch (Exception e) {
PluginLogger.error(e.toString());
return;
}
}, 5L);
}
}
}

View File

@ -37,17 +37,16 @@
package fr.moribus.imageonmap.ui;
import fr.moribus.imageonmap.Permissions;
import fr.moribus.imageonmap.PluginConfiguration;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.map.SingleMap;
import fr.zcraft.quartzlib.components.i18n.I;
import fr.zcraft.quartzlib.core.QuartzLib;
import fr.zcraft.quartzlib.tools.PluginLogger;
import fr.zcraft.quartzlib.tools.items.ItemStackBuilder;
import fr.zcraft.quartzlib.tools.items.ItemUtils;
import fr.zcraft.quartzlib.tools.runners.RunTask;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Queue;
@ -320,7 +319,6 @@ public class MapItemManager implements Listener {
|| !SplatterMapManager.hasSplatterMap(player, poster)) {
poster.give(player);
}
return;
}
}
@ -329,7 +327,7 @@ public class MapItemManager implements Listener {
if (!MapManager.managesMap(frame.getItem())) {
return;
}
SplatterMapManager.removePropertiesFromFrames(player, frame);
frame.setItem(new ItemStackBuilder(item)
.title(getMapTitle(item))
.hideAllAttributes()

View File

@ -37,6 +37,7 @@
package fr.moribus.imageonmap.ui;
import com.google.common.collect.ImmutableMap;
import fr.moribus.imageonmap.Permissions;
import fr.moribus.imageonmap.image.MapInitEvent;
import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager;
@ -53,6 +54,7 @@ import fr.zcraft.quartzlib.tools.runners.RunTask;
import fr.zcraft.quartzlib.tools.text.MessageSender;
import fr.zcraft.quartzlib.tools.world.FlatLocation;
import fr.zcraft.quartzlib.tools.world.WorldUtils;
import java.lang.reflect.Method;
import org.bukkit.ChatColor;
import org.bukkit.Color;
import org.bukkit.Material;
@ -65,7 +67,8 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
//TODO rework splatter effect, using ID is far more stable than nbt tags.
// To update when adding small picture previsualization.
public abstract class SplatterMapManager {
private SplatterMapManager() {
}
@ -137,6 +140,7 @@ public abstract class SplatterMapManager {
* @return True if the attribute was detected.
*/
public static boolean hasSplatterAttributes(ItemStack itemStack) {
try {
final NBTCompound nbt = NBT.fromItemStack(itemStack);
if (!nbt.containsKey("Enchantments")) {
@ -240,6 +244,7 @@ public abstract class SplatterMapManager {
//Rotation management relative to player rotation the default position is North,
// when on ceiling we flipped the rotation
RunTask.later(() -> {
addPropertiesToFrames(player, frame);
frame.setItem(
new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem());
}, 5L);
@ -301,6 +306,7 @@ public abstract class SplatterMapManager {
int id = poster.getMapIdAtReverseY(i);
RunTask.later(() -> {
addPropertiesToFrames(player, frame);
frame.setItem(
new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem());
}, 5L);
@ -359,10 +365,31 @@ public abstract class SplatterMapManager {
for (ItemFrame frame : matchingFrames) {
if (frame != null) {
removePropertiesFromFrames(player, frame);
frame.setItem(null);
}
}
return poster;
}
public static void addPropertiesToFrames(Player player, ItemFrame frame) {
if (Permissions.PLACE_INVISIBLE_SPLATTER_MAP.grantedTo(player)) {
try {
Method setVisible = frame.getClass().getMethod("setVisible", boolean.class);
setVisible.invoke(frame, false);
} catch (Exception e) {
//1.16-
}
}
}
public static void removePropertiesFromFrames(Player player, ItemFrame frame) {
try {
Method setVisible = frame.getClass().getMethod("setVisible", boolean.class);
setVisible.invoke(frame, true);
} catch (Exception e) {
//1.16-
}
}
}

View File

@ -26,3 +26,6 @@ limit-map-size-y: 0
# Should the full image be saved when a map is rendered?
save-full-image: false
# Give the name of trusted image hosting website
#Example allowlist_hostingsite: https://imgur.com/, https://i.imgur.com/, https://cdn.discordapp.com
allowlist_hostingsite:

View File

@ -1,6 +1,6 @@
name: ImageOnMap
main: fr.moribus.imageonmap.ImageOnMap
version: "4.1.2"
version: "4.2.0"
api-version: "1.13"
@ -38,6 +38,10 @@ permissions:
imageonmap.give: false
imageonmap.update: true
imageonmap.updateother: false
imageonmap.bypassmaplimit: false
imageonmap.bypassimagelimit: false
imageonmap.ignoreallowlist_hostingsite: true
imageonmap.placeinvisiblesplattermap: true
imageonmap.userender:
description: "Allows you to use /tomap and related commands (/maptool getremaining). Alias of imageonmap.new."
@ -110,3 +114,19 @@ permissions:
imageonmap.updateother:
description: "Allows you to update an existing map of an other player with a new image."
default: op
imageonmap.bypassmaplimit:
description: "Allows you to bypass permission node check for the number of used map (by default users have an unlimited amount of maps)."
default: op
imageonmap.bypassimagelimit:
description: "Allows you to bypass permission node check for the number of images in the playerMapStore (by default users have an unlimited amount of images)."
default: op
imageonmap.ignoreallowlist_hostingsite:
description: "Allows you to ignore the restriction on the allow list for image hosting website."
default: true
imageonmap.placeinvisiblesplattermap:
description: "Allows you to make the item frame on which you placed your splatter map invisible."
default: true