Complete Rework
- supports 1.13 and up - support bottom/top side placement for 1.14+ - support for fixed/invisible property in 1.16+ - complete UX rework - better command structure - more colors - better documentation - removed fastsend, since it's obsolete in modern version - GitHub Actions Continuous Integration
This commit is contained in:
parent
0fc7586df3
commit
075bf2a098
|
@ -0,0 +1,13 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
custom: ["paypal.me/sydmontague"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
|
||||
# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
# patreon: # Replace with a single Patreon username
|
||||
# open_collective: # Replace with a single Open Collective username
|
||||
# ko_fi: # Replace with a single Ko-fi username
|
||||
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
# liberapay: # Replace with a single Liberapay username
|
||||
# issuehunt: # Replace with a single IssueHunt username
|
||||
# otechie: # Replace with a single Otechie username
|
|
@ -0,0 +1,32 @@
|
|||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Java CI with Maven
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Package
|
||||
path: target/ImageMaps.jar
|
||||
- name: Publish to GitHub Packages Apache Maven
|
||||
run: mvn deploy
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }} # GITHUB_TOKEN is the default env for the password
|
|
@ -2,4 +2,5 @@
|
|||
/.classpath
|
||||
/.project
|
||||
/.settings
|
||||
/bin
|
||||
/bin
|
||||
/dependency-reduced-pom.xml
|
||||
|
|
80
pom.xml
80
pom.xml
|
@ -1,30 +1,51 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.craftlancer.imagemaps</groupId>
|
||||
<artifactId>ImageMaps</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>ImageMaps</name>
|
||||
<description>Draw Images on maps!</description>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>github-clcore</id>
|
||||
<name>GitHub CLCore Packages</name>
|
||||
<url>https://maven.pkg.github.com/SydMontague/ImageMaps</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>http://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>github</id>
|
||||
<name>GitHub SydMontague Apache Maven Packages</name>
|
||||
<url>https://maven.pkg.github.com/SydMontague/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.13.2-R0.1-SNAPSHOT</version>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.16.1-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.craftlancer</groupId>
|
||||
<artifactId>clcore</artifactId>
|
||||
<version>0.4.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
|
@ -34,16 +55,55 @@
|
|||
</resources>
|
||||
<finalName>${project.name}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>de.craftlancer:clcore</artifact>
|
||||
<includes>
|
||||
<include>de/craftlancer/core/command/*</include>
|
||||
<include>de/craftlancer/core/util/*</include>
|
||||
<include>de/craftlancer/core/LambdaRunnable*</include>
|
||||
<include>de/craftlancer/core/Utils*</include>
|
||||
<include>de/craftlancer/core/SemanticVersion*</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>de.craftlancer:clcore</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>de.craftlancer.core</pattern>
|
||||
<shadedPattern>de.craftlancer.imagemaps.clcore</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -1,71 +0,0 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class FastSendTask extends BukkitRunnable implements Listener
|
||||
{
|
||||
private Map<UUID, Queue<Integer>> status = new HashMap<>();
|
||||
private final ImageMaps plugin;
|
||||
private final int mapsPerRun;
|
||||
|
||||
public FastSendTask(ImageMaps plugin, int mapsPerSend)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.mapsPerRun = mapsPerSend;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (plugin.getFastSendList().isEmpty())
|
||||
return;
|
||||
|
||||
for (Player p : plugin.getServer().getOnlinePlayers())
|
||||
{
|
||||
Queue<Integer> state = getStatus(p);
|
||||
|
||||
for (int i = 0; i < mapsPerRun && !state.isEmpty(); i++)
|
||||
p.sendMap(plugin.getServer().getMap(state.poll()));
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<Integer> getStatus(Player p)
|
||||
{
|
||||
if (!status.containsKey(p.getUniqueId()))
|
||||
status.put(p.getUniqueId(), new LinkedList<Integer>(plugin.getFastSendList()));
|
||||
|
||||
return status.get(p.getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerJoin(PlayerJoinEvent e)
|
||||
{
|
||||
status.put(e.getPlayer().getUniqueId(), new LinkedList<Integer>(plugin.getFastSendList()));
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent e)
|
||||
{
|
||||
status.remove(e.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
public void addToQueue(int mapId)
|
||||
{
|
||||
for(Queue<Integer> queue : status.values())
|
||||
queue.add(mapId);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author gbl
|
||||
*/
|
||||
public class ImageDownloadCompleteNotifier extends BukkitRunnable {
|
||||
|
||||
private ImageMaps plugin;
|
||||
|
||||
public ImageDownloadCompleteNotifier(ImageMaps plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<ImageDownloadTask> tasks = plugin.getDownloadTasks();
|
||||
|
||||
Iterator<ImageDownloadTask> itr = tasks.iterator();
|
||||
while(itr.hasNext()) {
|
||||
ImageDownloadTask task = itr.next();
|
||||
|
||||
if(task.isDone()) {
|
||||
itr.remove();
|
||||
task.getSender().sendMessage("Download " + task.getURL() + ": " + task.getResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author gbl
|
||||
*/
|
||||
public class ImageDownloadTask implements Runnable {
|
||||
private JavaPlugin plugin;
|
||||
private String filename;
|
||||
private String downloadUrl;
|
||||
private CommandSender sender;
|
||||
private CompletableFuture future;
|
||||
|
||||
ImageDownloadTask(ImageMaps plugin, String url, String filename, CommandSender sender) {
|
||||
this.plugin = plugin;
|
||||
this.sender = sender;
|
||||
this.downloadUrl = url;
|
||||
this.filename = filename;
|
||||
|
||||
this.future = CompletableFuture.runAsync(this);
|
||||
}
|
||||
|
||||
public CommandSender getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return future.isDone();
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
try {
|
||||
return (future.isDone() ? (String) future.get() : null);
|
||||
} catch (Exception ex) {
|
||||
return "Exception when getting result";
|
||||
}
|
||||
}
|
||||
|
||||
public String getURL() {
|
||||
return this.downloadUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ReadableByteChannel in = null;
|
||||
FileOutputStream fos = null;
|
||||
FileChannel out = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
URL url = new URL(downloadUrl);
|
||||
URLConnection connection = url.openConnection();
|
||||
if (!(connection instanceof HttpURLConnection)) {
|
||||
future.complete("Not a http(s) URL");
|
||||
return;
|
||||
}
|
||||
|
||||
int responseCode = ((HttpURLConnection) connection).getResponseCode();
|
||||
if (responseCode != 200) {
|
||||
future.complete("HTTP Status " + responseCode);
|
||||
return;
|
||||
}
|
||||
|
||||
String mimeType = ((HttpURLConnection) connection).getHeaderField("Content-type");
|
||||
if (!(mimeType.startsWith("image/"))) {
|
||||
future.complete("That is a " + mimeType + ", not an image");
|
||||
return;
|
||||
}
|
||||
|
||||
in = Channels.newChannel(is=connection.getInputStream());
|
||||
fos = new FileOutputStream(new File(plugin.getDataFolder() + "/images", filename));
|
||||
out = fos.getChannel();
|
||||
out.transferFrom(in, 0, Long.MAX_VALUE);
|
||||
future.complete("Download to " + filename + " finished");
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
future.complete("URL invalid");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
future.complete("IO Exception");
|
||||
}
|
||||
finally {
|
||||
close(out);
|
||||
close(in);
|
||||
close(is);
|
||||
close(fos);
|
||||
}
|
||||
}
|
||||
|
||||
public void close(Closeable c) {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +1,90 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
public class ImageMap
|
||||
{
|
||||
private String image;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
|
||||
public class ImageMap implements ConfigurationSerializable {
|
||||
|
||||
private String filename;
|
||||
private int x;
|
||||
private int y;
|
||||
private boolean fastsend;
|
||||
private double scale;
|
||||
|
||||
public ImageMap(String image, int x, int y, boolean fastsend, double scale)
|
||||
{
|
||||
this.image = image;
|
||||
public ImageMap(String filename, int x, int y, double scale) {
|
||||
this.filename = filename;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.fastsend = fastsend;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public String getImage()
|
||||
{
|
||||
return image;
|
||||
public ImageMap(Map<?, ?> map) {
|
||||
this.filename = map.get("image").toString();
|
||||
this.x = (Integer) map.get("x");
|
||||
this.y = (Integer) map.get("y");
|
||||
this.scale = (Double) map.get("scale");
|
||||
}
|
||||
|
||||
public int getX()
|
||||
{
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("image", filename);
|
||||
map.put("x", x);
|
||||
map.put("y", y);
|
||||
map.put("scale", scale);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY()
|
||||
{
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public boolean isFastSend()
|
||||
{
|
||||
return fastsend;
|
||||
}
|
||||
|
||||
public double getScale()
|
||||
{
|
||||
public double getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
public boolean isSimilar(String file, int x2, int y2, double d)
|
||||
{
|
||||
if (!getImage().equalsIgnoreCase(file))
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((filename == null) ? 0 : filename.hashCode());
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(scale);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
result = prime * result + x;
|
||||
result = prime * result + y;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!(obj instanceof ImageMap))
|
||||
return false;
|
||||
if (getX() != x2)
|
||||
ImageMap other = (ImageMap) obj;
|
||||
if (filename == null) {
|
||||
if (other.filename != null)
|
||||
return false;
|
||||
}
|
||||
else if (!filename.equals(other.filename))
|
||||
return false;
|
||||
if (getY() != y2)
|
||||
if (Double.doubleToLongBits(scale) != Double.doubleToLongBits(other.scale))
|
||||
return false;
|
||||
|
||||
double diff = d - getScale();
|
||||
return (diff > -0.0001 && diff < 0.0001);
|
||||
if (x != other.x)
|
||||
return false;
|
||||
if (y != other.y)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ImageMapCommand implements TabExecutor
|
||||
{
|
||||
private ImageMaps plugin;
|
||||
|
||||
public ImageMapCommand(ImageMaps plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args)
|
||||
{
|
||||
switch (args.length)
|
||||
{
|
||||
case 1:
|
||||
return getMatches(args[0], new File(plugin.getDataFolder(), "images").list());
|
||||
case 2:
|
||||
return Arrays.asList("scale", "true", "false", "reload", "download", "info");
|
||||
case 3:
|
||||
if (args[2].equals("true") || args[2].equals("false"))
|
||||
return Arrays.asList("scale");
|
||||
break;
|
||||
case 5:
|
||||
if (args[2].equals("scale"))
|
||||
return Arrays.asList("true", "false");
|
||||
break;
|
||||
default:
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
|
||||
{
|
||||
if (!sender.hasPermission("imagemaps.use"))
|
||||
return true;
|
||||
|
||||
if (args.length < 1)
|
||||
return false;
|
||||
|
||||
String filename=args[0];
|
||||
for (int i = 0; i < filename.length(); i++) {
|
||||
if (filename.charAt(i) == '/'
|
||||
|| filename.charAt(i) == '\\'
|
||||
|| filename.charAt(i) == ':') {
|
||||
sender.sendMessage("Sorry, this filename isn't allowed");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(args.length >= 2 && args[1].equalsIgnoreCase("reload"))
|
||||
{
|
||||
plugin.reloadImage(args[0]);
|
||||
sender.sendMessage("Image " + args[0] + " reloaded!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length >= 2 && args[1].equals("info")) {
|
||||
BufferedImage image=plugin.loadImage(args[0]);
|
||||
if (image == null) {
|
||||
sender.sendMessage("Error getting this image, please consult server logs");
|
||||
return true;
|
||||
}
|
||||
int tileWidth = (image.getWidth() + ImageMaps.MAP_WIDTH - 1) / ImageMaps.MAP_WIDTH;
|
||||
int tileHeight = (image.getHeight() + ImageMaps.MAP_HEIGHT - 1) / ImageMaps.MAP_HEIGHT;
|
||||
|
||||
sender.sendMessage(String.format("This image is %d by %d tiles (%d by %d pixels).", tileWidth, tileHeight, image.getWidth(), image.getHeight()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length >= 2 && args[1].equals("download")) {
|
||||
if (sender.hasPermission("imagemaps.download")) {
|
||||
plugin.appendDownloadTask(new ImageDownloadTask(plugin, args[2], args[0], sender));
|
||||
}
|
||||
else {
|
||||
sender.sendMessage("You don't have download permission");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage("You need to be a player to do that");
|
||||
return true;
|
||||
}
|
||||
|
||||
BufferedImage image=plugin.loadImage(args[0]);
|
||||
if (image == null) {
|
||||
sender.sendMessage("Error getting this image, please consult server logs");
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean fastsend = false;
|
||||
int tilesx = 0;
|
||||
int tilesy = 0;
|
||||
|
||||
for (int i=1; i<args.length; i++) {
|
||||
if (args[i].equalsIgnoreCase("true")) {
|
||||
fastsend=true;
|
||||
}
|
||||
else if (args[i].equalsIgnoreCase("false")) {
|
||||
fastsend=false;
|
||||
}
|
||||
else if (args[i].equalsIgnoreCase("scale") && i+2<args.length) {
|
||||
try {
|
||||
tilesx=Integer.parseInt(args[i + 1]);
|
||||
tilesy=Integer.parseInt(args[i + 2]);
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
tilesx = tilesy = 0;
|
||||
}
|
||||
if (tilesx < 0 || tilesy < 0) {
|
||||
sender.sendMessage("Need to pass two integers to scale");
|
||||
return true;
|
||||
}
|
||||
i += 2;
|
||||
} else {
|
||||
sender.sendMessage("ignoring unknown parameter " + args[i] + " (continuing)");
|
||||
}
|
||||
}
|
||||
|
||||
double scalex = tilesx * 128.0 / image.getWidth();
|
||||
double scaley = tilesy * 128.0 / image.getHeight();
|
||||
double finalScale;
|
||||
|
||||
if (scalex == 0 && scaley == 0)
|
||||
finalScale = 1.0;
|
||||
else if (scalex == 0)
|
||||
finalScale = scaley;
|
||||
else if (scaley == 0)
|
||||
finalScale = scalex;
|
||||
else
|
||||
finalScale = Math.min(scalex, scaley);
|
||||
|
||||
plugin.startPlacing((Player) sender, args[0], fastsend, finalScale);
|
||||
|
||||
int width = (int) Math.ceil((double) image.getWidth() / (double) ImageMaps.MAP_WIDTH * finalScale - 0.0001);
|
||||
int height = (int) Math.ceil((double) image.getHeight() / (double) ImageMaps.MAP_WIDTH * finalScale - 0.0001);
|
||||
|
||||
sender.sendMessage(String.format("Started placing of %s, which needs a %d by %d area.", args[0], width, height));
|
||||
sender.sendMessage("Rightclick on the block, that should be the upper left corner.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all values of a String array which start with a given String
|
||||
*
|
||||
* @param value the given String
|
||||
* @param list the array
|
||||
* @return a List of all matches
|
||||
*/
|
||||
public static List<String> getMatches(String value, String[] list)
|
||||
{
|
||||
List<String> result = new LinkedList<>();
|
||||
|
||||
for (String str : list)
|
||||
if (str.startsWith(value))
|
||||
result.add(str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import de.craftlancer.core.command.CommandHandler;
|
||||
|
||||
public class ImageMapCommandHandler extends CommandHandler {
|
||||
public ImageMapCommandHandler(ImageMaps plugin) {
|
||||
super(plugin);
|
||||
registerSubCommand("download", new ImageMapDownloadCommand(plugin));
|
||||
registerSubCommand("place", new ImageMapPlaceCommand(plugin));
|
||||
registerSubCommand("info", new ImageMapInfoCommand(plugin));
|
||||
registerSubCommand("list", new ImageMapListCommand(plugin));
|
||||
registerSubCommand("reload", new ImageMapReloadCommand(plugin));
|
||||
registerSubCommand("help", new ImageMapHelpCommand(plugin, getCommands()), "?");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import de.craftlancer.core.LambdaRunnable;
|
||||
import de.craftlancer.core.util.MessageLevel;
|
||||
import de.craftlancer.core.util.MessageUtil;
|
||||
|
||||
public class ImageMapDownloadCommand extends ImageMapSubCommand {
|
||||
|
||||
public ImageMapDownloadCommand(ImageMaps plugin) {
|
||||
super("imagemaps.download", plugin, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (!checkSender(sender)) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.length < 3) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name and a download link.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String filename = args[1];
|
||||
String url = args[2];
|
||||
|
||||
if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Filename contains illegal character.");
|
||||
return null;
|
||||
}
|
||||
|
||||
new LambdaRunnable(() -> download(sender, url, filename)).runTaskAsynchronously(plugin);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void download(CommandSender sender, String input, String filename) {
|
||||
try {
|
||||
URL srcURL = new URL(input);
|
||||
|
||||
if (!srcURL.getProtocol().startsWith("http")) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Download URL is not valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
URLConnection connection = srcURL.openConnection();
|
||||
|
||||
if (!(connection instanceof HttpURLConnection)) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Download URL is not valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (((HttpURLConnection) connection).getResponseCode() != 200) {
|
||||
MessageUtil.sendMessage(getPlugin(),
|
||||
sender,
|
||||
MessageLevel.WARNING,
|
||||
String.format("Download failed, HTTP Error code %d.", ((HttpURLConnection) connection).getResponseCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
String mimeType = ((HttpURLConnection) connection).getHeaderField("Content-type");
|
||||
if (!(mimeType.startsWith("image/"))) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, String.format("Download is a %s file, not image.", mimeType));
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream str = connection.getInputStream()) {
|
||||
Files.copy(str, new File(plugin.getDataFolder(), "images" + File.separatorChar + filename).toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Download complete.");
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Malformatted URL");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.ERROR, "An IO Exception happened, see server log");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(CommandSender sender) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Downloads an image from an URL.");
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap download <filename> <sourceURL>");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import de.craftlancer.core.command.HelpCommand;
|
||||
import de.craftlancer.core.command.SubCommand;
|
||||
import de.craftlancer.core.util.MessageLevel;
|
||||
import de.craftlancer.core.util.MessageUtil;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
public class ImageMapHelpCommand extends HelpCommand {
|
||||
|
||||
public ImageMapHelpCommand(Plugin plugin, Map<String, SubCommand> map) {
|
||||
super("imagemaps.help", plugin, map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(CommandSender sender) {
|
||||
if (((ImageMaps) getPlugin()).isInvisibilitySupported())
|
||||
MessageUtil.sendMessage(getPlugin(),
|
||||
sender,
|
||||
MessageLevel.NORMAL,
|
||||
buildMessage("/imagemap place <filename> [frameVisible] [frameFixed] [size]", " - starts image placement"));
|
||||
else
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap place <filename> [size]", " - starts image placement"));
|
||||
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap download <filename> <sourceURL>", " - downloads an image"));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap info <filename>", " - displays image info"));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap reload <filename>", " - reloads an image from disk"));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap list [page]", " - lists all files in the images folder"));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, buildMessage("/imagemap help [command]", " - shows help"));
|
||||
}
|
||||
|
||||
private static BaseComponent buildMessage(String str1, String str2) {
|
||||
BaseComponent combined = new TextComponent();
|
||||
|
||||
BaseComponent comp1 = new TextComponent(str1);
|
||||
comp1.setColor(ChatColor.WHITE);
|
||||
BaseComponent comp2 = new TextComponent(str2);
|
||||
comp2.setColor(ChatColor.GRAY);
|
||||
|
||||
combined.addExtra(comp1);
|
||||
combined.addExtra(comp2);
|
||||
|
||||
return combined;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import de.craftlancer.core.Utils;
|
||||
import de.craftlancer.core.util.MessageLevel;
|
||||
import de.craftlancer.core.util.MessageUtil;
|
||||
import de.craftlancer.core.util.Tuple;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
public class ImageMapInfoCommand extends ImageMapSubCommand {
|
||||
|
||||
public ImageMapInfoCommand(ImageMaps plugin) {
|
||||
super("imagemaps.info", plugin, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (!checkSender(sender)) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String filename = args[1];
|
||||
BufferedImage image = getPlugin().getImage(filename);
|
||||
|
||||
if (image == null) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "No image with this name exists.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Tuple<Integer, Integer> size = getPlugin().getImageSize(filename, null);
|
||||
BaseComponent reloadAction = new TextComponent("[Reload]");
|
||||
reloadAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/imagemap reload " + filename));
|
||||
reloadAction.setColor(ChatColor.GOLD);
|
||||
BaseComponent placeAction = new TextComponent("[Place]");
|
||||
placeAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/imagemap place " + filename));
|
||||
placeAction.setColor(ChatColor.GOLD);
|
||||
|
||||
BaseComponent actions = new TextComponent("Action: ");
|
||||
actions.addExtra(reloadAction);
|
||||
actions.addExtra(" ");
|
||||
actions.addExtra(placeAction);
|
||||
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Image Information: ");
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, String.format("File Name: %s", filename));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, String.format("Resolution: %dx%d", image.getWidth(), image.getHeight()));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, String.format("Ingame Size: %dx%d", size.getKey(), size.getValue()));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, actions);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(CommandSender sender) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Displays information about an image.");
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap info <filename>");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
if (args.length == 2)
|
||||
return Utils.getMatches(args[1], new File(plugin.getDataFolder(), "images").list());
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.io.File;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import de.craftlancer.core.Utils;
|
||||
import de.craftlancer.core.util.MessageLevel;
|
||||
import de.craftlancer.core.util.MessageUtil;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
public class ImageMapListCommand extends ImageMapSubCommand {
|
||||
|
||||
public ImageMapListCommand(ImageMaps plugin) {
|
||||
super("imagemaps.list", plugin, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (!checkSender(sender)) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command.");
|
||||
return null;
|
||||
}
|
||||
|
||||
long page = args.length >= 2 ? Utils.parseIntegerOrDefault(args[1], 0) - 1 : 0;
|
||||
|
||||
String[] fileList = new File(plugin.getDataFolder(), "images").list();
|
||||
|
||||
MessageUtil.sendMessage(plugin,
|
||||
sender,
|
||||
MessageLevel.INFO,
|
||||
String.format("Image List %d/%d", page + 1, (int) Math.ceil((double) fileList.length / Utils.ELEMENTS_PER_PAGE)));
|
||||
|
||||
// TODO alternating color
|
||||
Utils.paginate(fileList, page).forEach(filename -> {
|
||||
BaseComponent infoAction = new TextComponent("[Info]");
|
||||
infoAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/imagemap info " + filename));
|
||||
infoAction.setColor(ChatColor.GOLD);
|
||||
BaseComponent reloadAction = new TextComponent("[Reload]");
|
||||
reloadAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/imagemap reload " + filename));
|
||||
reloadAction.setColor(ChatColor.GOLD);
|
||||
BaseComponent placeAction = new TextComponent("[Place]");
|
||||
placeAction.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/imagemap place " + filename));
|
||||
placeAction.setColor(ChatColor.GOLD);
|
||||
|
||||
BaseComponent message = new TextComponent(filename);
|
||||
message.addExtra(" ");
|
||||
message.addExtra(infoAction);
|
||||
message.addExtra(" ");
|
||||
message.addExtra(reloadAction);
|
||||
message.addExtra(" ");
|
||||
message.addExtra(placeAction);
|
||||
|
||||
MessageUtil.sendMessage(plugin, sender, MessageLevel.NORMAL, message);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(CommandSender sender) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Lists all files in the images folder.");
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap list [page]");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
|
||||
import de.craftlancer.core.Utils;
|
||||
import de.craftlancer.core.util.MessageLevel;
|
||||
import de.craftlancer.core.util.MessageUtil;
|
||||
import de.craftlancer.core.util.Tuple;
|
||||
|
||||
/*
|
||||
* imagemap place <image> <scale> <isVisible> <isFixed>
|
||||
*/
|
||||
public class ImageMapPlaceCommand extends ImageMapSubCommand {
|
||||
|
||||
public ImageMapPlaceCommand(ImageMaps plugin) {
|
||||
super("imagemaps.place", plugin, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (!checkSender(sender)) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String filename = args[1];
|
||||
boolean isInvisible = false;
|
||||
boolean isFixed = false;
|
||||
Tuple<Integer, Integer> scale;
|
||||
|
||||
if (getPlugin().isInvisibilitySupported()) {
|
||||
isInvisible = args.length >= 3 && Boolean.parseBoolean(args[2]);
|
||||
isFixed = args.length >= 4 && Boolean.parseBoolean(args[3]);
|
||||
scale = args.length >= 5 ? parseScale(args[4]) : new Tuple<>(-1, -1);
|
||||
}
|
||||
else
|
||||
scale = args.length >= 3 ? parseScale(args[2]) : new Tuple<>(-1, -1);
|
||||
|
||||
if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Filename contains illegal character.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!getPlugin().hasImage(filename)) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "No image with this name exists.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
player.setMetadata(ImageMaps.PLACEMENT_METADATA, new FixedMetadataValue(getPlugin(), new PlacementData(filename, isInvisible, isFixed, scale)));
|
||||
|
||||
Tuple<Integer, Integer> size = getPlugin().getImageSize(filename, scale);
|
||||
MessageUtil.sendMessage(getPlugin(),
|
||||
sender,
|
||||
MessageLevel.NORMAL,
|
||||
String.format("Started placing of %s. It needs a %d by %d area.", args[1], size.getKey(), size.getValue()));
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Rightclick on the block, that should be the upper left corner.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(CommandSender sender) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Starts placing an image.");
|
||||
|
||||
if (getPlugin().isInvisibilitySupported())
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap place <filename> [frameInvisible] [frameFixed] [size]");
|
||||
else
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap place <filename> [size]");
|
||||
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Size format: XxY -> 5x2, use -1 for default");
|
||||
MessageUtil.sendMessage(getPlugin(),
|
||||
sender,
|
||||
MessageLevel.NORMAL,
|
||||
"The plugin will scale the map to not be larger than the given size while maintaining the aspect ratio.");
|
||||
MessageUtil.sendMessage(getPlugin(),
|
||||
sender,
|
||||
MessageLevel.NORMAL,
|
||||
"It's recommended to avoid the size function in favor of using properly sized source images.");
|
||||
}
|
||||
|
||||
private static Tuple<Integer, Integer> parseScale(String string) {
|
||||
String[] tmp = string.split("x");
|
||||
|
||||
if (tmp.length < 2)
|
||||
return new Tuple<>(-1, -1);
|
||||
|
||||
return new Tuple<>(Utils.parseIntegerOrDefault(tmp[0], -1), Utils.parseIntegerOrDefault(tmp[1], -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
if (args.length > 2 && !getPlugin().isInvisibilitySupported())
|
||||
return Collections.emptyList();
|
||||
|
||||
switch (args.length) {
|
||||
case 2:
|
||||
return Utils.getMatches(args[1], new File(plugin.getDataFolder(), "images").list());
|
||||
case 3:
|
||||
return Utils.getMatches(args[2], Arrays.asList("true", "false"));
|
||||
case 4:
|
||||
return Utils.getMatches(args[3], Arrays.asList("true", "false"));
|
||||
default:
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import de.craftlancer.core.Utils;
|
||||
import de.craftlancer.core.util.MessageLevel;
|
||||
import de.craftlancer.core.util.MessageUtil;
|
||||
|
||||
public class ImageMapReloadCommand extends ImageMapSubCommand {
|
||||
|
||||
public ImageMapReloadCommand(ImageMaps plugin) {
|
||||
super("imagemap.reload", plugin, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (!checkSender(sender)) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You can't run this command.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "You must specify a file name.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String filename = args[1];
|
||||
|
||||
if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.WARNING, "Filename contains illegal character.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (getPlugin().reloadImage(filename))
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Image reloaded.");
|
||||
else
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Image couldn't be reloaded (does it exist?).");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(CommandSender sender) {
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Reloads an image from disk, to be used when the file changed.");
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.NORMAL, "Avoid resolution changes, since they won't be scaled.");
|
||||
MessageUtil.sendMessage(getPlugin(), sender, MessageLevel.INFO, "Usage: /imagemap reload <filename>");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
if (args.length == 2)
|
||||
return Utils.getMatches(args[1], new File(plugin.getDataFolder(), "images").list());
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
|
@ -9,46 +9,47 @@ import org.bukkit.map.MapCanvas;
|
|||
import org.bukkit.map.MapRenderer;
|
||||
import org.bukkit.map.MapView;
|
||||
|
||||
public class ImageMapRenderer extends MapRenderer
|
||||
{
|
||||
public class ImageMapRenderer extends MapRenderer {
|
||||
private BufferedImage image = null;
|
||||
private boolean first = true;
|
||||
|
||||
public ImageMapRenderer(BufferedImage image, int x1, int y1, double scale)
|
||||
{
|
||||
recalculateInput(image, x1, y1, scale);
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final double scale;
|
||||
|
||||
public ImageMapRenderer(BufferedImage image, int x, int y, double scale) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.scale = scale;
|
||||
recalculateInput(image);
|
||||
}
|
||||
|
||||
public void recalculateInput(BufferedImage input, int x1, int y1, double scale)
|
||||
{
|
||||
int x2 = ImageMaps.MAP_WIDTH;
|
||||
int y2 = ImageMaps.MAP_HEIGHT;
|
||||
|
||||
if (x1 > input.getWidth()* scale + 0.001 || y1 > input.getHeight() * scale + 0.001)
|
||||
public void recalculateInput(BufferedImage input) {
|
||||
if (x * ImageMaps.MAP_WIDTH > input.getWidth() * scale || y * ImageMaps.MAP_HEIGHT > input.getHeight() * scale)
|
||||
return;
|
||||
|
||||
if (x1 + x2 >= input.getWidth() * scale)
|
||||
x2 = (int)(input.getWidth() * scale) - x1;
|
||||
int x1 = (int) Math.round(x * ImageMaps.MAP_WIDTH / scale);
|
||||
int y1 = (int) Math.round(y * ImageMaps.MAP_HEIGHT / scale);
|
||||
|
||||
if (y1 + y2 >= input.getHeight() * scale)
|
||||
y2 = (int)(input.getHeight() * scale) - y1;
|
||||
|
||||
this.image = input.getSubimage((int)(x1/scale), (int)(y1/scale), (int)(x2/scale), (int)(y2/scale));
|
||||
if (scale != 1.0) {
|
||||
int x2 = (int) Math.round(Math.min(input.getWidth(), ((x + 1) * ImageMaps.MAP_WIDTH / scale)));
|
||||
int y2 = (int) Math.round(Math.min(input.getHeight(), ((y + 1) * ImageMaps.MAP_HEIGHT / scale)));
|
||||
|
||||
this.image = input.getSubimage(x1, y1, x2 - x1, y2 - y1);
|
||||
|
||||
if (scale != 1D) {
|
||||
BufferedImage resized = new BufferedImage(ImageMaps.MAP_WIDTH, ImageMaps.MAP_HEIGHT, input.getType());
|
||||
AffineTransform at = new AffineTransform();
|
||||
at.scale(scale, scale);
|
||||
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
|
||||
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
|
||||
this.image = scaleOp.filter(this.image, resized);
|
||||
}
|
||||
|
||||
first = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MapView view, MapCanvas canvas, Player player)
|
||||
{
|
||||
if (image != null && first)
|
||||
{
|
||||
public void render(MapView view, MapCanvas canvas, Player player) {
|
||||
if (image != null && first) {
|
||||
canvas.drawImage(0, 0, image);
|
||||
first = false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import de.craftlancer.core.command.SubCommand;
|
||||
|
||||
public abstract class ImageMapSubCommand extends SubCommand {
|
||||
|
||||
public ImageMapSubCommand(String permission, ImageMaps plugin, boolean console) {
|
||||
super(permission, plugin, console);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageMaps getPlugin() {
|
||||
return (ImageMaps) super.getPlugin();
|
||||
}
|
||||
}
|
|
@ -3,328 +3,497 @@ package de.craftlancer.imagemaps;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Rotation;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Hanging;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.MapMeta;
|
||||
import org.bukkit.map.MapRenderer;
|
||||
import org.bukkit.map.MapView;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import de.craftlancer.core.LambdaRunnable;
|
||||
import de.craftlancer.core.SemanticVersion;
|
||||
import de.craftlancer.core.Utils;
|
||||
import de.craftlancer.core.util.MessageLevel;
|
||||
import de.craftlancer.core.util.MessageUtil;
|
||||
import de.craftlancer.core.util.Tuple;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
public class ImageMaps extends JavaPlugin implements Listener {
|
||||
private static final String CONFIG_VERSION_KEY = "storageVersion";
|
||||
private static final int CONFIG_VERSION = 1;
|
||||
private static final long AUTOSAVE_PERIOD = 18000L; // 15 minutes
|
||||
|
||||
public static final String PLACEMENT_METADATA = "imagemaps.place";
|
||||
|
||||
public static final int MAP_WIDTH = 128;
|
||||
public static final int MAP_HEIGHT = 128;
|
||||
|
||||
private static final String IMAGES_DIR = "images";
|
||||
|
||||
private Map<UUID, PlacingCacheEntry> placing = new HashMap<>();
|
||||
private Map<Integer, ImageMap> maps = new HashMap<>();
|
||||
private Map<String, BufferedImage> images = new HashMap<>();
|
||||
private List<Integer> sendList = new ArrayList<>();
|
||||
private FastSendTask sendTask;
|
||||
private List<ImageDownloadTask> downloadTasks;
|
||||
private Map<String, BufferedImage> imageCache = new HashMap<>();
|
||||
private Map<ImageMap, Integer> maps = new HashMap<>();
|
||||
|
||||
static {
|
||||
ConfigurationSerialization.registerClass(ImageMap.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
BaseComponent prefix = new TextComponent(
|
||||
new ComponentBuilder("[").color(ChatColor.GRAY).append("ImageMaps").color(ChatColor.AQUA).append("]").color(ChatColor.GRAY).create());
|
||||
MessageUtil.registerPlugin(this, prefix, ChatColor.GRAY, ChatColor.YELLOW, ChatColor.RED, ChatColor.DARK_RED, ChatColor.DARK_AQUA);
|
||||
|
||||
if (!new File(getDataFolder(), IMAGES_DIR).exists())
|
||||
new File(getDataFolder(), IMAGES_DIR).mkdirs();
|
||||
|
||||
int sendPerTicks = getConfig().getInt("sendPerTicks", 20);
|
||||
int mapsPerSend = getConfig().getInt("mapsPerSend", 8);
|
||||
getCommand("imagemap").setExecutor(new ImageMapCommandHandler(this));
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
|
||||
loadMaps();
|
||||
getCommand("imagemap").setExecutor(new ImageMapCommand(this));
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
sendTask = new FastSendTask(this, mapsPerSend);
|
||||
getServer().getPluginManager().registerEvents(sendTask, this);
|
||||
sendTask.runTaskTimer(this, sendPerTicks, sendPerTicks);
|
||||
downloadTasks=new ArrayList<>();
|
||||
new ImageDownloadCompleteNotifier(this).runTaskTimer(this, 20, 20);
|
||||
|
||||
new LambdaRunnable(this::saveMaps).runTaskTimer(this, AUTOSAVE_PERIOD, AUTOSAVE_PERIOD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
saveMaps();
|
||||
getServer().getScheduler().cancelTasks(this);
|
||||
}
|
||||
|
||||
public List<Integer> getFastSendList() {
|
||||
return sendList;
|
||||
}
|
||||
|
||||
public void startPlacing(Player p, String image, boolean fastsend, double scale) {
|
||||
placing.put(p.getUniqueId(), new PlacingCacheEntry(image, fastsend, scale));
|
||||
}
|
||||
|
||||
public boolean placeImage(Player player, Block block, BlockFace face, PlacingCacheEntry cache) {
|
||||
int xMod = 0;
|
||||
int zMod = 0;
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onToggleFrameProperty(PlayerInteractEntityEvent event) {
|
||||
if (!isInvisibilitySupported())
|
||||
return;
|
||||
|
||||
switch (face) {
|
||||
case EAST:
|
||||
zMod = -1;
|
||||
break;
|
||||
case WEST:
|
||||
zMod = 1;
|
||||
break;
|
||||
case SOUTH:
|
||||
xMod = 1;
|
||||
break;
|
||||
case NORTH:
|
||||
xMod = -1;
|
||||
break;
|
||||
default:
|
||||
getLogger().severe("Someone tried to create an image with an invalid block facing");
|
||||
return false;
|
||||
if (event.getRightClicked().getType() != EntityType.ITEM_FRAME)
|
||||
return;
|
||||
|
||||
ItemFrame frame = (ItemFrame) event.getRightClicked();
|
||||
Player p = event.getPlayer();
|
||||
|
||||
if (p.getInventory().getItemInMainHand().getType() != Material.WOODEN_HOE)
|
||||
return;
|
||||
|
||||
if (p.isSneaking() && p.hasPermission("imagemaps.toggleFixed")) {
|
||||
frame.setFixed(!frame.isFixed());
|
||||
MessageUtil.sendMessage(this, p, MessageLevel.INFO, String.format("Frame set to %s.", frame.isFixed() ? "fixed" : "unfixed"));
|
||||
}
|
||||
else if (p.hasPermission("imagemaps.toggleVisible")) {
|
||||
frame.setVisible(!frame.isVisible());
|
||||
MessageUtil.sendMessage(this, p, MessageLevel.INFO, String.format("Frame set to %s.", frame.isVisible() ? "visible" : "invisible"));
|
||||
}
|
||||
|
||||
BufferedImage image = loadImage(cache.getImage());
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
public boolean isInvisibilitySupported() {
|
||||
SemanticVersion version = Utils.getMCVersion();
|
||||
return version.getMajor() >= 1 && version.getMinor() >= 16;
|
||||
}
|
||||
|
||||
public boolean isUpDownFaceSupported() {
|
||||
SemanticVersion version = Utils.getMCVersion();
|
||||
|
||||
if (image == null) {
|
||||
getLogger().severe("Someone tried to create an image with an invalid file!");
|
||||
if (version.getMajor() < 1)
|
||||
return false;
|
||||
}
|
||||
if (version.getMajor() == 1 && version.getMinor() == 14 && version.getRevision() >= 4)
|
||||
return true;
|
||||
return version.getMinor() > 14;
|
||||
}
|
||||
|
||||
private void saveMaps() {
|
||||
FileConfiguration config = new YamlConfiguration();
|
||||
config.set(CONFIG_VERSION_KEY, CONFIG_VERSION);
|
||||
config.set("maps", maps.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey)));
|
||||
|
||||
Block b = block.getRelative(face);
|
||||
|
||||
int width = (int) Math.ceil((double) image.getWidth() / (double) MAP_WIDTH * cache.getScale() - 0.0001);
|
||||
int height = (int) Math.ceil((double) image.getHeight() / (double) MAP_HEIGHT * cache.getScale() - 0.0001);
|
||||
|
||||
ImagePlaceEvent event = new ImagePlaceEvent(player, block, face, width, height, cache);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if(event.isCancelled())
|
||||
return false;
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (!block.getRelative(x * xMod, -y, x * zMod).getType().isSolid())
|
||||
return false;
|
||||
|
||||
if (block.getRelative(x * xMod - zMod, -y, x * zMod + xMod).getType().isSolid())
|
||||
return false;
|
||||
BukkitRunnable saveTask = new LambdaRunnable(() -> {
|
||||
try {
|
||||
config.save(new File(getDataFolder(), "maps.yml"));
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
for (int x = 0; x < width; x++)
|
||||
for (int y = 0; y < height; y++)
|
||||
setItemFrame(b.getRelative(x * xMod, -y, x * zMod), image, face, x * MAP_WIDTH, y * MAP_HEIGHT, cache);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// God forgive me, but I actually HAVE to catch this...
|
||||
getLogger().info("Some error occured while placing the ItemFrames. This can for example happen when some existing ItemFrame/Hanging Entity is blocking.");
|
||||
getLogger().info("Unfortunatly this is caused be the way Minecraft/CraftBukkit handles the spawning of Entities.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onInteract(PlayerInteractEvent e) {
|
||||
if (!placing.containsKey(e.getPlayer().getUniqueId()))
|
||||
return;
|
||||
|
||||
if (e.getAction() == Action.RIGHT_CLICK_AIR) {
|
||||
e.getPlayer().sendMessage("Placing cancelled");
|
||||
placing.remove(e.getPlayer().getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.getAction() != Action.RIGHT_CLICK_BLOCK)
|
||||
return;
|
||||
|
||||
if (!placeImage(e.getPlayer(), e.getClickedBlock(), e.getBlockFace(), placing.get(e.getPlayer().getUniqueId())))
|
||||
e.getPlayer().sendMessage(ChatColor.RED + "Can't place the image here!\nMake sure the area is large enough, unobstructed and without pre-existing hanging entities.");
|
||||
if (isEnabled())
|
||||
saveTask.runTaskAsynchronously(this);
|
||||
else
|
||||
saveMaps();
|
||||
|
||||
e.setCancelled(true);
|
||||
placing.remove(e.getPlayer().getUniqueId());
|
||||
|
||||
saveTask.run();
|
||||
}
|
||||
|
||||
private void setItemFrame(Block bb, BufferedImage image, BlockFace face, int x, int y, PlacingCacheEntry cache) {
|
||||
ItemFrame i = null;
|
||||
private void loadMaps() {
|
||||
Configuration config = YamlConfiguration.loadConfiguration(new File(getDataFolder(), "maps.yml"));
|
||||
int version = config.getInt(CONFIG_VERSION_KEY, -1);
|
||||
|
||||
i = bb.getWorld().spawn(bb.getLocation(), ItemFrame.class);
|
||||
if (version == -1)
|
||||
config = convertLegacyMaps(config);
|
||||
|
||||
i.setFacingDirection(face, false);
|
||||
ConfigurationSection section = config.getConfigurationSection("maps");
|
||||
if (section != null)
|
||||
section.getValues(false).forEach((a, b) -> {
|
||||
int id = Integer.parseInt(a);
|
||||
ImageMap imageMap = (ImageMap) b;
|
||||
@SuppressWarnings("deprecation")
|
||||
MapView map = Bukkit.getMap(id);
|
||||
BufferedImage image = getImage(imageMap.getFilename());
|
||||
|
||||
if (image == null) {
|
||||
getLogger().warning(() -> "Image file " + image + " not found. Removing map!");
|
||||
return;
|
||||
}
|
||||
|
||||
map.addRenderer(new ImageMapRenderer(image, imageMap.getX(), imageMap.getY(), imageMap.getScale()));
|
||||
maps.put(imageMap, id);
|
||||
});
|
||||
}
|
||||
|
||||
private Configuration convertLegacyMaps(Configuration config) {
|
||||
getLogger().info("Converting maps from Version <1.0");
|
||||
|
||||
ItemStack item = getMapItem(cache.getImage(), x, y, image, cache.getScale());
|
||||
i.setItem(item);
|
||||
Map<Integer, ImageMap> map = new HashMap<>();
|
||||
|
||||
int id = ((MapMeta) item.getItemMeta()).getMapId();
|
||||
|
||||
if (cache.isFastSend() && !sendList.contains(id)) {
|
||||
sendList.add(id);
|
||||
sendTask.addToQueue(id);
|
||||
for (String key : config.getKeys(false)) {
|
||||
int id = Integer.parseInt(key);
|
||||
String image = config.getString(key + ".image");
|
||||
int x = config.getInt(key + ".x") / MAP_WIDTH;
|
||||
int y = config.getInt(key + ".y") / MAP_HEIGHT;
|
||||
double scale = config.getDouble(key + ".scale", 1.0);
|
||||
map.put(id, new ImageMap(image, x, y, scale));
|
||||
}
|
||||
|
||||
maps.put(id, new ImageMap(cache.getImage(), x, y, sendList.contains(id), cache.getScale()));
|
||||
config = new YamlConfiguration();
|
||||
config.set(CONFIG_VERSION_KEY, CONFIG_VERSION);
|
||||
config.createSection("maps", map);
|
||||
return config;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private ItemStack getMapItem(String file, int x, int y, BufferedImage image, double scale) {
|
||||
ItemStack item = new ItemStack(Material.MAP);
|
||||
public boolean hasImage(String filename) {
|
||||
if (imageCache.containsKey(filename.toLowerCase()))
|
||||
return true;
|
||||
|
||||
for (Entry<Integer, ImageMap> entry : maps.entrySet()) {
|
||||
if (entry.getValue().isSimilar(file, x, y, scale)) {
|
||||
MapMeta meta = (MapMeta) item.getItemMeta();
|
||||
meta.setMapId(entry.getKey());
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
File file = new File(getDataFolder(), IMAGES_DIR + File.separatorChar + filename);
|
||||
|
||||
return file.exists() && getImage(filename) != null;
|
||||
}
|
||||
|
||||
public BufferedImage getImage(String filename) {
|
||||
if (filename.contains("/") || filename.contains("\\") || filename.contains(":")) {
|
||||
getLogger().warning("Someone tried to get image with illegal characters in file name.");
|
||||
return null;
|
||||
}
|
||||
|
||||
MapView map = getServer().createMap(getServer().getWorlds().get(0));
|
||||
for (MapRenderer r : map.getRenderers())
|
||||
map.removeRenderer(r);
|
||||
|
||||
map.addRenderer(new ImageMapRenderer(image, x, y, scale));
|
||||
|
||||
MapMeta meta = ((MapMeta) item.getItemMeta());
|
||||
meta.setMapId(map.getId());
|
||||
item.setItemMeta(meta);
|
||||
if (imageCache.containsKey(filename.toLowerCase()))
|
||||
return imageCache.get(filename.toLowerCase());
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public BufferedImage loadImage(String file) {
|
||||
if (images.containsKey(file))
|
||||
return images.get(file);
|
||||
|
||||
File f = new File(getDataFolder(), IMAGES_DIR + File.separatorChar + file);
|
||||
File file = new File(getDataFolder(), IMAGES_DIR + File.separatorChar + filename);
|
||||
BufferedImage image = null;
|
||||
|
||||
if (!f.exists())
|
||||
if (!file.exists())
|
||||
return null;
|
||||
|
||||
try {
|
||||
image = ImageIO.read(f);
|
||||
images.put(file, image);
|
||||
image = ImageIO.read(file);
|
||||
imageCache.put(filename.toLowerCase(), image);
|
||||
}
|
||||
catch (IOException e) {
|
||||
getLogger().log(Level.SEVERE, "Error while trying to read image " + f.getName(), e);
|
||||
getLogger().log(Level.SEVERE, String.format("Error while trying to read image %s.", file.getName()), e);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void loadMaps() {
|
||||
File file = new File(getDataFolder(), "maps.yml");
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
Set<String> warnedFilenames=new HashSet<>();
|
||||
@EventHandler
|
||||
public void onInteract(PlayerInteractEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
for (String key : config.getKeys(false)) {
|
||||
int id = Integer.parseInt(key);
|
||||
|
||||
MapView map = getServer().getMap(id);
|
||||
|
||||
if(map == null)
|
||||
continue;
|
||||
|
||||
for (MapRenderer r : map.getRenderers())
|
||||
map.removeRenderer(r);
|
||||
|
||||
String image = config.getString(key + ".image");
|
||||
int x = config.getInt(key + ".x");
|
||||
int y = config.getInt(key + ".y");
|
||||
boolean fastsend = config.getBoolean(key + ".fastsend", false);
|
||||
double scale = config.getDouble(key + ".scale", 1.0);
|
||||
|
||||
BufferedImage bimage = loadImage(image);
|
||||
|
||||
if (bimage == null) {
|
||||
if (!warnedFilenames.contains(image)) {
|
||||
warnedFilenames.add(image);
|
||||
getLogger().warning(() -> "Image file " + image + " not found, removing this map!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fastsend)
|
||||
sendList.add(id);
|
||||
|
||||
map.addRenderer(new ImageMapRenderer(loadImage(image), x, y, scale));
|
||||
maps.put(id, new ImageMap(image, x, y, fastsend, scale));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMaps() {
|
||||
File file = new File(getDataFolder(), "maps.yml");
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
if (!player.hasMetadata(PLACEMENT_METADATA))
|
||||
return;
|
||||
|
||||
for (String key : config.getKeys(false))
|
||||
config.set(key, null);
|
||||
|
||||
for (Entry<Integer, ImageMap> e : maps.entrySet()) {
|
||||
config.set(e.getKey() + ".image", e.getValue().getImage());
|
||||
config.set(e.getKey() + ".x", e.getValue().getX());
|
||||
config.set(e.getKey() + ".y", e.getValue().getY());
|
||||
config.set(e.getKey() + ".fastsend", e.getValue().isFastSend());
|
||||
config.set(e.getKey() + ".scale", e.getValue().getScale());
|
||||
}
|
||||
|
||||
try {
|
||||
config.save(file);
|
||||
}
|
||||
catch (IOException e1) {
|
||||
getLogger().log(Level.SEVERE, "Failed to save maps.yml!", e1);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void reloadImage(String file) {
|
||||
images.remove(file);
|
||||
BufferedImage image = loadImage(file);
|
||||
|
||||
if(image == null) {
|
||||
getLogger().warning(() -> "Failed to reload image: " + file);
|
||||
if (event.getAction() == Action.RIGHT_CLICK_AIR) {
|
||||
player.removeMetadata(PLACEMENT_METADATA, this);
|
||||
MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Image placement cancelled.");
|
||||
return;
|
||||
}
|
||||
|
||||
maps.values().stream().filter(a -> a.getImage().equals(file)).forEach(imageMap -> {
|
||||
int id = ((MapMeta) getMapItem(file, imageMap.getX(), imageMap.getY(), image, imageMap.getScale()).getItemMeta()).getMapId();
|
||||
MapView map = getServer().getMap(id);
|
||||
|
||||
for (MapRenderer renderer : map.getRenderers())
|
||||
if (renderer instanceof ImageMapRenderer)
|
||||
((ImageMapRenderer) renderer).recalculateInput(image, imageMap.getX(), imageMap.getY(), imageMap.getScale());
|
||||
|
||||
sendTask.addToQueue(id);
|
||||
});
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK)
|
||||
return;
|
||||
|
||||
PlacementData data = (PlacementData) player.getMetadata(PLACEMENT_METADATA).get(0).value();
|
||||
PlacementResult result = placeImage(player, event.getClickedBlock(), event.getBlockFace(), data);
|
||||
|
||||
switch (result) {
|
||||
case INVALID_FACING:
|
||||
MessageUtil.sendMessage(this, player, MessageLevel.WARNING, "You can't place an image on this block face.");
|
||||
break;
|
||||
case INVALID_DIRECTION:
|
||||
MessageUtil.sendMessage(this, player, MessageLevel.WARNING, "Couldn't calculate how to place the map.");
|
||||
break;
|
||||
case EVENT_CANCELLED:
|
||||
MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Image placement cancelled by another plugin.");
|
||||
break;
|
||||
case INSUFFICIENT_SPACE:
|
||||
MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Map couldn't be placed, the space is blocked.");
|
||||
break;
|
||||
case INSUFFICIENT_WALL:
|
||||
MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Map couldn't be placed, the supporting wall is too small.");
|
||||
break;
|
||||
case OVERLAPPING_ENTITY:
|
||||
MessageUtil.sendMessage(this, player, MessageLevel.NORMAL, "Map couldn't be placed, there is another entity in the way.");
|
||||
break;
|
||||
case SUCCESS:
|
||||
break;
|
||||
}
|
||||
|
||||
player.removeMetadata(PLACEMENT_METADATA, this);
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
public void appendDownloadTask(ImageDownloadTask task) {
|
||||
this.downloadTasks.add(task);
|
||||
private PlacementResult placeImage(Player player, Block block, BlockFace face, PlacementData data) {
|
||||
if (!isAxisAligned(face)) {
|
||||
getLogger().severe("Someone tried to create an image with an invalid block facing");
|
||||
return PlacementResult.INVALID_FACING;
|
||||
}
|
||||
|
||||
if (face.getModY() != 0 && !isUpDownFaceSupported())
|
||||
return PlacementResult.INVALID_FACING;
|
||||
|
||||
Block b = block.getRelative(face);
|
||||
BufferedImage image = getImage(data.getFilename());
|
||||
Tuple<Integer, Integer> size = getImageSize(data.getFilename(), data.getSize());
|
||||
BlockFace widthDirection = calculateWidthDirection(player, face);
|
||||
BlockFace heightDirection = calculateHeightDirection(player, face);
|
||||
|
||||
if (widthDirection == null || heightDirection == null)
|
||||
return PlacementResult.INVALID_DIRECTION;
|
||||
|
||||
// check for space
|
||||
for (int x = 0; x < size.getKey(); x++)
|
||||
for (int y = 0; y < size.getValue(); y++) {
|
||||
Block frameBlock = b.getRelative(widthDirection, x).getRelative(heightDirection, y);
|
||||
|
||||
if (!block.getRelative(widthDirection, x).getRelative(heightDirection, y).getType().isSolid())
|
||||
return PlacementResult.INSUFFICIENT_WALL;
|
||||
if (frameBlock.getType().isSolid())
|
||||
return PlacementResult.INSUFFICIENT_SPACE;
|
||||
if (!b.getWorld().getNearbyEntities(frameBlock.getLocation().add(0.5, 0.5, 0.5), 0.5, 0.5, 0.5, a -> (a instanceof Hanging)).isEmpty())
|
||||
return PlacementResult.OVERLAPPING_ENTITY;
|
||||
}
|
||||
|
||||
ImagePlaceEvent event = new ImagePlaceEvent(player, block, widthDirection, heightDirection, size.getKey(), size.getValue(), data);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled())
|
||||
return PlacementResult.EVENT_CANCELLED;
|
||||
|
||||
// spawn item frame
|
||||
for (int x = 0; x < size.getKey(); x++)
|
||||
for (int y = 0; y < size.getValue(); y++) {
|
||||
ItemFrame frame = block.getWorld().spawn(b.getRelative(widthDirection, x).getRelative(heightDirection, y).getLocation(), ItemFrame.class);
|
||||
frame.setFacingDirection(face);
|
||||
frame.setItem(getMapItem(image, x, y, data));
|
||||
frame.setRotation(facingToRotation(heightDirection, widthDirection));
|
||||
|
||||
if (isInvisibilitySupported()) {
|
||||
frame.setFixed(data.isFixed());
|
||||
frame.setVisible(!data.isInvisible());
|
||||
}
|
||||
}
|
||||
|
||||
return PlacementResult.SUCCESS;
|
||||
}
|
||||
|
||||
public List<ImageDownloadTask> getDownloadTasks() {
|
||||
return this.downloadTasks;
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean reloadImage(String filename) {
|
||||
if (!imageCache.containsKey(filename.toLowerCase()))
|
||||
return false;
|
||||
|
||||
imageCache.remove(filename.toLowerCase());
|
||||
BufferedImage image = getImage(filename);
|
||||
|
||||
if (image == null) {
|
||||
getLogger().warning(() -> "Failed to reload image: " + filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
maps.entrySet().stream().filter(a -> a.getKey().getFilename().equalsIgnoreCase(filename)).map(a -> Bukkit.getMap(a.getValue()))
|
||||
.flatMap(a -> a.getRenderers().stream()).filter(a -> a instanceof ImageMapRenderer).forEach(a -> ((ImageMapRenderer) a).recalculateInput(image));
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private ItemStack getMapItem(BufferedImage image, int x, int y, PlacementData data) {
|
||||
ItemStack item = new ItemStack(Material.FILLED_MAP);
|
||||
|
||||
ImageMap imageMap = new ImageMap(data.getFilename(), x, y, getScale(image, data.getSize()));
|
||||
if (maps.containsKey(imageMap)) {
|
||||
MapMeta meta = (MapMeta) item.getItemMeta();
|
||||
meta.setMapId(maps.get(imageMap));
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
MapView map = getServer().createMap(getServer().getWorlds().get(0));
|
||||
map.getRenderers().forEach(map::removeRenderer);
|
||||
map.addRenderer(new ImageMapRenderer(image, x, y, getScale(image, data.getSize())));
|
||||
|
||||
MapMeta meta = ((MapMeta) item.getItemMeta());
|
||||
meta.setMapView(map);
|
||||
item.setItemMeta(meta);
|
||||
maps.put(imageMap, map.getId());
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public Tuple<Integer, Integer> getImageSize(String filename, Tuple<Integer, Integer> size) {
|
||||
BufferedImage image = getImage(filename);
|
||||
|
||||
if (image == null)
|
||||
return new Tuple<>(0, 0);
|
||||
|
||||
double finalScale = getScale(image, size);
|
||||
int finalX = (int) ((MAP_WIDTH - 1 + Math.ceil(image.getWidth() * finalScale)) / MAP_WIDTH);
|
||||
int finalY = (int) ((MAP_HEIGHT - 1 + Math.ceil(image.getHeight() * finalScale)) / MAP_HEIGHT);
|
||||
|
||||
return new Tuple<>(finalX, finalY);
|
||||
}
|
||||
|
||||
public double getScale(String filename, Tuple<Integer, Integer> size) {
|
||||
return getScale(getImage(filename), size);
|
||||
}
|
||||
|
||||
public double getScale(BufferedImage image, Tuple<Integer, Integer> size) {
|
||||
if (image == null)
|
||||
return 1.0;
|
||||
|
||||
int baseX = image.getWidth();
|
||||
int baseY = image.getHeight();
|
||||
|
||||
double finalScale = 1D;
|
||||
|
||||
if (size != null) {
|
||||
int targetX = size.getKey() * MAP_WIDTH;
|
||||
int targetY = size.getValue() * MAP_HEIGHT;
|
||||
|
||||
double scaleX = size.getKey() > 0 ? (double) targetX / baseX : Double.MAX_VALUE;
|
||||
double scaleY = size.getValue() > 0 ? (double) targetY / baseY : Double.MAX_VALUE;
|
||||
|
||||
finalScale = Math.min(scaleX, scaleY);
|
||||
if (finalScale >= Double.MAX_VALUE)
|
||||
finalScale = 1D;
|
||||
}
|
||||
|
||||
return finalScale;
|
||||
}
|
||||
|
||||
private static Rotation facingToRotation(BlockFace heightDirection, BlockFace widthDirection) {
|
||||
switch (heightDirection) {
|
||||
case WEST:
|
||||
return Rotation.CLOCKWISE_45;
|
||||
case NORTH:
|
||||
return widthDirection == BlockFace.WEST ? Rotation.CLOCKWISE : Rotation.NONE;
|
||||
case EAST:
|
||||
return Rotation.CLOCKWISE_135;
|
||||
case SOUTH:
|
||||
return widthDirection == BlockFace.WEST ? Rotation.CLOCKWISE : Rotation.NONE;
|
||||
default:
|
||||
return Rotation.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockFace calculateWidthDirection(Player player, BlockFace face) {
|
||||
float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f;
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.WEST;
|
||||
case SOUTH:
|
||||
return BlockFace.EAST;
|
||||
case EAST:
|
||||
return BlockFace.NORTH;
|
||||
case WEST:
|
||||
return BlockFace.SOUTH;
|
||||
case UP:
|
||||
case DOWN:
|
||||
if (Utils.isBetween(yaw, 45.0, 135.0))
|
||||
return BlockFace.NORTH;
|
||||
else if (Utils.isBetween(yaw, 135.0, 225.0))
|
||||
return BlockFace.EAST;
|
||||
else if (Utils.isBetween(yaw, 225.0, 315.0))
|
||||
return BlockFace.SOUTH;
|
||||
else
|
||||
return BlockFace.WEST;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockFace calculateHeightDirection(Player player, BlockFace face) {
|
||||
float yaw = (360.0f + player.getLocation().getYaw()) % 360.0f;
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
case EAST:
|
||||
case WEST:
|
||||
return BlockFace.DOWN;
|
||||
case UP:
|
||||
if (Utils.isBetween(yaw, 45.0, 135.0))
|
||||
return BlockFace.EAST;
|
||||
else if (Utils.isBetween(yaw, 135.0, 225.0))
|
||||
return BlockFace.SOUTH;
|
||||
else if (Utils.isBetween(yaw, 225.0, 315.0))
|
||||
return BlockFace.WEST;
|
||||
else
|
||||
return BlockFace.NORTH;
|
||||
case DOWN:
|
||||
if (Utils.isBetween(yaw, 45.0, 135.0))
|
||||
return BlockFace.WEST;
|
||||
else if (Utils.isBetween(yaw, 135.0, 225.0))
|
||||
return BlockFace.NORTH;
|
||||
else if (Utils.isBetween(yaw, 225.0, 315.0))
|
||||
return BlockFace.EAST;
|
||||
else
|
||||
return BlockFace.SOUTH;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAxisAligned(BlockFace face) {
|
||||
switch (face) {
|
||||
case DOWN:
|
||||
case UP:
|
||||
case WEST:
|
||||
case EAST:
|
||||
case SOUTH:
|
||||
case NORTH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,57 +7,99 @@ import org.bukkit.event.Cancellable;
|
|||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Called when an image is attempted to be placed.
|
||||
*/
|
||||
public class ImagePlaceEvent extends Event implements Cancellable {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
|
||||
private final Player player;
|
||||
private final Block block;
|
||||
private final BlockFace face;
|
||||
private final BlockFace widthDirection;
|
||||
private final BlockFace heightDirection;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final PlacingCacheEntry cache;
|
||||
private final PlacementData cache;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public ImagePlaceEvent(Player player, Block block, BlockFace face, int width, int height, PlacingCacheEntry cache) {
|
||||
public ImagePlaceEvent(Player player, Block block, BlockFace widthDirection, BlockFace heightDirection, int width, int height, PlacementData cache) {
|
||||
this.player = player;
|
||||
this.block = block;
|
||||
this.face = face;
|
||||
this.widthDirection = widthDirection;
|
||||
this.heightDirection = heightDirection;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The player attempting to place the image
|
||||
* @return the player attempting to place the image
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial block the image is placed against.
|
||||
*
|
||||
* @return the initial block the image is placed against
|
||||
*/
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public BlockFace getFace() {
|
||||
return face;
|
||||
|
||||
/**
|
||||
* The direction in which maps are placed in the height direction of the image.
|
||||
*
|
||||
* @return the height direction of the map placement
|
||||
*/
|
||||
public BlockFace getHeightDirection() {
|
||||
return heightDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* The direction in which maps are placed in the width direction of the image.
|
||||
*
|
||||
* @return the width direction of the map placement
|
||||
*/
|
||||
public BlockFace getWidthDirection() {
|
||||
return widthDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the image in maps
|
||||
*
|
||||
* @return the width of the image in maps
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The height of the image in maps
|
||||
*
|
||||
* @return the height of the image in maps
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public PlacingCacheEntry getCacheEntry() {
|
||||
/**
|
||||
* The placement data used to place the image
|
||||
*
|
||||
* @return the placement data
|
||||
*/
|
||||
public PlacementData getCacheEntry() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
import de.craftlancer.core.util.Tuple;
|
||||
|
||||
/**
|
||||
* Data associated with placing an image.
|
||||
*/
|
||||
public class PlacementData {
|
||||
|
||||
private final String filename;
|
||||
private final boolean isInvisible;
|
||||
private final boolean isFixed;
|
||||
private final Tuple<Integer, Integer> scale;
|
||||
|
||||
public PlacementData(String filename, boolean isInvisible, boolean isFixed, Tuple<Integer, Integer> scale) {
|
||||
this.filename = filename;
|
||||
this.isInvisible = isInvisible;
|
||||
this.isFixed = isFixed;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file name of the image to be placed
|
||||
*
|
||||
* @return the file name of the image
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the placed item frame will have the "fixed" property set.
|
||||
* A fixed frame can't be destroyed or modified by survival players.
|
||||
*
|
||||
* Only supported in 1.16 or higher!
|
||||
*
|
||||
* @return whether the placed frames will be fixed or not
|
||||
*/
|
||||
public boolean isFixed() {
|
||||
return isFixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the placed item frame will have the "invisible" property set.
|
||||
* An invisible frame won't be rendered, leaving only the item/map visible.
|
||||
*
|
||||
* Only supported in 1.16 or higher!
|
||||
*
|
||||
* @return whether the placed frames will be invisible or not
|
||||
*/
|
||||
public boolean isInvisible() {
|
||||
return isInvisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <b>requested</b> size of the image. The actual size might be smaller
|
||||
* since the plugin won't modify aspect ratios.
|
||||
*
|
||||
* Values of -1 stand for the default value of an unscaled map.
|
||||
*
|
||||
* @return the requested size of the image
|
||||
*/
|
||||
public Tuple<Integer, Integer> getSize() {
|
||||
return scale;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
public enum PlacementResult {
|
||||
INVALID_FACING,
|
||||
EVENT_CANCELLED,
|
||||
INVALID_DIRECTION,
|
||||
INSUFFICIENT_WALL,
|
||||
INSUFFICIENT_SPACE,
|
||||
SUCCESS,
|
||||
OVERLAPPING_ENTITY;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package de.craftlancer.imagemaps;
|
||||
|
||||
public class PlacingCacheEntry
|
||||
{
|
||||
private final String image;
|
||||
private final boolean fastsend;
|
||||
private final double scale;
|
||||
|
||||
public PlacingCacheEntry(String image, boolean fastsend, double scale)
|
||||
{
|
||||
this.image = image;
|
||||
this.fastsend = fastsend;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public String getImage()
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
public boolean isFastSend()
|
||||
{
|
||||
return fastsend;
|
||||
}
|
||||
|
||||
public double getScale() {
|
||||
return scale;
|
||||
}
|
||||
}
|
|
@ -1,15 +1,42 @@
|
|||
main: de.craftlancer.imagemaps.ImageMaps
|
||||
author: SydMontague
|
||||
version: ${project.version}
|
||||
api-version: 1.13
|
||||
name: ImageMaps
|
||||
commands:
|
||||
imagemap:
|
||||
usage: |
|
||||
/imagemap <file> <fastsend> - then rightlick on a block, fastsend is either true or false
|
||||
/imagemap <file> reload - reloads the imagefile
|
||||
permission: imagemaps.use
|
||||
/imagemap place <filename> [frameVisible] [frameFixed] [size] - starts image placement
|
||||
/imagemap download <filename> <sourceURL> - downloads an image
|
||||
/imagemap info <filename> - displays image info
|
||||
/imagemap reload <filename> - reloads an image from disk
|
||||
/imagemap list [page] - lists all files in the images folder
|
||||
/imagemap help [command] - shows help
|
||||
permissions:
|
||||
imagemaps.use:
|
||||
imagemaps.*:
|
||||
default: op
|
||||
children:
|
||||
imagemaps.place: true
|
||||
imagemaps.download: true
|
||||
imagemaps.info: true
|
||||
imagemaps.list: true
|
||||
imagemaps.reload: true
|
||||
imagemaps.help: true
|
||||
imagemaps.toggleFixed: true
|
||||
imagemaps.toggleVisible: true
|
||||
imagemaps.place:
|
||||
default: op
|
||||
imagemaps.download:
|
||||
default: op
|
||||
imagemaps.info:
|
||||
default: op
|
||||
imagemaps.list:
|
||||
default: op
|
||||
imagemaps.reload:
|
||||
default: op
|
||||
imagemaps.help:
|
||||
default: op
|
||||
imagemaps.toggleFixed:
|
||||
default: op
|
||||
imagemaps.toggleVisible:
|
||||
default: op
|
Loading…
Reference in New Issue