实现了基本功能

This commit is contained in:
zhangyuheng 2024-02-20 23:25:31 +08:00
commit 84cf5d4495
18 changed files with 1288 additions and 0 deletions

113
.gitignore vendored Normal file
View File

@ -0,0 +1,113 @@
# User-specific stuff
.idea/
*.iml
*.ipr
*.iws
# IntelliJ
out/
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# Common working directory
run/

75
pom.xml Normal file
View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>cn.lunadeer</groupId>
<artifactId>ColorfulMap</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>ColorfulMap</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>dev.folia</groupId>
<artifactId>folia-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,171 @@
package cn.lunadeer.colorfulmap;
import cn.lunadeer.colorfulmap.utils.Notification;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Apis {
public static List<ItemFrame> getItemFrameMatrix(Player player, ItemFrame left_bottom, Integer x, Integer y) {
List<ItemFrame> item_frames = new ArrayList<>();
Location corner = left_bottom.getLocation();
BlockFace facing = left_bottom.getFacing();
if (facing == BlockFace.UP || facing == BlockFace.DOWN) {
Notification.warn(player, "暂时不支持上下方向的展示框阵列");
return null;
/*
//
if (getItemFrame(new Location(corner.getWorld(), corner.getBlockX() + x - 1, corner.getBlockY(), corner.getBlockZ() - y + 1)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX() + j, corner.getBlockY(), corner.getBlockZ() - i));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
//
if (getItemFrame(new Location(corner.getWorld(), corner.getBlockX() - y + 1, corner.getBlockY(), corner.getBlockZ() - x + 1)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX() - j, corner.getBlockY(), corner.getBlockZ() - i));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
//
if (getItemFrame(new Location(corner.getWorld(), corner.getBlockX() - x + 1, corner.getBlockY(), corner.getBlockZ() + y - 1)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX() - j, corner.getBlockY(), corner.getBlockZ() + i));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
//
if (getItemFrame(new Location(corner.getWorld(), corner.getBlockX() + y - 1, corner.getBlockY(), corner.getBlockZ() + x - 1)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX() + j, corner.getBlockY(), corner.getBlockZ() + i));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
*/
}
if (facing == BlockFace.NORTH) {
int t_r_x = corner.getBlockX() - x + 1;
int t_r_y = corner.getBlockY() + y - 1;
int t_r_z = corner.getBlockZ();
if (getItemFrame(new Location(corner.getWorld(), t_r_x, t_r_y, t_r_z)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX() - i, corner.getBlockY() + j, corner.getBlockZ()));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
}
if (facing == BlockFace.SOUTH) {
int t_r_x = corner.getBlockX() + x - 1;
int t_r_y = corner.getBlockY() + y - 1;
int t_r_z = corner.getBlockZ();
if (getItemFrame(new Location(corner.getWorld(), t_r_x, t_r_y, t_r_z)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX() + i, corner.getBlockY() + j, corner.getBlockZ()));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
}
if (facing == BlockFace.WEST) {
int t_r_x = corner.getBlockX();
int t_r_y = corner.getBlockY() + y - 1;
int t_r_z = corner.getBlockZ() + x - 1;
if (getItemFrame(new Location(corner.getWorld(), t_r_x, t_r_y, t_r_z)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX(), corner.getBlockY() + j, corner.getBlockZ() + i));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
}
if (facing == BlockFace.EAST) {
int t_r_x = corner.getBlockX();
int t_r_y = corner.getBlockY() + y - 1;
int t_r_z = corner.getBlockZ() - x + 1;
if (getItemFrame(new Location(corner.getWorld(), t_r_x, t_r_y, t_r_z)) != null) {
for (int j = y - 1; j >= 0; j--) {
for (int i = 0; i < x; i++) {
ItemFrame item_frame = getItemFrame(new Location(corner.getWorld(), corner.getBlockX(), corner.getBlockY() + j, corner.getBlockZ() - i));
if (item_frame == null) {
Notification.error(player, "展示框阵列不完整");
return null;
}
item_frames.add(item_frame);
}
}
return item_frames;
}
}
return null;
}
public static ItemFrame getItemFrame(Location loc) {
Collection<Entity> entities = loc.getWorld().getNearbyEntities(loc, 1, 1, 1);
for (Entity entity : entities) {
if (entity.getLocation().getBlockX() != loc.getBlockX() || entity.getLocation().getBlockY() != loc.getBlockY() || entity.getLocation().getBlockZ() != loc.getBlockZ()) {
continue;
}
if (entity instanceof ItemFrame) {
return (ItemFrame) entity;
}
}
return null;
}
}

View File

@ -0,0 +1,44 @@
package cn.lunadeer.colorfulmap;
import cn.lunadeer.colorfulmap.commands.ToMap;
import cn.lunadeer.colorfulmap.utils.XLogger;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Objects;
public final class ColorfulMap extends JavaPlugin {
@Override
public void onEnable() {
// Plugin startup logic
instance = this;
config = new Configuration(this);
Objects.requireNonNull(Bukkit.getPluginCommand("tomap")).setExecutor(new ToMap());
Bukkit.getPluginManager().registerEvents(new Events(), this);
new MapManager().init();
XLogger.info("ColorfulMap 已加载");
XLogger.info("版本: " + getPluginMeta().getVersion());
// https://patorjk.com/software/taag/#p=display&f=Big&t=ColorfulMap
XLogger.info(" _____ _ __ _ __ __");
XLogger.info(" / ____| | | / _| | | \\/ |");
XLogger.info(" | | ___ | | ___ _ __| |_ _ _| | \\ / | __ _ _ __");
XLogger.info(" | | / _ \\| |/ _ \\| '__| _| | | | | |\\/| |/ _` | '_ \\");
XLogger.info(" | |___| (_) | | (_) | | | | | |_| | | | | | (_| | |_) |");
XLogger.info(" \\_____\\___/|_|\\___/|_| |_| \\__,_|_|_| |_|\\__,_| .__/");
XLogger.info(" | |");
XLogger.info(" |_|");
XLogger.info("");
}
@Override
public void onDisable() {
// Plugin shutdown logic
}
public static ColorfulMap instance;
public static Configuration config;
}

View File

@ -0,0 +1,57 @@
package cn.lunadeer.colorfulmap;
import org.bukkit.configuration.file.FileConfiguration;
public class Configuration {
public Configuration(ColorfulMap plugin) {
_plugin = plugin;
_plugin.saveDefaultConfig();
reload();
_plugin.saveConfig();
}
public void reload() {
_plugin.reloadConfig();
_file = _plugin.getConfig();
_debug = _file.getBoolean("Debug", false);
_max_frame_x = _file.getInt("MaxFrameX", 32);
_max_frame_y = _file.getInt("MaxFrameY", 18);
}
public Boolean isDebug() {
return _debug;
}
public void setDebug(Boolean debug) {
_debug = debug;
_file.set("Debug", debug);
_plugin.saveConfig();
}
public Integer getMaxFrameX() {
return _max_frame_x;
}
public void setMaxFrameX(Integer max_frame_x) {
_max_frame_x = max_frame_x;
_file.set("MaxFrameX", max_frame_x);
_plugin.saveConfig();
}
public Integer getMaxFrameY() {
return _max_frame_y;
}
public void setMaxFrameY(Integer max_frame_y) {
_max_frame_y = max_frame_y;
_file.set("MaxFrameY", max_frame_y);
_plugin.saveConfig();
}
private final ColorfulMap _plugin;
private FileConfiguration _file;
private Boolean _debug;
private Integer _max_frame_x;
private Integer _max_frame_y;
}

View File

@ -0,0 +1,100 @@
package cn.lunadeer.colorfulmap;
import cn.lunadeer.colorfulmap.generator.ImageRenderer;
import cn.lunadeer.colorfulmap.utils.Notification;
import cn.lunadeer.colorfulmap.utils.XLogger;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
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.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static cn.lunadeer.colorfulmap.Apis.getItemFrameMatrix;
public class Events implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void putImageMapsOnItemFrame(PlayerInteractEntityEvent event) {
XLogger.debug("PlayerInteractEntityEvent called");
// if not item frame return
Entity entity = event.getRightClicked();
if (!(entity instanceof ItemFrame)) {
return;
}
ItemFrame item_frame = (ItemFrame) entity;
ItemStack item = event.getPlayer().getInventory().getItemInMainHand();
XLogger.debug("item: " + item);
if (item.getType() == Material.AIR) {
return;
}
// if not map return
if (item.getType() != Material.FILLED_MAP) {
return;
}
if (!item.getItemMeta().hasLore()) {
return;
}
Player player = event.getPlayer();
List<Component> lore = item.getItemMeta().lore();
XLogger.debug("PlayerInteractEntityEvent");
if (lore == null || lore.size() != 3) {
return;
}
XLogger.debug("putImageMapsOnItemFrame");
UUID uuid = UUID.fromString(((TextComponent) lore.get(0)).content());
int count_x = Integer.parseInt(((TextComponent) lore.get(1)).content());
int count_y = Integer.parseInt(((TextComponent) lore.get(2)).content());
XLogger.debug("uuid: " + uuid);
XLogger.debug("count_x: " + count_x);
XLogger.debug("count_y: " + count_y);
event.setCancelled(true);
List<ItemFrame> item_frames = getItemFrameMatrix(player, item_frame, count_x, count_y);
if (item_frames == null) {
event.setCancelled(true);
Notification.error(player, "没有足够的展示框阵列,尺寸应该为 " + count_x + "x" + count_y);
return;
}
List<ItemStack> maps = new ArrayList<>();
for (int y = 0; y < count_y; y++) {
for (int x = 0; x < count_x; x++) {
String path = "maps/" + uuid + "/" + x + "_" + y + ".png";
ItemStack map = ImageRenderer.getMapItemFromImageTile(player, path);
if (map == null) {
Notification.error(player, "无法加载地图,原始路径丢失:" + path);
return;
}
maps.add(map);
}
}
XLogger.debug("maps size: " + maps.size());
XLogger.debug("item_frames size: " + item_frames.size());
for (int i = 0; i < item_frames.size(); i++) {
item_frames.get(i).setItem(new ItemStack(Material.AIR));
player.getInventory().setItemInMainHand(maps.get(i));
PlayerInteractEntityEvent event_put_map = new PlayerInteractEntityEvent(player, item_frames.get(i));
Bukkit.getPluginManager().callEvent(event_put_map);
if (event_put_map.isCancelled()) {
player.getInventory().setItemInMainHand(item);
Notification.error(player, "无法放置地图");
return;
} else {
item_frames.get(i).setItem(maps.get(i));
}
}
player.getInventory().setItemInMainHand(new ItemStack(Material.AIR));
}
}

View File

@ -0,0 +1,171 @@
package cn.lunadeer.colorfulmap;
import cn.lunadeer.colorfulmap.generator.ImageRenderer;
import cn.lunadeer.colorfulmap.utils.XLogger;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.MapInitializeEvent;
import org.bukkit.map.MapView;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class MapManager implements Listener {
public static MapManager instance = null;
private final MapsFile dataFile = new MapsFile();
/*
world_name:
id: "path/to/image.png"
*/
private final Map<String, Map<Integer, BufferedImage>> savedImages = new HashMap<>();
/***
* Call this method in the onEnable()
* Code:
* ImageManager manager = ImageManger.getInstance();
* manager.init();
*/
public void init() {
MapManager.instance = this;
Bukkit.getPluginManager().registerEvents(this, ColorfulMap.instance);
loadImages();
}
@EventHandler
public void onMapInitEvent(MapInitializeEvent event) {
World world = event.getMap().getWorld();
if (world == null) {
return;
}
int id = event.getMap().getId();
if (!hasImage(world, id)) {
return;
}
MapView view = event.getMap();
view.getRenderers().clear();
BufferedImage image = getImage(world, id);
if (image == null) {
XLogger.warn("图片丢失ID" + id);
return;
}
view.addRenderer(new ImageRenderer(image));
}
/***
* Whenever a new map is created, save the ID and Image to data file.
*
* @param id - MapView ID
* @param path - Image Path
*/
public void saveImage(World world, Integer id, String path) {
String world_name = world.getName();
FileConfiguration config = dataFile.getConfig();
config.set(world_name + "." + id, path);
dataFile.saveConfig();
if (!savedImages.containsKey(world_name)) {
savedImages.put(world_name, new HashMap<>());
}
BufferedImage image = StorageMaps.load(path);
savedImages.get(world_name).put(id, image);
}
/***
* Loads images from data file to HashMap.
*/
private void loadImages() {
FileConfiguration config = dataFile.getConfig();
for (String world : config.getKeys(false)) {
for (String id : Objects.requireNonNull(config.getConfigurationSection(world)).getKeys(false)) {
String path = config.getString(world + "." + id);
if (path == null) {
continue;
}
BufferedImage image = StorageMaps.load(path);
if (image == null) {
XLogger.err("无法加载图片: " + path);
continue;
}
if (!savedImages.containsKey(world)) {
savedImages.put(world, new HashMap<>());
}
savedImages.get(world).put(Integer.parseInt(id), image);
}
}
}
public boolean hasImage(World world_name, int id) {
return savedImages.containsKey(world_name.getName()) && savedImages.get(world_name.getName()).containsKey(id);
}
public BufferedImage getImage(World world_name, int id) {
return savedImages.get(world_name.getName()).get(id);
}
static class MapsFile {
private final ColorfulMap plugin = ColorfulMap.instance;
private FileConfiguration dataConfig = null;
private File dataConfigFile = null;
private final String path = "maps/maps.yml";
public MapsFile() {
saveDefaultConfig();
}
public void reloadConfig() {
if (dataConfigFile == null)
dataConfigFile = new File(plugin.getDataFolder(), path);
this.dataConfig = YamlConfiguration
.loadConfiguration(dataConfigFile);
InputStream defConfigStream = plugin.getResource(path);
if (defConfigStream != null) {
YamlConfiguration defConfig = YamlConfiguration
.loadConfiguration(new InputStreamReader(defConfigStream));
this.dataConfig.setDefaults(defConfig);
}
}
public FileConfiguration getConfig() {
if (this.dataConfig == null)
reloadConfig();
return this.dataConfig;
}
public void saveConfig() {
if ((dataConfig == null) || (dataConfigFile == null))
return;
try {
getConfig().save(dataConfigFile);
} catch (IOException e) {
XLogger.err("Could not save config to " + dataConfigFile);
}
}
public void saveDefaultConfig() {
if (dataConfigFile == null)
dataConfigFile = new File(plugin.getDataFolder(), path);
if (!dataConfigFile.exists())
plugin.saveResource(path, false);
}
}
}

View File

@ -0,0 +1,85 @@
package cn.lunadeer.colorfulmap;
import cn.lunadeer.colorfulmap.utils.Notification;
import org.bukkit.entity.Player;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class StorageMaps {
private static final File data_folder = ColorfulMap.instance.getDataFolder();
/**
* save images to plugins/ColorfulMap/maps/{map_uuid}/x_y.png
*
* @param player player
* @param images images
* @param x_count x_count
* @param y_count y_count
*/
public static UUID save(Player player, List<BufferedImage> images, int x_count, int y_count) {
if (images.size() != x_count * y_count) {
return null;
}
UUID map_uuid = UUID.randomUUID();
for (int y = 0; y < y_count; y++) {
for (int x = 0; x < x_count; x++) {
BufferedImage image = images.get(y * x_count + x);
File map_folder = new File(data_folder, "maps/" + map_uuid);
if (!map_folder.exists()) {
if (!map_folder.mkdirs()) {
Notification.error(player, "Failed to save map: failed to create map folder");
return null;
}
}
File image_file = new File(map_folder, x + "_" + y + ".png");
try {
ImageIO.write(image, "png", image_file);
} catch (Exception e) {
Notification.error(player, "Failed to save map: " + e.getMessage());
return null;
}
}
}
return map_uuid;
}
/**
* load images from plugins/ColorfulMap/maps/{map_uuid}/x_y.png
*
* @param player player
* @param map_uuid map_uuid
* @param x_count x_count
* @param y_count y_count
* @return images
*/
public static List<BufferedImage> load(Player player, UUID map_uuid, int x_count, int y_count) {
List<BufferedImage> images = new ArrayList<>();
for (int y = 0; y < y_count; y++) {
for (int x = 0; x < x_count; x++) {
File image_file = new File(data_folder, "maps/" + map_uuid + "/" + x + "_" + y + ".png");
try {
BufferedImage image = ImageIO.read(image_file);
images.add(image);
} catch (Exception e) {
Notification.error(player, "Failed to load map: " + e.getMessage());
return null;
}
}
}
return images;
}
public static BufferedImage load(String path) {
File image_file = new File(data_folder, path);
try {
return ImageIO.read(image_file);
} catch (Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,67 @@
package cn.lunadeer.colorfulmap.commands;
import cn.lunadeer.colorfulmap.ColorfulMap;
import cn.lunadeer.colorfulmap.generator.Multi;
import cn.lunadeer.colorfulmap.utils.Notification;
import cn.lunadeer.colorfulmap.utils.Time;
import cn.lunadeer.colorfulmap.utils.XLogger;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class ToMap implements CommandExecutor {
/**
* Executes the given command, returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
XLogger.warn("只有玩家可以使用此命令");
return true;
}
Player player = (Player) sender;
if (args.length == 0) {
Notification.error(player, "用法 /tomap <图片url> [缩放比例(选填默认1)]");
return true;
}
String url = args[0];
float scale = 1;
if (args.length == 2) {
try {
scale = Float.parseFloat(args[1]);
} catch (NumberFormatException e) {
Notification.error(player, "缩放比例必须是数字");
return true;
}
}
// ItemStack mapImage = Multi.generate(player, url, scale);
// if (mapImage == null){
// Notification.error(player, "生成地图失败");
// return true;
// }
// player.getInventory().addItem(mapImage);
float finalScale = scale;
Time.runAsync(ColorfulMap.instance, () -> {
ItemStack mapImage = Multi.generate(player, url, finalScale);
if (mapImage == null) {
Notification.error(player, "生成地图失败");
return;
}
player.getInventory().addItem(mapImage);
}
);
return true;
}
}

View File

@ -0,0 +1,64 @@
package cn.lunadeer.colorfulmap.generator;
import cn.lunadeer.colorfulmap.MapManager;
import cn.lunadeer.colorfulmap.StorageMaps;
import cn.lunadeer.colorfulmap.utils.Notification;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView;
import org.jetbrains.annotations.NotNull;
import java.awt.image.BufferedImage;
public class ImageRenderer extends MapRenderer {
public ImageRenderer(BufferedImage image) {
this.image = image;
}
private final BufferedImage image;
/**
* Render to the given map.
*
* @param map The MapView being rendered to.
* @param canvas The canvas to use for rendering.
* @param player The player who triggered the rendering.
*/
@Override
public void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player) {
try {
if (image.getWidth() > 128 || image.getHeight() > 128) {
Notification.error(player, "Failed to render image: image is too large");
return;
}
int x_offset = (128 - image.getWidth()) / 2;
int y_offset = (128 - image.getHeight()) / 2;
canvas.drawImage(x_offset, y_offset, image);
} catch (Exception e) {
Notification.error(player, "Failed to render image: " + e.getMessage());
}
}
public static ItemStack getMapItemFromImageTile(Player player, String path){
BufferedImage image = StorageMaps.load(path);
if (image == null) {
Notification.error(player, "图片丢失,无法加载,请重新生成。");
return null;
}
ItemStack mapItem = new ItemStack(Material.FILLED_MAP, 1);
MapMeta meta = (MapMeta) mapItem.getItemMeta();
MapView mapView = Bukkit.createMap(player.getWorld());
ImageRenderer renderer = new ImageRenderer(image);
mapView.getRenderers().clear();
mapView.addRenderer(renderer);
meta.setMapView(mapView);
mapItem.setItemMeta(meta);
MapManager.instance.saveImage(player.getWorld(), mapView.getId(), path);
return mapItem;
}
}

View File

@ -0,0 +1,85 @@
package cn.lunadeer.colorfulmap.generator;
import cn.lunadeer.colorfulmap.ColorfulMap;
import cn.lunadeer.colorfulmap.StorageMaps;
import cn.lunadeer.colorfulmap.utils.Notification;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static cn.lunadeer.colorfulmap.generator.TextRenderer.applyTextToMap;
public class Multi {
public static ItemStack generate(Player player, String url, Float scale) {
try {
URL _url = new URL(url);
BufferedImage image = ImageIO.read(_url);
if (scale != 1.0) {
int width = image.getWidth();
int height = image.getHeight();
int new_width = (int) (width * scale);
int new_height = (int) (height * scale);
BufferedImage newImage = new BufferedImage(new_width, new_height, BufferedImage.TYPE_INT_ARGB);
newImage.getGraphics().drawImage(image, 0, 0, new_width, new_height, null);
image = newImage;
}
int image_width = image.getWidth();
int image_height = image.getHeight();
int x_count = (int) Math.ceil(image_width / 128.0);
int y_count = (int) Math.ceil(image_height / 128.0);
if (x_count > ColorfulMap.config.getMaxFrameX() || y_count > ColorfulMap.config.getMaxFrameY()) {
Notification.error(player, "无法生成地图画: 图片太大,分辨率不得超过" + ColorfulMap.config.getMaxFrameX() * 128 + "x" + ColorfulMap.config.getMaxFrameY() * 128);
return null;
}
int new_width = x_count * 128;
int new_height = y_count * 128;
BufferedImage newImage = new BufferedImage(new_width, new_height, BufferedImage.TYPE_INT_ARGB);
newImage.getGraphics().drawImage(image, (new_width - image_width) / 2, (new_height - image_height) / 2, null);
image = newImage;
image_width = image.getWidth();
image_height = image.getHeight();
List<BufferedImage> split_images = new ArrayList<>();
for (int y = 0; y < y_count; y++) {
for (int x = 0; x < x_count; x++) {
int width = Math.min(128, image_width - x * 128);
int height = Math.min(128, image_height - y * 128);
BufferedImage sub_image = image.getSubimage(x * 128, y * 128, width, height);
split_images.add(sub_image);
}
}
if (split_images.size() == 0) {
Notification.error(player, "无法生成地图画: 图片为空");
return null;
}
UUID map_images_uuid = StorageMaps.save(player, split_images, x_count, y_count);
if (map_images_uuid == null) {
Notification.error(player, "无法生成地图画: 无法保存图片");
return null;
}
List<String> map_info = new ArrayList<>();
map_info.add("size: " + x_count + "x" + y_count);
ItemStack mapItem = applyTextToMap(player, map_info);
// add lore to map item (uuid, x_count, y_count)
MapMeta meta = (MapMeta) mapItem.getItemMeta();
List<Component> lore = new ArrayList<>();
lore.add(Component.text(map_images_uuid.toString()));
lore.add(Component.text(String.valueOf(x_count)));
lore.add(Component.text(String.valueOf(y_count)));
meta.lore(lore);
mapItem.setItemMeta(meta);
return mapItem;
} catch (Exception e) {
Notification.error(player, "无法生成地图画: " + e.getMessage());
return null;
}
}
}

View File

@ -0,0 +1,55 @@
package cn.lunadeer.colorfulmap.generator;
import cn.lunadeer.colorfulmap.utils.Notification;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView;
import org.bukkit.map.MinecraftFont;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class TextRenderer extends MapRenderer {
public TextRenderer(List<String> text) {
this.text = text;
}
private final List<String> text;
/**
* Render to the given map.
*
* @param map The MapView being rendered to.
* @param canvas The canvas to use for rendering.
* @param player The player who triggered the rendering.
*/
@Override
public void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player) {
try {
int y = 0;
for (String line : text) {
canvas.drawText(0, y, MinecraftFont.Font, line);
y += 10;
}
} catch (Exception e) {
Notification.error(player, "Failed to render text: " + e.getMessage());
}
}
public static ItemStack applyTextToMap(Player player, List<String> text){
ItemStack mapItem = new ItemStack(Material.FILLED_MAP, 1);
MapMeta meta = (MapMeta) mapItem.getItemMeta();
MapView mapView = Bukkit.createMap(player.getWorld());
TextRenderer renderer = new TextRenderer(text);
mapView.addRenderer(renderer);
meta.setMapView(mapView);
mapItem.setItemMeta(meta);
return mapItem;
}
}

View File

@ -0,0 +1,63 @@
package cn.lunadeer.colorfulmap.utils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class Notification {
private static final Style i_style = Style.style(TextColor.color(139, 255, 123));
private static final Style w_style = Style.style(TextColor.color(255, 185, 69));
private static final Style e_style = Style.style(TextColor.color(255, 96, 72));
private static final String prefix = "[ColorfulMap] ";
public static void info(Player player, String msg) {
player.sendMessage(Component.text(prefix + msg, i_style));
}
public static void warn(Player player, String msg) {
player.sendMessage(Component.text(prefix + msg, w_style));
}
public static void error(Player player, String msg) {
player.sendMessage(Component.text(prefix + msg, e_style));
}
public static void info(CommandSender sender, String msg) {
sender.sendMessage(Component.text(prefix + msg, i_style));
}
public static void warn(CommandSender sender, String msg) {
sender.sendMessage(Component.text(prefix + msg, w_style));
}
public static void error(CommandSender sender, String msg) {
sender.sendMessage(Component.text(prefix + msg, e_style));
}
public static void info(Player player, Component msg) {
player.sendMessage(Component.text(prefix, i_style).append(msg));
}
public static void warn(Player player, Component msg) {
player.sendMessage(Component.text(prefix, w_style).append(msg));
}
public static void error(Player player, Component msg) {
player.sendMessage(Component.text(prefix, e_style).append(msg));
}
public static void info(CommandSender player, Component msg) {
player.sendMessage(Component.text(prefix, i_style).append(msg));
}
public static void warn(CommandSender player, Component msg) {
player.sendMessage(Component.text(prefix, w_style).append(msg));
}
public static void error(CommandSender player, Component msg) {
player.sendMessage(Component.text(prefix, e_style).append(msg));
}
}

View File

@ -0,0 +1,67 @@
package cn.lunadeer.colorfulmap.utils;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Time {
public static String nowStr() {
// yyyy-MM-dd HH:mm:ss
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
/**
* 尝试获取folia的调度器
*
* @return 是否成功
*/
private static boolean tryFolia() {
try {
Bukkit.getAsyncScheduler();
return true;
} catch (Throwable ignored) {
}
return false;
}
private static Boolean IS_FOLIA = null;
/**
* 判断是否是folia核心
*
* @return 是否是folia核心
*/
public static Boolean isFolia() {
if (IS_FOLIA == null) IS_FOLIA = tryFolia();
return IS_FOLIA;
}
/**
* 定时异步任务
*
* @param plugin 插件
* @param runnable 任务
* @param ticks 间隔
*/
public static void runAtFixedRateAsync(Plugin plugin, Runnable runnable, int ticks) {
if (isFolia())
Bukkit.getAsyncScheduler().runAtFixedRate(plugin, (task) -> runnable.run(), ticks / 20, ticks / 20, TimeUnit.SECONDS);
else Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, ticks, ticks);
}
public static void runLater(Plugin plugin, Runnable runnable, int ticks) {
if (isFolia())
Bukkit.getAsyncScheduler().runDelayed(plugin, (task) -> runnable.run(), ticks / 20, TimeUnit.SECONDS);
else Bukkit.getScheduler().runTaskLater(plugin, runnable, ticks);
}
public static void runAsync(Plugin plugin, Runnable runnable) {
if (isFolia())
Bukkit.getAsyncScheduler().runNow(plugin, (task) -> runnable.run());
else Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable);
}
}

View File

@ -0,0 +1,56 @@
package cn.lunadeer.colorfulmap.utils;
import cn.lunadeer.colorfulmap.ColorfulMap;
import org.bukkit.entity.Player;
import java.util.logging.Logger;
public class XLogger {
private static final ColorfulMap _plugin = ColorfulMap.instance;
private static final Logger _logger = _plugin.getLogger();
private static final String prefix = "[ColorfulMap] ";
public static void info(Player player, String message) {
Notification.info(player, prefix + "I | " + message);
if (ColorfulMap.config.isDebug())
debug("来自玩家[ " + player.getName() + " ] 的信息 | " + message);
}
public static void info(String message) {
_logger.info(" I | " + message);
}
public static void warn(Player player, String message) {
Notification.warn(player, prefix + "W | " + message);
if (ColorfulMap.config.isDebug())
debug("来自玩家[ " + player.getName() + " ] 的警告 | " + message);
}
public static void warn(String message) {
_logger.info(" W | " + message);
}
public static void err(Player player, String message) {
Notification.error(player, prefix + "E | " + message);
if (ColorfulMap.config.isDebug())
debug("来自玩家[ " + player.getName() + " ] 的报错 | " + message);
}
public static void err(String message) {
_logger.info(" E | " + message);
}
public static void debug(Player player, String message) {
if (!ColorfulMap.config.isDebug()) return;
if (player.isOp())
Notification.info(player, prefix + "D | " + message);
else
debug("来自玩家[ " + player.getName() + " ] 的调试 | " + message);
}
public static void debug(String message) {
if (!ColorfulMap.config.isDebug()) return;
_logger.info(" D | " + message);
}
}

View File

@ -0,0 +1,5 @@
MaxFrameX: 32
MaxFrameY: 18
Debug: false

View File

View File

@ -0,0 +1,10 @@
name: ColorfulMap
version: '${project.version}'
main: cn.lunadeer.colorfulmap.ColorfulMap
api-version: '1.20'
load: STARTUP
folia-supported: true
commands:
tomap:
description: 将图片转换为地图画
usage: /tomap <图片url> [缩放比例(默认1)]