init: 项目初始化

项目初始化
This commit is contained in:
2022-08-06 01:15:48 +08:00
commit 75b06aae41
33 changed files with 2610 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
setArchivesBaseName("HamsterCore-Bukkit")
evaluationDependsOn(':hamster-core-common')
configurations {
api.extendsFrom apiShade
implementation.extendsFrom implementationShade
}
dependencies {
compileOnly project(":hamster-core-common")
compileOnly 'org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT'
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
//noinspection GradlePackageUpdate
apiShade 'com.zaxxer:HikariCP:4.0.3'
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
apiShade 'com.squareup.okhttp3:okhttp:4.10.0'
// https://mvnrepository.com/artifact/net.kyori/adventure-platform-bukkit
apiShade 'net.kyori:adventure-platform-bukkit:4.1.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
}
test {
useJUnitPlatform()
}
processResources {
inputs.property "version", project.version
filesMatching("plugin.yml") {
expand "version": project.version
}
}
task addSource {
doLast {
sourceSets {
main {
java.srcDirs += [
project(':hamster-core-common').sourceSets.main.java
]
resources.srcDirs += [
project(':hamster-core-common').sourceSets.main.resources
]
}
}
}
}
classes.dependsOn(addSource)
jar {
from([
configurations.implementationShade.collect {
it.isDirectory() ? it : zipTree(it)
},
configurations.apiShade.collect {
it.isDirectory() ? it : zipTree(it)
}
])
}

View File

@@ -0,0 +1,49 @@
package cn.hamster3.core.bukkit;
import cn.hamster3.core.bukkit.page.listener.PageListener;
import cn.hamster3.core.common.constant.ConstantObjects;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class HamsterCorePlugin extends JavaPlugin {
private static HamsterCorePlugin instance;
public static HamsterCorePlugin getInstance() {
return instance;
}
public static void sync(Runnable runnable) {
Bukkit.getScheduler().runTask(instance, runnable);
}
public static void async(Runnable runnable) {
Bukkit.getScheduler().runTaskAsynchronously(instance, runnable);
}
@Override
public void onLoad() {
instance = this;
}
@Override
public void onEnable() {
long start = System.currentTimeMillis();
getLogger().info("仓鼠核心正在启动...");
Bukkit.getPluginManager().registerEvents(PageListener.INSTANCE, this);
getLogger().info("已注册 PageListener.");
long time = System.currentTimeMillis() - start;
getLogger().info("仓鼠核心已启动,总计耗时 " + time + " ms.");
}
@Override
public void onDisable() {
long start = System.currentTimeMillis();
getLogger().info("仓鼠核心正在关闭...");
ConstantObjects.WORKER_EXECUTOR.shutdownNow();
getLogger().info("已暂停 WORKER_EXECUTOR.");
ConstantObjects.SCHEDULED_EXECUTOR.shutdownNow();
getLogger().info("已暂停 SCHEDULED_EXECUTOR.");
long time = System.currentTimeMillis() - start;
getLogger().info("仓鼠核心已关闭,总计耗时 " + time + " ms.");
}
}

View File

@@ -0,0 +1,11 @@
package cn.hamster3.core.bukkit.api;
import cn.hamster3.core.common.api.CommonAPI;
@SuppressWarnings("unused")
public class BukkitAPI extends CommonAPI {
public static BukkitAPI getInstance() {
return (BukkitAPI) instance;
}
}

View File

@@ -0,0 +1,184 @@
package cn.hamster3.core.bukkit.page;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@SuppressWarnings("unused")
public class ButtonGroup {
private final String name;
private final PageConfig config;
private final HashMap<Character, String> buttonNameMap;
/**
* 实例化这个按钮组
*
* @param pageConfig Page 设定
* @param config 按钮组设定
*/
public ButtonGroup(PageConfig pageConfig, ConfigurationSection config) {
this.config = pageConfig;
name = config.getName();
buttonNameMap = new HashMap<>();
for (String key : config.getKeys(false)) {
buttonNameMap.put(key.charAt(0), config.getString(key));
}
}
/**
* 以图形字符来获取按钮名称
* <p>
* 若未找到则返回 "empty"
*
* @param graphicKey 图形字符
* @return 按钮名称
*/
@NotNull
public String getButtonName(char graphicKey) {
return buttonNameMap.getOrDefault(graphicKey, "empty");
}
/**
* 以索引位置来获取按钮名称
* <p>
* 若未找到则返回 "empty"
*
* @param index 索引位置
* @return 按钮名称
*/
@NotNull
public String getButtonName(int index) {
return buttonNameMap.getOrDefault(config.getButtonKey(index), "empty");
}
/**
* 获取按钮在 GUI 中的第一个索引位置
*
* @param buttonName 按钮名称
* @return 按钮在 GUI 中的第一个索引位置
*/
public int getButtonIndex(String buttonName) {
Character graphicKey = getGraphicKey(buttonName);
if (graphicKey == null) {
return -1;
}
List<String> graphic = config.getGraphic();
for (int i = 0; i < graphic.size(); i++) {
char[] chars = graphic.get(i).toCharArray();
for (int j = 0; j < chars.length; j++) {
if (chars[j] == graphicKey) {
return i * 9 + j;
}
}
}
return -1;
}
/**
* 获得按钮在 GUI 中全部的索引位置
*
* @param buttonName 按钮名称
* @return 按钮在 GUI 中全部的索引位置
*/
public ArrayList<Integer> getButtonAllIndex(String buttonName) {
ArrayList<Integer> list = new ArrayList<>();
Character graphicKey = getGraphicKey(buttonName);
if (graphicKey == null) {
return list;
}
List<String> graphic = config.getGraphic();
for (int i = 0; i < graphic.size(); i++) {
char[] chars = graphic.get(i).toCharArray();
for (int j = 0; j < chars.length; j++) {
if (chars[j] == graphicKey) {
list.add(i * 9 + j);
}
}
}
return list;
}
/**
* 以按钮名称来获取图形字符
*
* @param buttonName 按钮名称
* @return 图形中的字符
*/
@Nullable
public Character getGraphicKey(String buttonName) {
for (Map.Entry<Character, String> entry : buttonNameMap.entrySet()) {
if (entry.getValue().equalsIgnoreCase(buttonName)) {
return entry.getKey();
}
}
return null;
}
/**
* 以图形字符来获取按钮物品
*
* @param graphicKey 图形字符
* @return 按钮物品
*/
public ItemStack getButton(char graphicKey) {
return getButton(getButtonName(graphicKey));
}
/**
* 以按钮名称来获取按钮物品
*
* @param buttonName 按钮名称
* @return 按钮物品
*/
public ItemStack getButton(String buttonName) {
ItemStack stack = config.getButtons().get(buttonName);
if (stack != null) {
stack = stack.clone();
}
return stack;
}
/**
* 获取这个按钮组的名称
*
* @return 按钮组名称
*/
public String getName() {
return name;
}
/**
* 获取把图形字符映射到按钮名称的表
*
* @return 把图形字符映射到按钮名称的表
*/
public HashMap<Character, String> getButtonNameMap() {
return buttonNameMap;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ButtonGroup)) return false;
ButtonGroup that = (ButtonGroup) o;
return name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String toString() {
return "ButtonGroup{" +
"name='" + name + '\'' +
", buttonNameMap=" + buttonNameMap +
'}';
}
}

View File

@@ -0,0 +1,195 @@
package cn.hamster3.core.bukkit.page;
import cn.hamster3.core.bukkit.HamsterCorePlugin;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings("unused")
public class PageConfig implements InventoryHolder {
@NotNull
private final Plugin plugin;
@NotNull
private final ConfigurationSection config;
@NotNull
private final String title;
@NotNull
private final List<String> graphic;
@NotNull
private final ArrayList<ButtonGroup> buttonGroups;
@NotNull
private final HashMap<String, Sound> buttonSounds;
@NotNull
private final HashMap<String, ItemStack> buttons;
@NotNull
private final Inventory inventory;
public PageConfig(@NotNull Plugin plugin, @NotNull ConfigurationSection config) {
this.plugin = plugin;
this.config = config;
title = config.getString("title", "").replace("&", "§");
List<String> graphicString = config.getStringList("graphic");
if (graphicString.size() > 6) {
graphicString = graphicString.subList(0, 6);
}
for (int i = 0; i < graphicString.size(); i++) {
String s = graphicString.get(i);
if (s.length() > 9) {
s = s.substring(0, 9);
}
graphicString.set(i, s);
}
graphic = graphicString;
inventory = Bukkit.createInventory(this, graphicString.size() * 9, title);
buttons = new HashMap<>();
ConfigurationSection buttonsConfig = config.getConfigurationSection("buttons");
for (String key : buttonsConfig.getKeys(false)) {
buttons.put(key, buttonsConfig.getItemStack(key));
}
buttonGroups = new ArrayList<>();
ConfigurationSection buttonGroupsConfig = config.getConfigurationSection("groups");
for (String key : buttonGroupsConfig.getKeys(false)) {
buttonGroups.add(
new ButtonGroup(this, buttonGroupsConfig.getConfigurationSection(key))
);
}
ButtonGroup group = getButtonGroup("default");
for (int i = 0; i < graphicString.size(); i++) {
char[] chars = graphicString.get(i).toCharArray();
for (int j = 0; j < chars.length; j++) {
char c = chars[j];
int index = i * 9 + j;
inventory.setItem(index, group.getButton(c));
}
}
buttonSounds = new HashMap<>();
ConfigurationSection buttonSoundConfig = config.getConfigurationSection("sounds");
for (String key : buttonSoundConfig.getKeys(false)) {
try {
buttonSounds.put(key, Sound.valueOf(buttonSoundConfig.getString(key)));
} catch (IllegalArgumentException e) {
HamsterCorePlugin.getInstance().getLogger().warning("初始化 PageConfig 时遇到了一个异常:");
e.printStackTrace();
}
}
}
/**
* 获取把 buttonName 映射到 展示物品 的表
*
* @return 把 buttonName 映射到 展示物品 的表
*/
@NotNull
public HashMap<String, ItemStack> getButtons() {
return buttons;
}
/**
* 获取索引位置上的 graphicKey
*
* @param index 索引
* @return 若超出 graphic 范围则返回 null
*/
@Nullable
public Character getButtonKey(int index) {
if (index < 0) return null;
if (index / 9 >= graphic.size()) return null;
String s = graphic.get(index / 9);
return s.charAt(index % 9);
}
/**
* 获取该显示物品对应的 buttonName
*
* @param stack 显示物品
* @return 按钮名称,若无法找到则返回 "empty"
*/
@NotNull
public String getButtonName(@Nullable ItemStack stack) {
if (stack == null) {
return "empty";
}
for (Map.Entry<String, ItemStack> entry : buttons.entrySet()) {
if (entry.getValue().isSimilar(stack)) {
return entry.getKey();
}
}
return "empty";
}
@Nullable
public Sound getButtonSound(@NotNull String buttonName) {
return buttonSounds.get(buttonName);
}
@NotNull
public ButtonGroup getButtonGroup(@NotNull String groupName) {
for (ButtonGroup group : buttonGroups) {
if (group.getName().equalsIgnoreCase(groupName)) {
return group;
}
}
return buttonGroups.get(0);
}
@NotNull
public Plugin getPlugin() {
return plugin;
}
@NotNull
public ConfigurationSection getConfig() {
return config;
}
@NotNull
public String getTitle() {
return title;
}
@NotNull
public List<String> getGraphic() {
return graphic;
}
@NotNull
public ArrayList<ButtonGroup> getButtonGroups() {
return buttonGroups;
}
@NotNull
@Override
public Inventory getInventory() {
return inventory;
}
@Override
public String toString() {
return "PageConfig{" +
", title='" + title + '\'' +
", graphic=" + graphic +
", buttonMap=" + buttons +
", buttonGroups=" + buttonGroups +
'}';
}
}

View File

@@ -0,0 +1,133 @@
package cn.hamster3.core.bukkit.page;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings("unused")
public interface PageElement {
/**
* 获取展示物品
* <p>
* 若返回 null 则使用 config 中的全局设置值
*
* @param player 占位符显示的目标玩家
* @return 展示物品
*/
default ItemStack getDisplayItem(HumanEntity player) {
return null;
}
/**
* 获取展示物品的显示材质
* <p>
* 若返回 null 则使用 config 中的全局设置值
*
* @param player 占位符显示的目标玩家
* @return 展示物品的显示材质
*/
default Material getMaterial(HumanEntity player) {
return null;
}
/**
* 替换物品的信息
*
* @param player 玩家
* @param stack 物品
* @param globalVariable 全局变量
*/
default void replaceItemInfo(HumanEntity player, ItemStack stack, HashMap<String, String> globalVariable) {
if (stack == null) {
return;
}
Material type = getMaterial(player);
if (type != null) {
stack.setType(type);
}
ItemMeta meta = stack.getItemMeta();
replaceMetaInfo(player, meta, globalVariable);
stack.setItemMeta(meta);
}
/**
* 替换物品的信息
*
* @param player 玩家
* @param meta 物品信息
* @param globalVariable 全局变量
*/
default void replaceMetaInfo(HumanEntity player, ItemMeta meta, HashMap<String, String> globalVariable) {
if (meta == null) {
return;
}
Map<String, String> replacer = getVariable(player, globalVariable);
if (meta.hasDisplayName()) {
String displayName = replaceDisplayName(player, meta.getDisplayName(), globalVariable);
meta.setDisplayName(displayName);
}
List<String> lore = meta.getLore();
if (lore != null) {
lore = replaceLore(player, lore, globalVariable);
meta.setLore(lore);
}
}
/**
* 替换物品展示名称
*
* @param player 玩家
* @param displayName 物品原名称
* @param globalVariable 全局变量
* @return 替换后的物品展示名称
*/
default String replaceDisplayName(HumanEntity player, String displayName, HashMap<String, String> globalVariable) {
return replacePlaceholder(player, displayName, globalVariable);
}
/**
* 替换物品 lore 信息
*
* @param player 玩家
* @param lore 物品原 lore 信息
* @param globalReplacer 全局变量
* @return 替换后的物品 lore 信息
*/
default List<String> replaceLore(HumanEntity player, List<String> lore, HashMap<String, String> globalReplacer) {
if (lore == null) {
return null;
}
Map<String, String> replacer = getVariable(player, globalReplacer);
for (int i = 0; i < lore.size(); i++) {
String s = lore.get(i);
for (Map.Entry<String, String> entry : replacer.entrySet()) {
s = s.replace(entry.getKey(), entry.getValue());
}
lore.set(i, s);
}
return lore;
}
default String replacePlaceholder(HumanEntity player, String string, HashMap<String, String> globalVariable) {
Map<String, String> replacer = getVariable(player, globalVariable);
for (Map.Entry<String, String> entry : replacer.entrySet()) {
string = string.replace(entry.getKey(), entry.getValue());
}
return string;
}
default Map<String, String> getVariable(HumanEntity player, HashMap<String, String> globalVariable) {
return globalVariable;
}
}

View File

@@ -0,0 +1,51 @@
package cn.hamster3.core.bukkit.page;
import cn.hamster3.core.bukkit.HamsterCorePlugin;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
public abstract class PageManager {
private static final HashMap<String, PageConfig> PAGE_CONFIG = new HashMap<>();
public static PageConfig getPageConfig(Class<?> clazz) {
PageConfig pageConfig = PAGE_CONFIG.get(clazz.getName());
if (pageConfig != null) {
return pageConfig;
}
if (!clazz.isAnnotationPresent(PluginPage.class)) {
throw new IllegalArgumentException(clazz.getName() + " 未被 @PluginPage 注解修饰!");
}
PluginPage annotation = clazz.getAnnotation(PluginPage.class);
String value = annotation.value();
Plugin plugin = Bukkit.getPluginManager().getPlugin(value);
File pageFolder = new File(plugin.getDataFolder(), "pages");
if (pageFolder.mkdirs()) {
HamsterCorePlugin.getInstance().getLogger().info("" + value + " 创建页面配置文件夹...");
}
String pageFileName = clazz.getSimpleName() + ".yml";
File pageFile = new File(pageFolder, pageFileName);
if (pageFile.exists()) {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(pageFile);
pageConfig = new PageConfig(plugin, configuration);
PAGE_CONFIG.put(clazz.getName(), pageConfig);
return pageConfig;
}
try {
Files.copy(plugin.getResource("/" + pageFileName), pageFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException(e);
}
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(pageFile);
pageConfig = new PageConfig(plugin, configuration);
PAGE_CONFIG.put(clazz.getName(), pageConfig);
return pageConfig;
}
}

View File

@@ -0,0 +1,15 @@
package cn.hamster3.core.bukkit.page;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PluginPage {
/**
* @return 插件名称
*/
String value();
}

View File

@@ -0,0 +1,50 @@
package cn.hamster3.core.bukkit.page.handler;
import cn.hamster3.core.bukkit.page.ButtonGroup;
import cn.hamster3.core.bukkit.page.PageConfig;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* 固定页面的 GUI
*/
@SuppressWarnings("unused")
public class FixedPageHandler extends PageHandler {
public FixedPageHandler(@NotNull HumanEntity player) {
super(player);
}
public FixedPageHandler(@NotNull HumanEntity player, @NotNull String title) {
super(player, title);
}
public FixedPageHandler(@NotNull PageConfig pageConfig, @NotNull HumanEntity player) {
super(pageConfig, player);
}
public FixedPageHandler(@NotNull PageConfig pageConfig, @NotNull String title, @NotNull HumanEntity player) {
super(pageConfig, title, player);
}
@Override
public void initPage() {
HumanEntity player = getPlayer();
Inventory inventory = getInventory();
PageConfig config = getPageConfig();
ButtonGroup group = getButtonGroup();
List<String> graphic = config.getGraphic();
for (int i = 0; i < graphic.size(); i++) {
char[] chars = graphic.get(i).toCharArray();
for (int j = 0; j < chars.length; j++) {
char c = chars[j];
int index = i * 9 + j;
inventory.setItem(index, group.getButton(c));
}
}
}
}

View File

@@ -0,0 +1,130 @@
package cn.hamster3.core.bukkit.page.handler;
import cn.hamster3.core.bukkit.HamsterCorePlugin;
import cn.hamster3.core.bukkit.page.ButtonGroup;
import cn.hamster3.core.bukkit.page.PageConfig;
import cn.hamster3.core.bukkit.page.PageManager;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.*;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
/**
* GUI 处理类
*/
@SuppressWarnings("unused")
public abstract class PageHandler implements InventoryHolder {
private final PageConfig pageConfig;
private final HumanEntity player;
private final Inventory inventory;
public PageHandler(@NotNull HumanEntity player) {
try {
pageConfig = PageManager.getPageConfig(getClass());
} catch (Exception e) {
throw new IllegalArgumentException("加载界面配置时遇到了一个异常!", e);
}
this.player = player;
inventory = Bukkit.createInventory(this, pageConfig.getInventory().getSize(), pageConfig.getTitle());
}
public PageHandler(@NotNull HumanEntity player, @NotNull String title) {
try {
pageConfig = PageManager.getPageConfig(getClass());
} catch (Exception e) {
throw new IllegalArgumentException("加载界面配置时遇到了一个异常!", e);
}
this.player = player;
inventory = Bukkit.createInventory(this, pageConfig.getInventory().getSize(), title);
}
public PageHandler(@NotNull PageConfig pageConfig, @NotNull HumanEntity player) {
this(pageConfig, pageConfig.getTitle(), player);
}
public PageHandler(@NotNull PageConfig pageConfig, @NotNull String title, @NotNull HumanEntity player) {
this.pageConfig = pageConfig;
this.player = player;
inventory = Bukkit.createInventory(this, pageConfig.getInventory().getSize(), title);
}
public abstract void initPage();
public void onOpen(@NotNull InventoryOpenEvent event) {
}
public void onClick(@NotNull InventoryClickEvent event) {
if (event.getSlot() == event.getRawSlot()) {
return;
}
event.setCancelled(true);
}
public void onClickInside(@NotNull InventoryClickEvent event) {
event.setCancelled(true);
}
public void onClickInside(@NotNull ClickType clickType, @NotNull InventoryAction action, int index) {
}
public void onPlayButtonSound(@NotNull ClickType clickType, @NotNull InventoryAction action, int index) {
Sound sound = getPageConfig().getButtonSound(getButtonGroup().getButtonName(index));
if (sound == null) {
return;
}
if (!(player instanceof Player)) {
return;
}
((Player) player).playSound(player.getLocation(), sound, 1, 1);
}
public void onDrag(@NotNull InventoryDragEvent event) {
}
public void onDragInside(@NotNull InventoryDragEvent event) {
event.setCancelled(true);
}
public void onClose(@NotNull InventoryCloseEvent event) {
}
public void show() {
show(true);
}
public void show(boolean init) {
if (init) {
initPage();
}
HamsterCorePlugin.sync(() -> player.openInventory(getInventory()));
}
public void close() {
HamsterCorePlugin.sync(player::closeInventory);
}
@NotNull
public PageConfig getPageConfig() {
return pageConfig;
}
@NotNull
public HumanEntity getPlayer() {
return player;
}
@NotNull
public ButtonGroup getButtonGroup() {
return getPageConfig().getButtonGroup("default");
}
@NotNull
@Override
public Inventory getInventory() {
return inventory;
}
}

View File

@@ -0,0 +1,185 @@
package cn.hamster3.core.bukkit.page.handler;
import cn.hamster3.core.bukkit.page.ButtonGroup;
import cn.hamster3.core.bukkit.page.PageConfig;
import cn.hamster3.core.bukkit.page.PageElement;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* 支持翻页的 GUI
*
* @param <E> 页面元素
*/
@SuppressWarnings("unused")
public abstract class PageableHandler<E extends PageElement> extends FixedPageHandler {
private String previewButtonName = "preview";
private String nextButtonName = "next";
private String barrierButtonName = "barrier";
private String elementButtonName = "element";
private int page;
private HashMap<Integer, E> elementSlot;
public PageableHandler(@NotNull HumanEntity player, int page) {
super(player);
this.page = page;
}
public PageableHandler(@NotNull HumanEntity player, @NotNull String title, int page) {
super(player, title);
this.page = page;
}
public PageableHandler(@NotNull PageConfig pageConfig, @NotNull HumanEntity player, int page) {
super(pageConfig, player);
this.page = page;
}
public PageableHandler(@NotNull PageConfig pageConfig, @NotNull String title, @NotNull HumanEntity player, int page) {
super(pageConfig, title, player);
this.page = page;
}
@NotNull
public abstract List<E> getPageElements();
public abstract void onClickElement(@NotNull ClickType clickType, @NotNull InventoryAction action, @NotNull E element);
@NotNull
public String getElementButtonName(@NotNull E element) {
return elementButtonName;
}
public void initElementButton(@NotNull E element, @NotNull ItemStack displayItem, HashMap<String, String> replacer) {
element.replaceItemInfo(getPlayer(), displayItem, replacer);
}
@Override
public void initPage() {
super.initPage();
List<E> elements = getPageElements();
ButtonGroup group = getButtonGroup();
Inventory inventory = getInventory();
HumanEntity player = getPlayer();
ArrayList<Integer> buttonIndexes = group.getButtonAllIndex(elementButtonName);
int pageSize = buttonIndexes.size(); // 一页有多少个按钮
elementSlot = new HashMap<>();
HashMap<String, String> replacer = getReplacer();
for (int i = 0; i < pageSize; i++) {
// 元素在当前 page 中的索引位置
int elementIndex = page * pageSize + i;
// 按钮在 GUI 中的索引位置
int buttonIndex = buttonIndexes.get(i);
if (elementIndex >= elements.size()) {
inventory.setItem(buttonIndex, null);
continue;
}
E element = elements.get(elementIndex);
elementSlot.put(buttonIndex, element);
ItemStack elementDisplayItem = element.getDisplayItem(player);
if (elementDisplayItem != null) {
elementDisplayItem = elementDisplayItem.clone();
element.replaceItemInfo(player, elementDisplayItem, replacer);
inventory.setItem(buttonIndex, elementDisplayItem);
continue;
}
ItemStack button = group.getButton(getElementButtonName(element));
if (button == null) {
inventory.setItem(buttonIndex, null);
continue;
}
ItemStack elementItem = button.clone();
initElementButton(element, elementItem, replacer);
inventory.setItem(buttonIndex, elementItem);
}
if (page == 0) {
// 如果页面已在首页则撤掉上一页按钮
inventory.setItem(group.getButtonIndex(previewButtonName), group.getButton(barrierButtonName));
}
if (elements.size() <= (page + 1) * pageSize) {
// 如果页面显示超出已有元素数量则撤掉下一页按钮
inventory.setItem(group.getButtonIndex(nextButtonName), group.getButton(barrierButtonName));
}
}
@Override
public void onClickInside(@NotNull InventoryClickEvent event) {
event.setCancelled(true);
int slot = event.getSlot();
E e = elementSlot.get(slot);
if (e != null) {
onClickElement(event.getClick(), event.getAction(), e);
return;
}
String name = getPageConfig().getButtonName(event.getCurrentItem());
if (name.equalsIgnoreCase(nextButtonName)) {
showNextPage();
return;
}
if (name.equalsIgnoreCase(previewButtonName)) {
showPreviewPage();
}
}
public void showPreviewPage() {
page--;
show();
}
public void showNextPage() {
page++;
show();
}
@NotNull
public HashMap<Integer, E> getElementSlot() {
return elementSlot;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public void setPreviewButtonName(String previewButtonName) {
this.previewButtonName = previewButtonName;
}
public void setNextButtonName(String nextButtonName) {
this.nextButtonName = nextButtonName;
}
public void setBarrierButtonName(String barrierButtonName) {
this.barrierButtonName = barrierButtonName;
}
public void setElementButtonName(String elementButtonName) {
this.elementButtonName = elementButtonName;
}
public HashMap<String, String> getReplacer() {
return new HashMap<>();
}
}

View File

@@ -0,0 +1,120 @@
package cn.hamster3.core.bukkit.page.listener;
import cn.hamster3.core.bukkit.HamsterCorePlugin;
import cn.hamster3.core.bukkit.page.handler.PageHandler;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.Inventory;
/**
* GUI 监听器
*/
public class PageListener implements Listener {
public static final PageListener INSTANCE = new PageListener();
private PageListener() {
}
@EventHandler
public void onInventoryOpen(InventoryOpenEvent event) {
Inventory inventory = event.getView().getTopInventory();
if (!(inventory.getHolder() instanceof PageHandler)) {
return;
}
PageHandler pageHandler = (PageHandler) inventory.getHolder();
pageHandler.onOpen(event);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
Inventory inventory = event.getView().getTopInventory();
if (!(inventory.getHolder() instanceof PageHandler)) {
return;
}
PageHandler pageHandler = (PageHandler) inventory.getHolder();
try {
pageHandler.onClick(event);
} catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format("执行 %s 的 onClick(event) 时遇到了一个异常: ", pageHandler.getClass().getName()));
e.printStackTrace();
}
if (event.isCancelled()) {
return;
}
int index = event.getRawSlot();
if (index < 0) {
return;
}
if (index != event.getSlot()) {
return;
}
try {
pageHandler.onClickInside(event);
} catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format("执行 %s 的 onClickInside(event) 时遇到了一个异常: ", pageHandler.getClass().getName()));
e.printStackTrace();
}
try {
pageHandler.onClickInside(event.getClick(), event.getAction(), index);
} catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format(
"执行 %s 的 onClickInside(%s, %s, %d) 时遇到了一个异常: ",
pageHandler.getClass().getName(),
event.getClick().name(),
event.getAction().name(),
index
));
e.printStackTrace();
}
try {
pageHandler.onPlayButtonSound(event.getClick(), event.getAction(), index);
} catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format(
"执行 %s 的 onPlayButtonSound(%s, %s, %d) 时遇到了一个异常: ",
pageHandler.getClass().getName(),
event.getClick().name(),
event.getAction().name(),
index
));
}
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
Inventory inventory = event.getView().getTopInventory();
if (!(inventory.getHolder() instanceof PageHandler)) {
return;
}
PageHandler pageHandler = (PageHandler) inventory.getHolder();
try {
pageHandler.onDrag(event);
} catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format("执行 %s 的 onDrag(event) 时遇到了一个异常: ", pageHandler.getClass().getName()));
e.printStackTrace();
}
if (event.isCancelled()) {
return;
}
int size = inventory.getSize();
for (Integer slot : event.getRawSlots()) {
if (slot < size) {
pageHandler.onDragInside(event);
break;
}
}
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
Inventory inventory = event.getView().getTopInventory();
if (!(inventory.getHolder() instanceof PageHandler)) {
return;
}
PageHandler pageHandler = (PageHandler) inventory.getHolder();
pageHandler.onClose(event);
}
}

View File

@@ -0,0 +1,194 @@
package cn.hamster3.core.bukkit.util;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@SuppressWarnings("unused")
public class BukkitUtils {
@NotNull
public static String getMCVersion() {
return Bukkit.getBukkitVersion().split("-")[0];
}
@NotNull
public static String getNMSVersion() {
return Bukkit.getServer().getClass().getName().split("\\.")[3];
}
@NotNull
public static Package getNMSPackage() {
String nmsVersion = getNMSVersion();
return Package.getPackage("net.minecraft.server." + nmsVersion);
}
@NotNull
public static Class<?> getNMSClass(@NotNull String className) throws ClassNotFoundException {
String nmsVersion = getNMSVersion();
return Class.forName("net.minecraft.server." + nmsVersion + "." + className);
}
/**
* 让玩家以最高权限执行命令
*
* @param player 玩家
* @param command 要执行的命令
*/
public static void opCommand(@NotNull Player player, @NotNull String command) {
boolean isOp = player.isOp();
player.setOp(true);
Bukkit.dispatchCommand(player, command);
player.setOp(isOp);
}
/**
* 获取玩家的头颅
* 在1.11以上的服务端中获取头颅材质是在服务器上运行的
* 因此建议使用异步线程调用该方法
*
* @param uuid 要获取的玩家
* @return 玩家的头颅物品
*/
@NotNull
public static ItemStack getPlayerHead(@NotNull UUID uuid) {
return getPlayerHead(Bukkit.getOfflinePlayer(uuid));
}
/**
* 获取玩家的头颅
* 在1.11以上的服务端中获取头颅材质是在服务器上运行的
* 因此建议使用异步线程调用该方法
*
* @param offlinePlayer 要获取的玩家
* @return 玩家的头颅物品
*/
@NotNull
public static ItemStack getPlayerHead(@NotNull OfflinePlayer offlinePlayer) {
ItemStack stack;
try {
stack = new ItemStack(Material.valueOf("PLAYER_HEAD"));
} catch (IllegalArgumentException e) {
stack = new ItemStack(Material.valueOf("SKULL_ITEM"), 1, (short) 3);
}
SkullMeta meta = (SkullMeta) stack.getItemMeta();
if (meta != null) {
meta.setOwningPlayer(offlinePlayer);
}
stack.setItemMeta(meta);
return stack;
}
/**
* 获取玩家的头颅
* 在1.11以上的服务端中获取头颅材质是在服务器上运行的
* 因此建议使用异步线程调用该方法
*
* @param name 要获取的玩家
* @return 玩家的头颅物品
*/
@NotNull
@SuppressWarnings("deprecation")
public static ItemStack getPlayerHead(@NotNull String name) {
ItemStack stack;
try {
stack = new ItemStack(Material.valueOf("PLAYER_HEAD"));
} catch (IllegalArgumentException e) {
stack = new ItemStack(Material.valueOf("SKULL_ITEM"), 1, (short) 3);
}
SkullMeta meta = (SkullMeta) stack.getItemMeta();
meta.setOwner(name);
stack.setItemMeta(meta);
return stack;
}
/**
* 判断物品是否为空
* 当对象为null时返回true
* 当物品的Material为AIR时返回true
* 当物品的数量小于1时返回true
*
* @param stack 物品
* @return 是否为空
*/
public static boolean isEmptyItemStack(ItemStack stack) {
return stack == null || stack.getType() == Material.AIR || stack.getAmount() < 1;
}
/**
* 给予玩家一个物品, 当玩家背包满时
* 将会在玩家附近生成这个物品的掉落物
*
* @param player 玩家
* @param stack 物品
*/
public static void giveItem(@NotNull HumanEntity player, @NotNull ItemStack stack) {
if (isEmptyItemStack(stack)) {
return;
}
World world = player.getWorld();
for (ItemStack dropItem : player.getInventory().addItem(stack).values()) {
world.dropItem(player.getLocation(), dropItem);
}
}
/**
* 获取物品的名称
* 当物品为null时返回"null"
* 当物品拥有DisplayName时返回DisplayName
* 否则返回物品的Material的name
*
* @param stack 物品
* @return 物品的名称
*/
@NotNull
public static String getItemName(ItemStack stack) {
if (stack == null) {
return "null";
}
if (stack.hasItemMeta()) {
ItemMeta meta = stack.getItemMeta();
if (meta.hasDisplayName()) {
return meta.getDisplayName();
}
}
return stack.getType().name();
}
/**
* 创建 SQL 连接池
*
* @param config SQL 配置
* @return SQL 连接池
*/
@NotNull
public static HikariDataSource getHikariDataSource(@NotNull ConfigurationSection config) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(config.getString("driver"));
hikariConfig.setJdbcUrl(config.getString("url"));
hikariConfig.setUsername(config.getString("user"));
hikariConfig.setPassword(config.getString("password"));
hikariConfig.setMaximumPoolSize(config.getInt("maximumPoolSize", 3));
hikariConfig.setMinimumIdle(config.getInt("minimumIdle", 1));
hikariConfig.setIdleTimeout(config.getLong("idleTimeout", 5 * 60 * 1000));
hikariConfig.setMaxLifetime(config.getLong("maxLifetime", 0));
return new HikariDataSource(hikariConfig);
}
// todo public static TextComponent getItemDisplayInfo(@NotNull ItemStack stack)
}

View File

@@ -0,0 +1,3 @@
name: HamsterCore
main: cn.hamster3.core.bukkit.HamsterCorePlugin
version: ${version}