Skip to content

Commit

Permalink
Added support for custom max. image dimension
Browse files Browse the repository at this point in the history
- Updated image commands
- Updated image renderer
- Updated Permissions class
- Updated YamipaPlugin class
- Created ImageDimensionArgument class
- Updated documentation
- Updated dependencies
  • Loading branch information
josemmo committed Jan 3, 2024
1 parent 85c075a commit 2a8cd05
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 31 deletions.
35 changes: 29 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ Yamipa is ready-to-go right out of the box. By default, it creates the following
- `images.dat`: A file holding the list and properties (e.g. coordinates) of all placed images in your server. You
shouldn't modify its contents.

You can change the default path of these files by creating a `config.yml` file in the plugin configuration directory:
You can change the path of these files by creating a `config.yml` file in the plugin configuration directory.
Here are the default configuration values if you don't specify them:
```yaml
verbose: false # Set to "true" to enable more verbose logging
animate-images: true # Set to "false" to disable GIF support
images-path: images # Path to images directory
cache-path: cache # Path to cache directory
data-path: images.dat # Path to placed images database file
verbose: false # Set to "true" to enable more verbose logging
animate-images: true # Set to "false" to disable GIF support
images-path: images # Path to images directory
cache-path: cache # Path to cache directory
data-path: images.dat # Path to placed images database file
max-image-dimension: 30 # Maximum width or height in blocks allowed in images
```
This library uses bStats to anonymously report the number of installs. If you don't like this, feel free to
Expand Down Expand Up @@ -123,6 +125,27 @@ You can change which roles or players are granted these commands by using a perm
such as [LuckPerms](https://luckperms.net/) or [GroupManager](https://elgarl.github.io/GroupManager/).
Both these plugins have been tested to work with Yamipa, although any similar one should work just fine.

## Player variables
Some permission plugins like LuckPerms allow server operators to assign
[key-value pairs](https://luckperms.net/wiki/Meta-Commands) to entities as if they were permissions.
This is useful for granting different capabilities to different players or groups.

Yamipa looks for the following variables which, if found, override the default configuration value that applies to all
players:

| Variable (key) | Overrides | Description |
|:-----------------------------|:----------------------|:---------------------------------------------------------------------------------|
| `yamipa-max-image-dimension` | `max-image-dimension` | Maximum width or height of images and image items issued by this player or group |

For example, if you want to limit the image size to 5x5 blocks just for the "test" player, you can run this command:
```sh
# Using LuckPerms
/lp user test meta set yamipa-max-image-dimension 5
# Using GroupManager
/manuaddv test yamipa-max-image-dimension 5
```

## Protecting areas
In large servers, letting your players place and remove images wherever they want might not be the most sensible idea.
For those cases, Yamipa is compatible with other Bukkit plugins that allow creating and managing world areas.
Expand Down
20 changes: 18 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,29 @@
<scope>provided</scope>
</dependency>

<!-- https://repo.maven.apache.org/maven2/org/bstats/bstats-bukkit/ -->
<!-- https://central.sonatype.com/artifact/org.bstats/bstats-bukkit -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.2</version>
</dependency>

<!-- https://central.sonatype.com/artifact/net.luckperms/api -->
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.4</version>
<scope>provided</scope>
</dependency>

<!-- https://jitpack.io/com/github/ElgarL/groupmanager/ -->
<dependency>
<groupId>com.github.ElgarL</groupId>
<artifactId>groupmanager</artifactId>
<version>3.2</version>
<scope>provided</scope>
</dependency>

<!-- https://maven.enginehub.org/artifactory/repo/com/sk89q/worldguard/worldguard-bukkit/ -->
<dependency>
<groupId>com.sk89q.worldguard</groupId>
Expand Down Expand Up @@ -101,7 +117,7 @@
<scope>provided</scope>
</dependency>

<!-- https://repo.maven.apache.org/maven2/org/jetbrains/annotations/ -->
<!-- https://central.sonatype.com/artifact/org.jetbrains/annotations -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/josemmo/bukkit/plugin/YamipaPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ public void onEnable() {
// Create image renderer
boolean animateImages = getConfig().getBoolean("animate-images", true);
LOGGER.info(animateImages ? "Enabled image animation support" : "Image animation support is disabled");
renderer = new ImageRenderer(basePath.resolve(dataPath), animateImages);
int maxImageDimension = getConfig().getInt("max-image-dimension", 30);
renderer = new ImageRenderer(basePath.resolve(dataPath), animateImages, maxImageDimension);
renderer.start();

// Create image item service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public static void placeImage(
player.sendMessage(ChatColor.RED + "The requested file is not a valid image");
return;
}
final int finalHeight = (height == 0) ? FakeImage.getProportionalHeight(sizeInPixels, width) : height;
final int finalHeight = (height == 0) ? FakeImage.getProportionalHeight(sizeInPixels, player, width) : height;

// Ask player where to place image
SelectBlockTask task = new SelectBlockTask(player);
Expand Down Expand Up @@ -461,7 +461,7 @@ public static void giveImageItems(
return;
}
if (height == 0) {
height = FakeImage.getProportionalHeight(sizeInPixels, width);
height = FakeImage.getProportionalHeight(sizeInPixels, sender, width);
}

// Create item stack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ public static void register(@NotNull YamipaPlugin plugin) {
.withArgument(new OnlinePlayerArgument("player"))
.withArgument(new ImageFileArgument("filename"))
.withArgument(new IntegerArgument("amount", 1, 64))
.withArgument(new IntegerArgument("width", 1, FakeImage.MAX_DIMENSION))
.withArgument(new IntegerArgument("height", 1, FakeImage.MAX_DIMENSION))
.withArgument(new ImageDimensionArgument("width"))
.withArgument(new ImageDimensionArgument("height"))
.withArgument(new ImageFlagsArgument("flags", FakeImage.DEFAULT_GIVE_FLAGS))
.executes((sender, args) -> {
ImageCommand.giveImageItems(sender, (Player) args[1], (ImageFile) args[2], (int) args[3],
Expand All @@ -132,8 +132,8 @@ public static void register(@NotNull YamipaPlugin plugin) {
.withArgument(new OnlinePlayerArgument("player"))
.withArgument(new ImageFileArgument("filename"))
.withArgument(new IntegerArgument("amount", 1, 64))
.withArgument(new IntegerArgument("width", 1, FakeImage.MAX_DIMENSION))
.withArgument(new IntegerArgument("height", 1, FakeImage.MAX_DIMENSION))
.withArgument(new ImageDimensionArgument("width"))
.withArgument(new ImageDimensionArgument("height"))
.executes((sender, args) -> {
ImageCommand.giveImageItems(sender, (Player) args[1], (ImageFile) args[2], (int) args[3],
(int) args[4], (int) args[5], FakeImage.DEFAULT_GIVE_FLAGS);
Expand All @@ -143,7 +143,7 @@ public static void register(@NotNull YamipaPlugin plugin) {
.withArgument(new OnlinePlayerArgument("player"))
.withArgument(new ImageFileArgument("filename"))
.withArgument(new IntegerArgument("amount", 1, 64))
.withArgument(new IntegerArgument("width", 1, FakeImage.MAX_DIMENSION))
.withArgument(new ImageDimensionArgument("width"))
.executes((sender, args) -> {
ImageCommand.giveImageItems(sender, (Player) args[1], (ImageFile) args[2], (int) args[3],
(int) args[4], 0, FakeImage.DEFAULT_GIVE_FLAGS);
Expand All @@ -167,25 +167,25 @@ public static void register(@NotNull YamipaPlugin plugin) {
root.addSubcommand("place")
.withPermission("yamipa.command.place", "yamipa.place")
.withArgument(new ImageFileArgument("filename"))
.withArgument(new IntegerArgument("width", 1, FakeImage.MAX_DIMENSION))
.withArgument(new IntegerArgument("height", 1, FakeImage.MAX_DIMENSION))
.withArgument(new ImageDimensionArgument("width"))
.withArgument(new ImageDimensionArgument("height"))
.withArgument(new ImageFlagsArgument("flags", FakeImage.DEFAULT_PLACE_FLAGS))
.executesPlayer((player, args) -> {
ImageCommand.placeImage(player, (ImageFile) args[1], (int) args[2], (int) args[3], (int) args[4]);
});
root.addSubcommand("place")
.withPermission("yamipa.command.place", "yamipa.place")
.withArgument(new ImageFileArgument("filename"))
.withArgument(new IntegerArgument("width", 1, FakeImage.MAX_DIMENSION))
.withArgument(new IntegerArgument("height", 1, FakeImage.MAX_DIMENSION))
.withArgument(new ImageDimensionArgument("width"))
.withArgument(new ImageDimensionArgument("height"))
.executesPlayer((player, args) -> {
ImageCommand.placeImage(player, (ImageFile) args[1], (int) args[2], (int) args[3],
FakeImage.DEFAULT_PLACE_FLAGS);
});
root.addSubcommand("place")
.withPermission("yamipa.command.place", "yamipa.place")
.withArgument(new ImageFileArgument("filename"))
.withArgument(new IntegerArgument("width", 1, FakeImage.MAX_DIMENSION))
.withArgument(new ImageDimensionArgument("width"))
.executesPlayer((player, args) -> {
ImageCommand.placeImage(player, (ImageFile) args[1], (int) args[2], 0,
FakeImage.DEFAULT_PLACE_FLAGS);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.josemmo.bukkit.plugin.commands.arguments;

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import io.josemmo.bukkit.plugin.renderer.FakeImage;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;

public class ImageDimensionArgument extends IntegerArgument {
/**
* Dimension Argument constructor
* @param name Argument name
*/
public ImageDimensionArgument(@NotNull String name) {
super(name, 1);
}

@Override
public @NotNull Object parse(@NotNull CommandSender sender, @NotNull Object rawValue) throws CommandSyntaxException {
int maxDimension = FakeImage.getMaxImageDimension(sender);
int value = (int) rawValue;
if (value > maxDimension) {
throw newException("Image cannot be larger than " + maxDimension + "x" + maxDimension);
}
return value;
}
}
29 changes: 25 additions & 4 deletions src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import io.josemmo.bukkit.plugin.storage.ImageFile;
import io.josemmo.bukkit.plugin.utils.DirectionUtils;
import io.josemmo.bukkit.plugin.utils.Logger;
import io.josemmo.bukkit.plugin.utils.Permissions;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.Rotation;
import org.bukkit.block.BlockFace;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
Expand All @@ -25,7 +27,6 @@ public class FakeImage extends FakeEntity {
private static final Logger LOGGER = Logger.getLogger("FakeImage");

// Image constants
public static final int MAX_DIMENSION = 30; // In blocks
public static final int MAX_STEPS = 500; // For animated images
public static final int MIN_DELAY = 1; // Minimum step delay in 50ms intervals (50ms / 50ms)
public static final int MAX_DELAY = 50; // Maximum step delay in 50ms intervals (5000ms / 50ms)
Expand Down Expand Up @@ -89,16 +90,36 @@ public class FakeImage extends FakeEntity {
}
}

/**
* Get maximum image dimension
* @param sender Sender instance
* @return Maximum image dimension in blocks
*/
public static int getMaxImageDimension(@NotNull CommandSender sender) {
if (sender instanceof Player) {
String rawValue = Permissions.getVariable("yamipa-max-image-dimension", (Player) sender);
if (rawValue != null) {
try {
return Integer.parseInt(rawValue);
} catch (NumberFormatException __) {
LOGGER.warning("Max. image dimension for " + sender + " is not a valid integer: \"" + rawValue + "\"");
}
}
}
return YamipaPlugin.getInstance().getRenderer().getMaxImageDimension();
}

/**
* Get proportional height
* @param sizeInPixels Image file dimension in pixels
* @param sender Sender instance
* @param width Desired width in blocks
* @return Height in blocks (capped at <code>FakeImage.MAX_DIMENSION</code>)
* @return Height in blocks (capped at maximum image dimension for sender)
*/
public static int getProportionalHeight(@NotNull Dimension sizeInPixels, int width) {
public static int getProportionalHeight(@NotNull Dimension sizeInPixels, @NotNull CommandSender sender, int width) {
float imageRatio = (float) sizeInPixels.height / sizeInPixels.width;
int height = Math.round(width * imageRatio);
height = Math.min(height, MAX_DIMENSION);
height = Math.min(height, getMaxImageDimension(sender));
return height;
}

Expand Down
21 changes: 16 additions & 5 deletions src/main/java/io/josemmo/bukkit/plugin/renderer/ImageRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class ImageRenderer implements Listener {
private static final Logger LOGGER = Logger.getLogger("ImageRenderer");
private final Path configPath;
private final boolean animateImages;
private final int maxImageDimension;
private BukkitTask saveTask;
private final AtomicBoolean hasConfigChanged = new AtomicBoolean(false);
private final ConcurrentMap<WorldAreaId, Set<FakeImage>> images = new ConcurrentHashMap<>();
Expand All @@ -38,12 +39,14 @@ public class ImageRenderer implements Listener {

/**
* Class constructor
* @param configPath Path to configuration file
* @param animateImages Whether to animate images or not
* @param configPath Path to configuration file
* @param animateImages Whether to animate images or not
* @param maxImageDimension Maximum image dimension in blocks
*/
public ImageRenderer(@NotNull Path configPath, boolean animateImages) {
public ImageRenderer(@NotNull Path configPath, boolean animateImages, int maxImageDimension) {
this.configPath = configPath;
this.animateImages = animateImages;
this.maxImageDimension = maxImageDimension;
}

/**
Expand All @@ -54,6 +57,14 @@ public boolean isAnimationEnabled() {
return animateImages;
}

/**
* Get maximum image dimension
* @return Maximum image dimension in blocks
*/
public int getMaxImageDimension() {
return maxImageDimension;
}

/**
* Start instance
*/
Expand Down Expand Up @@ -118,8 +129,8 @@ private void loadConfig() {
Location location = new Location(world, x, y, z);
BlockFace face = BlockFace.valueOf(row[5]);
Rotation rotation = Rotation.valueOf(row[6]);
int width = Math.min(FakeImage.MAX_DIMENSION, Math.abs(Integer.parseInt(row[7])));
int height = Math.min(FakeImage.MAX_DIMENSION, Math.abs(Integer.parseInt(row[8])));
int width = Math.abs(Integer.parseInt(row[7]));
int height = Math.abs(Integer.parseInt(row[8]));
Date placedAt = (row.length > 9 && !row[9].isEmpty()) ?
new Date(Long.parseLong(row[9])*1000L) :
null;
Expand Down
40 changes: 39 additions & 1 deletion src/main/java/io/josemmo/bukkit/plugin/utils/Permissions.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import me.angeschossen.lands.api.land.LandWorld;
import me.angeschossen.lands.api.player.LandPlayer;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import org.anjocaido.groupmanager.GroupManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
Expand All @@ -27,12 +30,26 @@

public class Permissions {
private static final Logger LOGGER = Logger.getLogger();
private static @Nullable LuckPerms luckPerms;
private static @Nullable GroupManager groupManager;
private static @Nullable WorldGuard worldGuard;
private static @Nullable GriefPrevention griefPrevention;
private static @Nullable TownyAPI townyApi;
private static @Nullable LandsIntegration landsApi = null;
private static @Nullable LandsIntegration landsApi;

static {
try {
luckPerms = LuckPermsProvider.get();
} catch (NoClassDefFoundError | IllegalStateException __) {
// LuckPerms is not installed
}

try {
groupManager = (GroupManager) YamipaPlugin.getInstance().getServer().getPluginManager().getPlugin("GroupManager");
} catch (NoClassDefFoundError __) {
// GroupManager is not installed
}

try {
worldGuard = WorldGuard.getInstance();
} catch (NoClassDefFoundError __) {
Expand All @@ -58,6 +75,27 @@ public class Permissions {
}
}

/**
* Get player variable
* @param variable Variable name (key)
* @param player Player instance
* @return Variable value or NULL if not found
*/
public static @Nullable String getVariable(@NotNull String variable, @NotNull Player player) {
if (luckPerms != null) {
return luckPerms.getPlayerAdapter(Player.class).getUser(player).getCachedData().getMetaData()
.getMetaValue(variable);
}

if (groupManager != null) {
String rawValue = groupManager.getWorldsHolder().getWorldPermissions(player)
.getPermissionString(player.getName(), variable);
return rawValue.isEmpty() ? null : rawValue;
}

return null;
}

/**
* Can build at this block
* @param player Player instance
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ api-version: 1.16
depend: [ProtocolLib]
softdepend:
- GriefPrevention
- GroupManager
- Hyperverse
- LuckPerms
- Multiverse-Core
- My_Worlds
- Towny
Expand Down

0 comments on commit 2a8cd05

Please sign in to comment.