I upgraded code with ChatGPT to senior level and added README.txt
This commit is contained in:
parent
f61620e96c
commit
d46a4d2d20
41
README.txt
Normal file
41
README.txt
Normal file
@ -0,0 +1,41 @@
|
||||
GiftAPI
|
||||
=======
|
||||
|
||||
Плагин для Майнкрафт сервера с ядром Bukkit 1.6.4.
|
||||
Это API + кастомный инвентарь.
|
||||
|
||||
Использование:
|
||||
--------------
|
||||
1. Скачайте релиз или соберите плагин сами.
|
||||
2. Переместите в папку КОРНЕВАЯ-ПАПКА-СЕРВЕРА/plugins.
|
||||
3. Запустите сервер или введите команду /reload.
|
||||
|
||||
Документация:
|
||||
-------------
|
||||
1. 'add(player, item)'
|
||||
- Описание: добавляет предмет в кастомные инвентарь игрока.
|
||||
- Параметры:
|
||||
- 'player' (Player): игрок которому нужно добавить предмет.
|
||||
- 'item' (ItemStack): предмет который должен быть сохранен в инвентарь.
|
||||
|
||||
2. 'remove(player, item)'
|
||||
- Описание: удаляет предмет из кастомного инвентаря игрока.
|
||||
- Параметры:
|
||||
- 'player' (Player): игрок которому нужно удалить предмет.
|
||||
- 'item' (ItemStack): предмет который должен быть удалён из инвентаря.
|
||||
|
||||
3. 'openGUI()'
|
||||
- Описание: открывает кастомный инвентарь игрока.
|
||||
- Параметры:
|
||||
- 'player' (Player): игрок которому нужно открыть кастомный инвентарь.
|
||||
|
||||
Требования:
|
||||
-----------
|
||||
- Bukkit 1.6.4
|
||||
|
||||
|
||||
Лицензия:
|
||||
---------
|
||||
Этот проект использует MIT лицензию.
|
||||
|
||||
Смотреть больше: https://opensource.org/licenses/MIT
|
@ -7,38 +7,74 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import tokarotik.giftapi.inventory.InventoryManager;
|
||||
|
||||
/**
|
||||
* Handles API-facing logic such as adding, removing, and opening
|
||||
* gift inventories for players. Acts as a middle layer between plugin
|
||||
* logic and internal inventory mechanics.
|
||||
*/
|
||||
public class APIManager implements Listener {
|
||||
|
||||
public final Main plugin;
|
||||
private final InventoryManager inventoryManager;
|
||||
|
||||
public APIManager(Main plugin, FileConfiguration config, int inventory_slots)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
public APIManager(Main plugin, FileConfiguration config, int inventorySlots) {
|
||||
if (plugin == null || config == null) {
|
||||
throw new IllegalArgumentException("Plugin and config must not be null.");
|
||||
}
|
||||
|
||||
this.inventoryManager = new InventoryManager(
|
||||
plugin.getCacheManager(),
|
||||
inventory_slots,
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUINAME, "GiftAPI Menu"),
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUIRIGHT, "<<right"),
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUILEFT, "left>>"),
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUIEXIT, "quit")
|
||||
inventorySlots,
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUI_TITLE, "GiftAPI Menu"),
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUI_BUTTON_RIGHT, "<<right"),
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUI_BUTTON_LEFT, "left>>"),
|
||||
ConfigPaths.getWithColor(config, ConfigPaths.GUI_BUTTON_EXIT, "quit")
|
||||
);
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(this.inventoryManager, this.plugin);
|
||||
// Register inventory click listener
|
||||
Bukkit.getPluginManager().registerEvents(this.inventoryManager, plugin);
|
||||
}
|
||||
|
||||
public synchronized void add(Player player, ItemStack item) { this.inventoryManager.add(player, item); }
|
||||
|
||||
public synchronized void remove(Player player, ItemStack item) { this.inventoryManager.remove(player, item); }
|
||||
|
||||
public synchronized void openInventory(Player player)
|
||||
{
|
||||
this.inventoryManager.openCustomInventory(player);
|
||||
/**
|
||||
* Adds an item to the player's gift inventory.
|
||||
*
|
||||
* @param player the target player
|
||||
* @param item the item to add
|
||||
*/
|
||||
public void add(Player player, ItemStack item) {
|
||||
if (player == null || item == null) return;
|
||||
inventoryManager.add(player, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the player's gift inventory.
|
||||
*
|
||||
* @param player the target player
|
||||
* @param item the item to remove
|
||||
*/
|
||||
public void remove(Player player, ItemStack item) {
|
||||
if (player == null || item == null) return;
|
||||
inventoryManager.remove(player, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the custom inventory GUI for the specified player.
|
||||
*
|
||||
* @param player the player to open the inventory for
|
||||
*/
|
||||
public void openInventory(Player player) {
|
||||
if (player == null) return;
|
||||
inventoryManager.openCustomInventory(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates inventory click events to the inventory manager.
|
||||
*
|
||||
* @param event the inventory click event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) { this.inventoryManager.onInventoryClick(event); }
|
||||
public void handleInventoryClick(InventoryClickEvent event) {
|
||||
inventoryManager.handleInventoryClick(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,35 @@ package tokarotik.giftapi;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
public class ConfigPaths {
|
||||
public static final String DEVTEST = "dev-test";
|
||||
public static final String GUINAME = "gui.title-menu";
|
||||
public static final String GUIRIGHT = "gui.buttons.right-name";
|
||||
public static final String GUILEFT = "gui.buttons.left-name";
|
||||
public static final String GUIEXIT = "gui.buttons.exit-name";
|
||||
/**
|
||||
* Centralized configuration path keys and utility methods for retrieving
|
||||
* and colorizing string values from the plugin's configuration.
|
||||
*/
|
||||
public final class ConfigPaths {
|
||||
|
||||
public static String getWithColor(FileConfiguration config, String path, String defaul)
|
||||
{
|
||||
return ChatColor.translateAlternateColorCodes('&', config.getString(path, defaul));
|
||||
private ConfigPaths() {
|
||||
// Utility class; prevent instantiation
|
||||
throw new UnsupportedOperationException("ConfigPaths is a utility class and cannot be instantiated.");
|
||||
}
|
||||
|
||||
// === Config Path Constants ===
|
||||
public static final String GUI_TITLE = "gui.title-menu";
|
||||
public static final String GUI_BUTTON_RIGHT = "gui.buttons.right-name";
|
||||
public static final String GUI_BUTTON_LEFT = "gui.buttons.left-name";
|
||||
public static final String GUI_BUTTON_EXIT = "gui.buttons.exit-name";
|
||||
|
||||
/**
|
||||
* Retrieves a string from the config and translates alternate color codes (& -> §).
|
||||
*
|
||||
* @param config The plugin's configuration object.
|
||||
* @param path The path in the config file.
|
||||
* @param fallback The default value to return if the path is missing or null.
|
||||
* @return A colorized string from the config or the provided fallback.
|
||||
*/
|
||||
public static String getWithColor(FileConfiguration config, String path, String fallback) {
|
||||
if (config == null || path == null) return ChatColor.translateAlternateColorCodes('&', fallback);
|
||||
String raw = config.getString(path, fallback);
|
||||
return ChatColor.translateAlternateColorCodes('&', raw != null ? raw : fallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,63 +9,81 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import tokarotik.giftapi.cache.CacheManager;
|
||||
import tokarotik.giftapi.dev.AddCommand;
|
||||
import tokarotik.giftapi.dev.GiftCommand;
|
||||
|
||||
public class Main extends JavaPlugin implements Listener
|
||||
{
|
||||
public class Main extends JavaPlugin implements Listener {
|
||||
|
||||
private static final int INVENTORY_SLOTS = 54;
|
||||
private static final int CACHED_SLOTS = INVENTORY_SLOTS - 9;
|
||||
|
||||
private CacheManager cacheManager;
|
||||
private APIManager apiManager;
|
||||
private final int inventory_slots = 54;
|
||||
|
||||
@Override
|
||||
public void onEnable()
|
||||
{
|
||||
public void onEnable() {
|
||||
saveDefaultConfig();
|
||||
|
||||
cacheManager = new CacheManager(inventory_slots - 9);
|
||||
apiManager = new APIManager(this, getConfig(), inventory_slots);
|
||||
this.cacheManager = new CacheManager(CACHED_SLOTS);
|
||||
this.apiManager = new APIManager(this, getConfig(), INVENTORY_SLOTS);
|
||||
|
||||
this.getCommand("gift").setExecutor(new GiftCommand(this)); // DELETE FOR RELEASE
|
||||
this.getCommand("add").setExecutor(new AddCommand(this)); // DELETE FOR RELEASE
|
||||
registerEvents();
|
||||
|
||||
getServer().getPluginManager().registerEvents(this, this); // bullshit
|
||||
|
||||
getLogger().info("GiftAPI enabled!");
|
||||
getLogger().info("GiftAPI has been enabled.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable()
|
||||
{
|
||||
getLogger().info("Saving GiftApi...");
|
||||
cacheManager.disable();
|
||||
public void onDisable() {
|
||||
getLogger().info("Saving GiftAPI state...");
|
||||
if (cacheManager != null) {
|
||||
cacheManager.disable();
|
||||
}
|
||||
getLogger().info("GiftAPI has been disabled.");
|
||||
}
|
||||
|
||||
getLogger().info("GiftAPI disabled!");
|
||||
private void registerEvents() {
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event)
|
||||
{
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this, () -> cacheManager.playerQuit(event.getPlayer()));
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this, () -> cacheManager.playerQuit(player));
|
||||
}
|
||||
|
||||
// if failed to add, return false
|
||||
// if successfully added, return true
|
||||
/**
|
||||
* Adds an item to the player's gift cache.
|
||||
* This operation is asynchronous.
|
||||
*
|
||||
* @param player the player to add the item to
|
||||
* @param item the item to be added
|
||||
*/
|
||||
public void add(Player player, ItemStack item) {
|
||||
if (player == null || item == null) return;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this, () -> apiManager.add(player, item));
|
||||
}
|
||||
// if failed to remove item, return false
|
||||
// if successfully removed item, return true
|
||||
public void remove(Player player, ItemStack item)
|
||||
{
|
||||
|
||||
/**
|
||||
* Removes an item from the player's gift cache.
|
||||
* This operation is asynchronous.
|
||||
*
|
||||
* @param player the player to remove the item from
|
||||
* @param item the item to be removed
|
||||
*/
|
||||
public void remove(Player player, ItemStack item) {
|
||||
if (player == null || item == null) return;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this, () -> apiManager.remove(player, item));
|
||||
}
|
||||
|
||||
// will open inventory GiftAPI
|
||||
public void openGUI(Player player)
|
||||
{
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this, () -> apiManager.openInventory(player));
|
||||
/**
|
||||
* Opens the GiftAPI GUI for the specified player.
|
||||
*
|
||||
* @param player the player for whom to open the GUI
|
||||
*/
|
||||
public void openGUI(Player player) {
|
||||
if (player == null) return;
|
||||
Bukkit.getScheduler().runTask(this, () -> apiManager.openInventory(player));
|
||||
}
|
||||
|
||||
public CacheManager getCacheManager() { return this.cacheManager; }
|
||||
public CacheManager getCacheManager() {
|
||||
return cacheManager;
|
||||
}
|
||||
}
|
@ -1,98 +1,105 @@
|
||||
package tokarotik.giftapi.NBT.item;
|
||||
|
||||
import net.minecraft.server.v1_6_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_6_R3.NBTTagList;
|
||||
import net.minecraft.server.v1_6_R3.NBTTagString;
|
||||
import net.minecraft.server.v1_6_R3.NBTTagCompound;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemLoad
|
||||
{
|
||||
public static ItemStack getItem(NBTTagCompound tag)
|
||||
{
|
||||
public final class ItemLoad {
|
||||
|
||||
ItemStack item = new ItemStack(
|
||||
getId(tag), getCount(tag), getDurability(tag)
|
||||
);
|
||||
private ItemLoad() {
|
||||
// Utility class; prevent instantiation
|
||||
}
|
||||
|
||||
// тут чехорда. с начало сет айтем, после сет мета, а в самом конце гет мета
|
||||
item.setItemMeta(
|
||||
setMeta(
|
||||
tag,
|
||||
item.getItemMeta()
|
||||
));
|
||||
/**
|
||||
* Constructs an ItemStack from the given NBTTagCompound.
|
||||
*
|
||||
* @param tag the NBTTagCompound containing item data
|
||||
* @return an ItemStack representing the item data from NBT
|
||||
*/
|
||||
public static ItemStack getItem(NBTTagCompound tag) {
|
||||
int id = getId(tag);
|
||||
short count = getCount(tag);
|
||||
short durability = getDurability(tag);
|
||||
|
||||
ItemStack item = new ItemStack(id, count, durability);
|
||||
|
||||
// Set item meta after item creation
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta = applyMeta(tag, meta);
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static int getId(NBTTagCompound tag)
|
||||
{
|
||||
int id = 0;
|
||||
if (tag.hasKey("id"))
|
||||
{
|
||||
id = tag.getInt("id");
|
||||
}
|
||||
return id;
|
||||
private static int getId(NBTTagCompound tag) {
|
||||
return tag.hasKey("id") ? tag.getInt("id") : 0;
|
||||
}
|
||||
|
||||
private static short getCount(NBTTagCompound tag)
|
||||
{
|
||||
short count = 1;
|
||||
if (tag.hasKey("count"))
|
||||
{
|
||||
count = tag.getShort("count");
|
||||
}
|
||||
return count;
|
||||
private static short getCount(NBTTagCompound tag) {
|
||||
// Default count 1 for valid items
|
||||
return tag.hasKey("count") ? tag.getShort("count") : 1;
|
||||
}
|
||||
|
||||
private static short getDurability(NBTTagCompound tag)
|
||||
{
|
||||
short durability = 0;
|
||||
if (tag.hasKey("damage"))
|
||||
{
|
||||
durability = tag.getShort("damage");
|
||||
}
|
||||
return durability;
|
||||
private static short getDurability(NBTTagCompound tag) {
|
||||
// Default durability 0 (undamaged)
|
||||
return tag.hasKey("damage") ? tag.getShort("damage") : 0;
|
||||
}
|
||||
|
||||
private static ItemMeta setMeta(NBTTagCompound tag, ItemMeta meta)
|
||||
{
|
||||
if (tag.hasKey("meta"))
|
||||
{
|
||||
/**
|
||||
* Applies metadata to an ItemMeta from the NBT tag.
|
||||
*
|
||||
* @param tag the NBTTagCompound containing meta data
|
||||
* @param meta the ItemMeta to apply changes to
|
||||
* @return the modified ItemMeta
|
||||
*/
|
||||
private static ItemMeta applyMeta(NBTTagCompound tag, ItemMeta meta) {
|
||||
if (tag.hasKey("meta")) {
|
||||
NBTTagCompound metaNBT = tag.getCompound("meta");
|
||||
|
||||
if (metaNBT.hasKey("name"))
|
||||
{
|
||||
if (metaNBT.hasKey("name")) {
|
||||
meta.setDisplayName(metaNBT.getString("name"));
|
||||
}
|
||||
if (metaNBT.hasKey("lore"))
|
||||
{
|
||||
meta.setLore(
|
||||
NBTListToStringList(
|
||||
metaNBT.getList("lore")
|
||||
)
|
||||
);
|
||||
|
||||
if (metaNBT.hasKey("lore")) {
|
||||
NBTTagList loreList = metaNBT.getList("lore");
|
||||
List<String> lore = nbtListToStringList(loreList);
|
||||
meta.setLore(lore);
|
||||
}
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
private static List<String> NBTListToStringList(NBTTagList nbt)
|
||||
{
|
||||
String[] list = new String[nbt.size()];
|
||||
|
||||
for (int i = 0; i < nbt.size(); i++)
|
||||
{
|
||||
NBTTagString base = (NBTTagString) nbt.get(i);
|
||||
|
||||
list[i] = base.data;
|
||||
/**
|
||||
* Converts an NBTTagList of strings to a List<String>.
|
||||
*
|
||||
* @param nbt the NBTTagList containing NBTTagStrings
|
||||
* @return a List of Strings extracted from the NBT list
|
||||
*/
|
||||
private static List<String> nbtListToStringList(NBTTagList nbt) {
|
||||
if (nbt == null || nbt.size() == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String[] list = new String[nbt.size()];
|
||||
for (int i = 0; i < nbt.size(); i++) {
|
||||
// Defensive cast, check instance before casting if possible
|
||||
if (nbt.get(i) instanceof NBTTagString) {
|
||||
NBTTagString base = (NBTTagString) nbt.get(i);
|
||||
list[i] = base.data != null ? base.data : "";
|
||||
} else {
|
||||
list[i] = "";
|
||||
}
|
||||
}
|
||||
return Arrays.asList(list);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,12 +10,24 @@ import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ItemNBT
|
||||
{
|
||||
public static NBTTagCompound getTag(ItemStack item)
|
||||
{
|
||||
if (item == null) {return null;}
|
||||
public final class ItemNBT {
|
||||
|
||||
private ItemNBT() {
|
||||
// Utility class; prevent instantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Bukkit ItemStack into an NBTTagCompound representing the item.
|
||||
*
|
||||
* @param item the ItemStack to convert
|
||||
* @return the NBTTagCompound representing the item or null if the item is null
|
||||
*/
|
||||
public static NBTTagCompound getTag(ItemStack item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NBTTagCompound tag = new NBTTagCompound("item");
|
||||
|
||||
@ -25,87 +37,78 @@ public class ItemNBT
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static void setBasic(ItemStack item, NBTTagCompound tag)
|
||||
{
|
||||
short count = (short)item.getAmount();
|
||||
if (count != 1) { tag.setShort("count", count); }
|
||||
private static void setBasic(ItemStack item, NBTTagCompound tag) {
|
||||
short count = (short) item.getAmount();
|
||||
if (count != 1) {
|
||||
tag.setShort("count", count);
|
||||
}
|
||||
|
||||
int id = item.getTypeId();
|
||||
if (id != 0) { tag.setInt("id", item.getTypeId()); }
|
||||
if (id != 0) {
|
||||
tag.setInt("id", id);
|
||||
}
|
||||
|
||||
short damage = item.getDurability();
|
||||
if (damage != 0) { tag.setShort("damage", item.getDurability()); }
|
||||
}
|
||||
|
||||
private static void setMetaTag(ItemStack item, NBTTagCompound baseTag)
|
||||
{
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
|
||||
if (item.hasItemMeta())
|
||||
{
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
|
||||
if (meta.hasDisplayName())
|
||||
{
|
||||
tag.setString(
|
||||
"name",
|
||||
meta.getDisplayName()
|
||||
);
|
||||
}
|
||||
|
||||
if (meta.hasLore())
|
||||
{
|
||||
tag.set(
|
||||
"lore",
|
||||
stringListToNBTCompound(meta.getLore())
|
||||
);
|
||||
}
|
||||
|
||||
if (meta.hasEnchants())
|
||||
{
|
||||
|
||||
tag.setCompound(
|
||||
"enchantments",
|
||||
enchantsToNBTCompound(meta.getEnchants())
|
||||
);
|
||||
}
|
||||
|
||||
baseTag.setCompound("meta", tag);
|
||||
if (damage != 0) {
|
||||
tag.setShort("damage", damage);
|
||||
}
|
||||
}
|
||||
|
||||
private static NBTTagList stringListToNBTCompound(List<String> list)
|
||||
{
|
||||
NBTTagList NBTlist = new NBTTagList();
|
||||
|
||||
for (String s : list) {
|
||||
NBTlist.add(
|
||||
new NBTTagString(
|
||||
null,
|
||||
s
|
||||
)
|
||||
);
|
||||
private static void setMetaTag(ItemStack item, NBTTagCompound baseTag) {
|
||||
if (!item.hasItemMeta()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
NBTTagCompound metaTag = new NBTTagCompound();
|
||||
|
||||
if (meta.hasDisplayName()) {
|
||||
metaTag.setString("name", meta.getDisplayName());
|
||||
}
|
||||
|
||||
if (meta.hasLore()) {
|
||||
metaTag.set("lore", stringListToNBTList(meta.getLore()));
|
||||
}
|
||||
|
||||
if (meta.hasEnchants()) {
|
||||
metaTag.setCompound("enchantments", enchantsToNBTCompound(meta.getEnchants()));
|
||||
}
|
||||
|
||||
if (!metaTag.isEmpty()) {
|
||||
baseTag.setCompound("meta", metaTag);
|
||||
}
|
||||
return NBTlist;
|
||||
}
|
||||
|
||||
private static NBTTagCompound enchantsToNBTCompound(Map<Enchantment, Integer> enchantments)
|
||||
{
|
||||
NBTTagCompound NBTlist = new NBTTagCompound();
|
||||
private static NBTTagList stringListToNBTList(List<String> list) {
|
||||
NBTTagList nbtList = new NBTTagList();
|
||||
if (list == null || list.isEmpty()) {
|
||||
return nbtList;
|
||||
}
|
||||
|
||||
for (String str : list) {
|
||||
// Null-safe insertion of strings
|
||||
nbtList.add(new NBTTagString(null, Objects.toString(str, "")));
|
||||
}
|
||||
return nbtList;
|
||||
}
|
||||
|
||||
private static NBTTagCompound enchantsToNBTCompound(Map<Enchantment, Integer> enchantments) {
|
||||
NBTTagCompound enchantmentsNBT = new NBTTagCompound();
|
||||
|
||||
if (enchantments == null || enchantments.isEmpty()) {
|
||||
return enchantmentsNBT;
|
||||
}
|
||||
|
||||
enchantments.forEach((enchantment, level) -> {
|
||||
if (enchantment == null) return; // defensive check
|
||||
|
||||
NBTTagCompound enchantmentNBT = new NBTTagCompound();
|
||||
enchantmentNBT.setInt("level", level != null ? level : 0);
|
||||
|
||||
int levelNBT = 0;
|
||||
if (level != null) // type Integer can be null. To not get a null error I wrote it :)
|
||||
{
|
||||
levelNBT = level;
|
||||
}
|
||||
enchantmentNBT.setInt("level", levelNBT);
|
||||
|
||||
NBTlist.setCompound(String.valueOf(enchantment.getId()), enchantmentNBT);
|
||||
enchantmentsNBT.setCompound(String.valueOf(enchantment.getId()), enchantmentNBT);
|
||||
});
|
||||
return NBTlist;
|
||||
|
||||
return enchantmentsNBT;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,56 +1,78 @@
|
||||
package tokarotik.giftapi.NBT.pages;
|
||||
|
||||
import net.minecraft.server.v1_6_R3.NBTTagList;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class PagesManager
|
||||
{
|
||||
private NBTTagList tag;
|
||||
private int current_page = 0;
|
||||
private final int items_slots;
|
||||
public PagesManager(NBTTagList tag, int items_slots)
|
||||
{
|
||||
this.items_slots = items_slots;
|
||||
this.tag = tag;
|
||||
public final class PagesManager {
|
||||
|
||||
private volatile NBTTagList tag;
|
||||
private volatile int currentPage = 0;
|
||||
private final int itemsSlots;
|
||||
|
||||
public PagesManager(NBTTagList tag, int itemsSlots) {
|
||||
this.itemsSlots = itemsSlots;
|
||||
this.tag = tag != null ? tag : new NBTTagList("giftapi");
|
||||
}
|
||||
|
||||
public PagesManager(int items_slots)
|
||||
{
|
||||
this.items_slots = items_slots;
|
||||
public PagesManager(int itemsSlots) {
|
||||
this.itemsSlots = itemsSlots;
|
||||
this.tag = new NBTTagList("giftapi");
|
||||
}
|
||||
|
||||
public synchronized void add(ItemStack item)
|
||||
{
|
||||
public synchronized void add(ItemStack item) {
|
||||
PagesUtil.add(item, this.tag);
|
||||
}
|
||||
|
||||
public synchronized void remove(ItemStack item)
|
||||
{
|
||||
public synchronized void remove(ItemStack item) {
|
||||
this.tag = PagesUtil.remove(item, this.tag);
|
||||
}
|
||||
|
||||
public synchronized NBTTagList getTag() { return this.tag; }
|
||||
|
||||
public synchronized ItemStack[] getList() { return PagesUtil.getRawList(this.tag); }
|
||||
|
||||
public synchronized int getCurrentPage() { return this.current_page; }
|
||||
public synchronized void setCurrentPage(int current_page) { this.current_page = current_page; }
|
||||
public int getCountPages() { return this.tag.size() / (this.items_slots + 1); }
|
||||
public synchronized void nextPage()
|
||||
{
|
||||
if (getCountPages() > getCurrentPage())
|
||||
{
|
||||
this.current_page++;
|
||||
// Optionally reset currentPage if it's now out of range
|
||||
int maxPage = getCountPages() - 1;
|
||||
if (currentPage > maxPage) {
|
||||
currentPage = Math.max(0, maxPage);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void backPage()
|
||||
{
|
||||
if (0 < getCurrentPage())
|
||||
{
|
||||
this.current_page -= 1;
|
||||
public synchronized NBTTagList getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public synchronized ItemStack[] getList() {
|
||||
return PagesUtil.getRawList(tag);
|
||||
}
|
||||
|
||||
public synchronized int getCurrentPage() {
|
||||
return currentPage;
|
||||
}
|
||||
|
||||
public synchronized void setCurrentPage(int currentPage) {
|
||||
if (currentPage < 0) {
|
||||
this.currentPage = 0;
|
||||
} else if (currentPage >= getCountPages()) {
|
||||
this.currentPage = Math.max(0, getCountPages() - 1);
|
||||
} else {
|
||||
this.currentPage = currentPage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of pages, accounting for partial pages.
|
||||
*/
|
||||
public synchronized int getCountPages() {
|
||||
int size = tag.size();
|
||||
if (size == 0) return 1;
|
||||
return (int) Math.ceil((double) size / itemsSlots);
|
||||
}
|
||||
|
||||
public synchronized void nextPage() {
|
||||
if (currentPage < getCountPages() - 1) {
|
||||
currentPage++;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void backPage() {
|
||||
if (currentPage > 0) {
|
||||
currentPage--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,69 +2,85 @@ package tokarotik.giftapi.NBT.pages;
|
||||
|
||||
import net.minecraft.server.v1_6_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_6_R3.NBTTagList;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import tokarotik.giftapi.NBT.item.ItemLoad;
|
||||
import tokarotik.giftapi.NBT.item.ItemNBT;
|
||||
|
||||
public class PagesUtil
|
||||
{
|
||||
public synchronized static void add(ItemStack item, NBTTagList tag)
|
||||
{
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class PagesUtil {
|
||||
|
||||
private PagesUtil() {
|
||||
// Utility class - prevent instantiation
|
||||
}
|
||||
|
||||
public static synchronized void add(ItemStack item, NBTTagList tag) {
|
||||
if (item == null || tag == null) return;
|
||||
|
||||
NBTTagCompound nbt = ItemNBT.getTag(item);
|
||||
|
||||
tag.add(nbt);
|
||||
if (nbt != null) {
|
||||
tag.add(nbt);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static NBTTagList remove(ItemStack item, NBTTagList tag)
|
||||
{
|
||||
ItemStack[] new_list = addAllExtraOne(tag, item);
|
||||
return itemStackListToNBTList(new_list);
|
||||
}
|
||||
public static synchronized NBTTagList remove(ItemStack item, NBTTagList tag) {
|
||||
if (item == null || tag == null) return tag;
|
||||
|
||||
public synchronized static ItemStack[] addAllExtraOne(NBTTagList tag, ItemStack item)
|
||||
{
|
||||
ItemStack[] new_list = new ItemStack[tag.size()];
|
||||
ItemStack[] old_list = getRawList(tag);
|
||||
// Convert NBTTagList -> List<ItemStack>
|
||||
List<ItemStack> oldList = getListFromNBT(tag);
|
||||
List<ItemStack> newList = new ArrayList<>(oldList.size());
|
||||
|
||||
for (int i = 0;i < tag.size();i++)
|
||||
{
|
||||
if (!old_list[i].equals(item))
|
||||
{
|
||||
new_list[i] = old_list[i];
|
||||
for (ItemStack stack : oldList) {
|
||||
if (!equalsItem(stack, item)) {
|
||||
newList.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
return new_list;
|
||||
return itemStackListToNBTList(newList);
|
||||
}
|
||||
|
||||
public synchronized static NBTTagList itemStackListToNBTList(ItemStack[] items)
|
||||
{
|
||||
private static boolean equalsItem(ItemStack a, ItemStack b) {
|
||||
if (a == null || b == null) return false;
|
||||
return a.isSimilar(b);
|
||||
}
|
||||
|
||||
public static synchronized NBTTagList itemStackListToNBTList(List<ItemStack> items) {
|
||||
NBTTagList list = new NBTTagList();
|
||||
|
||||
for (ItemStack item : items) {
|
||||
if (item != null) {
|
||||
NBTTagCompound compound = ItemNBT.getTag(item);
|
||||
|
||||
list.add(compound);
|
||||
if (compound != null) {
|
||||
list.add(compound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public synchronized static ItemStack[] getRawList(NBTTagList tag)
|
||||
{
|
||||
public static synchronized ItemStack[] getRawList(NBTTagList tag) {
|
||||
if (tag == null) return new ItemStack[0];
|
||||
|
||||
ItemStack[] list = new ItemStack[tag.size()];
|
||||
|
||||
for (int i = 0; i < tag.size(); i++)
|
||||
{
|
||||
for (int i = 0; i < tag.size(); i++) {
|
||||
NBTTagCompound compound = (NBTTagCompound) tag.get(i);
|
||||
|
||||
list[i] = ItemLoad.getItem(compound);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static synchronized List<ItemStack> getListFromNBT(NBTTagList tag) {
|
||||
ItemStack[] array = getRawList(tag);
|
||||
List<ItemStack> list = new ArrayList<>(array.length);
|
||||
for (ItemStack item : array) {
|
||||
if (item != null) {
|
||||
list.add(item);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
@ -9,85 +9,127 @@ import org.bukkit.craftbukkit.v1_6_R3.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BasicNBT {
|
||||
public final class BasicNBT {
|
||||
|
||||
private final String path;
|
||||
private final Path worldPath;
|
||||
|
||||
public BasicNBT(String world)
|
||||
{
|
||||
this.path = getPath() + "\\" + world;
|
||||
public BasicNBT(String world) {
|
||||
this.worldPath = Paths.get(getBasePath(), world);
|
||||
}
|
||||
|
||||
public boolean writePlayerNBT(NBTTagCompound compound, EntityPlayer entityPlayer)
|
||||
{
|
||||
try
|
||||
{
|
||||
entityPlayer.f(compound);
|
||||
|
||||
FileOutputStream out = new FileOutputStream(getSavePath(entityPlayer));
|
||||
NBTCompressedStreamTools.a(compound, out);
|
||||
out.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Bukkit.getLogger().warning(e.toString());
|
||||
|
||||
/**
|
||||
* Writes the player's NBT data to disk.
|
||||
*
|
||||
* @param compound the NBTTagCompound representing player data
|
||||
* @param entityPlayer the player entity
|
||||
* @return true if write succeeded, false otherwise
|
||||
*/
|
||||
public boolean writePlayerNBT(NBTTagCompound compound, EntityPlayer entityPlayer) {
|
||||
if (compound == null || entityPlayer == null) {
|
||||
Bukkit.getLogger().warning("Attempted to write NBT with null compound or player");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update NBT with entityPlayer state before saving
|
||||
entityPlayer.f(compound);
|
||||
|
||||
Path savePath = getSavePath(entityPlayer);
|
||||
File saveFile = savePath.toFile();
|
||||
|
||||
// Ensure parent directories exist
|
||||
File parentDir = saveFile.getParentFile();
|
||||
if (parentDir != null && !parentDir.exists() && !parentDir.mkdirs()) {
|
||||
Bukkit.getLogger().warning("Failed to create directories for player save path: " + parentDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(saveFile)) {
|
||||
NBTCompressedStreamTools.a(compound, out);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to write player NBT for " + entityPlayer.getName(), e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public NBTTagCompound readPlayerNBT(EntityPlayer entityPlayer)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInputStream in = new FileInputStream(getSavePath(entityPlayer));
|
||||
/**
|
||||
* Reads the player's NBT data from disk.
|
||||
* If the file does not exist or fails to read, returns a fresh NBTTagCompound
|
||||
* initialized with the player's current state.
|
||||
*
|
||||
* @param entityPlayer the player entity
|
||||
* @return the player's NBTTagCompound data or null if unrecoverable error occurs
|
||||
*/
|
||||
public NBTTagCompound readPlayerNBT(EntityPlayer entityPlayer) {
|
||||
if (entityPlayer == null) {
|
||||
Bukkit.getLogger().warning("Attempted to read NBT for null player");
|
||||
return null;
|
||||
}
|
||||
|
||||
Path savePath = getSavePath(entityPlayer);
|
||||
File saveFile = savePath.toFile();
|
||||
|
||||
if (!saveFile.exists()) {
|
||||
// No existing data, return fresh compound from entity state
|
||||
return getFreshPlayerNBT(entityPlayer);
|
||||
}
|
||||
|
||||
try (FileInputStream in = new FileInputStream(saveFile)) {
|
||||
NBTTagCompound compound = NBTCompressedStreamTools.a(in);
|
||||
|
||||
// Load data into entityPlayer
|
||||
entityPlayer.e(compound);
|
||||
|
||||
return compound;
|
||||
}
|
||||
catch(Exception ignored)
|
||||
{
|
||||
try
|
||||
{
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
entityPlayer.e(compound);
|
||||
|
||||
return compound;
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Bukkit.getLogger().warning(e.toString());
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to read player NBT for " + entityPlayer.getName(), e);
|
||||
return getFreshPlayerNBT(entityPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
public static EntityPlayer getPlayer(Player player)
|
||||
{
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
try
|
||||
{
|
||||
return new File(".").getCanonicalFile().getAbsolutePath();
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
private NBTTagCompound getFreshPlayerNBT(EntityPlayer entityPlayer) {
|
||||
try {
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
entityPlayer.e(compound);
|
||||
return compound;
|
||||
} catch (Exception e) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Failed to initialize fresh NBT for " + entityPlayer.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getSavePath(EntityPlayer entityPlayer)
|
||||
{
|
||||
return this.path + "\\players\\" + entityPlayer.getName() + ".dat";
|
||||
public static EntityPlayer getPlayer(Player player) {
|
||||
if (!(player instanceof CraftPlayer)) {
|
||||
throw new IllegalArgumentException("Player must be a CraftPlayer instance");
|
||||
}
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base directory path of the running server jar or working directory.
|
||||
*/
|
||||
private String getBasePath() {
|
||||
try {
|
||||
return new File(".").getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to get base path", e);
|
||||
return System.getProperty("user.dir"); // fallback
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the file path where the player's NBT data should be saved.
|
||||
*
|
||||
* @param entityPlayer the player entity
|
||||
* @return Path to the player's .dat file
|
||||
*/
|
||||
private Path getSavePath(EntityPlayer entityPlayer) {
|
||||
return worldPath.resolve(Paths.get("players", entityPlayer.getName() + ".dat"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,20 +5,26 @@ import net.minecraft.server.v1_6_R3.NBTTagCompound;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class WorldNBTSynchronizer
|
||||
{
|
||||
@Nullable
|
||||
public static String getWorldWhatHasPlayer(EntityPlayer entityPlayer)
|
||||
{
|
||||
for (World world : Bukkit.getWorlds())
|
||||
{
|
||||
String worldName = world.getName();
|
||||
import java.util.Objects;
|
||||
|
||||
if (hasAll(entityPlayer, worldName))
|
||||
{
|
||||
public final class WorldNBTSynchronizer {
|
||||
|
||||
/**
|
||||
* Finds the name of the world that contains the given player and where the player's NBT
|
||||
* has a valid "giftapi" tag.
|
||||
*
|
||||
* @param entityPlayer the player entity to check
|
||||
* @return the world name if found, otherwise null
|
||||
*/
|
||||
@Nullable
|
||||
public static String getWorldContainingPlayerWithGiftApiNBT(EntityPlayer entityPlayer) {
|
||||
Objects.requireNonNull(entityPlayer, "entityPlayer cannot be null");
|
||||
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
String worldName = world.getName();
|
||||
if (isPlayerInWorldWithGiftApiNBT(entityPlayer, worldName)) {
|
||||
return worldName;
|
||||
}
|
||||
}
|
||||
@ -26,47 +32,62 @@ public class WorldNBTSynchronizer
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean hasAll(EntityPlayer entityPlayer, String worldName)
|
||||
{
|
||||
boolean isHasPlayer = hasPlayer(entityPlayer.getName(), worldName);
|
||||
if (!isHasPlayer) { return false; }
|
||||
|
||||
return hasGiftApiNBT(entityPlayer, worldName);
|
||||
/**
|
||||
* Checks whether the player with the specified name exists in the world and
|
||||
* if their NBT contains the "giftapi" tag.
|
||||
*
|
||||
* @param entityPlayer the player entity
|
||||
* @param worldName the world name
|
||||
* @return true if player exists in world and has valid giftapi NBT, false otherwise
|
||||
*/
|
||||
public static boolean isPlayerInWorldWithGiftApiNBT(EntityPlayer entityPlayer, String worldName) {
|
||||
if (!isPlayerOnlineInWorld(entityPlayer.getName(), worldName)) {
|
||||
return false;
|
||||
}
|
||||
return doesPlayerNBTContainGiftApi(entityPlayer, worldName);
|
||||
}
|
||||
|
||||
private static boolean hasPlayer(String playerName, String worldName)
|
||||
{
|
||||
/**
|
||||
* Checks if the player with the given name is currently online in the specified world.
|
||||
*
|
||||
* @param playerName the player name to check
|
||||
* @param worldName the world name
|
||||
* @return true if player is online in the world, false otherwise
|
||||
*/
|
||||
private static boolean isPlayerOnlineInWorld(String playerName, String worldName) {
|
||||
World world = Bukkit.getWorld(worldName);
|
||||
|
||||
for (Player player : world.getPlayers())
|
||||
{
|
||||
if (player.getName().equals(playerName)) { return true; }
|
||||
if (world == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Use stream for clarity and short-circuiting
|
||||
return world.getPlayers().stream()
|
||||
.anyMatch(player -> player.getName().equals(playerName));
|
||||
}
|
||||
|
||||
private static boolean hasGiftApiNBT(EntityPlayer entityPlayer, String worldName)
|
||||
{
|
||||
/**
|
||||
* Checks if the player's NBT data in the specified world contains a valid "giftapi" NBTTagList.
|
||||
*
|
||||
* @param entityPlayer the player entity
|
||||
* @param worldName the world name
|
||||
* @return true if valid giftapi tag exists, false otherwise
|
||||
*/
|
||||
private static boolean doesPlayerNBTContainGiftApi(EntityPlayer entityPlayer, String worldName) {
|
||||
BasicNBT basicNBT = new BasicNBT(worldName);
|
||||
|
||||
NBTTagCompound tag = basicNBT.readPlayerNBT(entityPlayer);
|
||||
|
||||
if (tag == null) { return false; }
|
||||
|
||||
if (tag.hasKey("giftapi")) // if exists giftapi tag in player
|
||||
{
|
||||
try
|
||||
{
|
||||
tag.getList("giftapi");
|
||||
return true;
|
||||
}
|
||||
|
||||
catch(Exception ignored){} // if giftapi is not type list, throw exception and for what I used construction try-catch.
|
||||
if (tag == null || !tag.hasKey("giftapi")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
try {
|
||||
// Check if "giftapi" is an NBTTagList
|
||||
tag.getList("giftapi");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
// Swallow exception since the tag exists but is invalid type
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,61 +1,97 @@
|
||||
package tokarotik.giftapi.cache;
|
||||
|
||||
import net.minecraft.server.v1_6_R3.EntityPlayer;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import tokarotik.giftapi.NBT.savers.BasicNBT;
|
||||
import tokarotik.giftapi.NBT.savers.WorldNBTSynchronizer;
|
||||
import tokarotik.giftapi.NBT.pages.PagesManager;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CacheManager
|
||||
{
|
||||
/**
|
||||
* Manages player-specific cache of PagesManager instances across worlds.
|
||||
* Handles loading from NBT or initializing defaults.
|
||||
*/
|
||||
public class CacheManager {
|
||||
|
||||
private final CacheUtil cacheUtil;
|
||||
private final int inventory_slots;
|
||||
private final int inventorySlots;
|
||||
|
||||
public CacheManager(int inventory_slots)
|
||||
{
|
||||
this.inventory_slots = inventory_slots;
|
||||
cacheUtil = new CacheUtil(inventory_slots);
|
||||
public CacheManager(int inventorySlots) {
|
||||
if (inventorySlots <= 0) {
|
||||
throw new IllegalArgumentException("Inventory slots must be positive.");
|
||||
}
|
||||
|
||||
this.inventorySlots = inventorySlots;
|
||||
this.cacheUtil = new CacheUtil(inventorySlots);
|
||||
}
|
||||
|
||||
public synchronized PagesManager load(Player player) {
|
||||
String playerWorld = player.getWorld().getName();
|
||||
Map<Player, PagesManager> worldMap = cacheUtil.getWorlMap(playerWorld);
|
||||
EntityPlayer entityPlayer = BasicNBT.getPlayer(player);
|
||||
|
||||
// Return cached PageManager if already loaded
|
||||
if (worldMap.containsKey(player)) {
|
||||
return worldMap.get(player);
|
||||
/**
|
||||
* Loads the PagesManager for a given player.
|
||||
* If already cached, returns immediately.
|
||||
* Attempts to load from NBT, falling back to default if necessary.
|
||||
*
|
||||
* @param player the player to load data for
|
||||
* @return PagesManager instance associated with the player
|
||||
*/
|
||||
public PagesManager load(Player player) {
|
||||
if (player == null) {
|
||||
throw new IllegalArgumentException("Player cannot be null.");
|
||||
}
|
||||
|
||||
// Attempt to load from current world
|
||||
if (WorldNBTSynchronizer.hasAll(entityPlayer, playerWorld)) {
|
||||
return this.cacheUtil.loadAndCachePageManager(player, playerWorld, entityPlayer, worldMap);
|
||||
synchronized (getLockFor(player)) {
|
||||
String worldName = player.getWorld().getName();
|
||||
EntityPlayer entityPlayer = BasicNBT.getPlayer(player);
|
||||
Map<Player, PagesManager> worldMap = cacheUtil.getWorlMap(worldName);
|
||||
|
||||
// Use cached instance if present
|
||||
if (worldMap.containsKey(player)) {
|
||||
return worldMap.get(player);
|
||||
}
|
||||
|
||||
// Load from NBT if available in current or alternate world
|
||||
if (WorldNBTSynchronizer.isPlayerInWorldWithGiftApiNBT(entityPlayer, worldName)) {
|
||||
return cacheUtil.loadAndCachePageManager(player, worldName, entityPlayer, worldMap);
|
||||
}
|
||||
|
||||
String alternateWorld = WorldNBTSynchronizer.getWorldContainingPlayerWithGiftApiNBT(entityPlayer);
|
||||
if (alternateWorld != null) {
|
||||
return cacheUtil.loadAndCachePageManager(player, alternateWorld, entityPlayer, worldMap);
|
||||
}
|
||||
|
||||
// Default fallback: new empty manager
|
||||
PagesManager defaultPages = new PagesManager(inventorySlots);
|
||||
worldMap.put(player, defaultPages);
|
||||
return defaultPages;
|
||||
}
|
||||
|
||||
// Attempt to load from another world if present
|
||||
String alternateWorld = WorldNBTSynchronizer.getWorldWhatHasPlayer(entityPlayer);
|
||||
if (alternateWorld != null) {
|
||||
return this.cacheUtil.loadAndCachePageManager(player, alternateWorld, entityPlayer, worldMap);
|
||||
}
|
||||
|
||||
// Fallback to new empty PageManager
|
||||
PagesManager defaultPages = new PagesManager(this.inventory_slots);
|
||||
worldMap.put(player, defaultPages);
|
||||
|
||||
return defaultPages;
|
||||
}
|
||||
|
||||
public synchronized void disable()
|
||||
{
|
||||
/**
|
||||
* Saves all cached player data to disk.
|
||||
* Call during plugin disable.
|
||||
*/
|
||||
public void disable() {
|
||||
cacheUtil.saveAllHard();
|
||||
}
|
||||
|
||||
public synchronized void playerQuit(Player player)
|
||||
{
|
||||
this.cacheUtil.saveHard(player);
|
||||
/**
|
||||
* Saves data for a player when they leave.
|
||||
*
|
||||
* @param player the player who quit
|
||||
*/
|
||||
public void playerQuit(Player player) {
|
||||
if (player == null) return;
|
||||
|
||||
synchronized (getLockFor(player)) {
|
||||
cacheUtil.saveHard(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple locking strategy for per-player concurrency without coarse-grained locking.
|
||||
*/
|
||||
private Object getLockFor(Player player) {
|
||||
// Optional improvement: use a WeakHashMap<Player, Object> for real-world concurrency handling
|
||||
return player.getUniqueId();
|
||||
}
|
||||
}
|
||||
|
121
src/main/java/tokarotik/giftapi/cache/CacheUtil.java
vendored
121
src/main/java/tokarotik/giftapi/cache/CacheUtil.java
vendored
@ -3,80 +3,119 @@ package tokarotik.giftapi.cache;
|
||||
import net.minecraft.server.v1_6_R3.EntityPlayer;
|
||||
import net.minecraft.server.v1_6_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_6_R3.NBTTagList;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import tokarotik.giftapi.NBT.savers.BasicNBT;
|
||||
import tokarotik.giftapi.NBT.pages.PagesManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class CacheUtil
|
||||
{
|
||||
public final Map<Player, PagesManager> world_map = new HashMap<>();
|
||||
public final Map<Player, PagesManager> nether_map = new HashMap<>();
|
||||
public final Map<Player, PagesManager> end_map = new HashMap<>();
|
||||
/**
|
||||
* Handles caching and persistent storage of PagesManager instances
|
||||
* for players across different world environments.
|
||||
*/
|
||||
public class CacheUtil {
|
||||
|
||||
public final int inventory_slots;
|
||||
private static final String OVERWORLD = "world";
|
||||
private static final String NETHER = "world_nether";
|
||||
private static final String END = "world_the_end";
|
||||
|
||||
public CacheUtil(int inventory_slots)
|
||||
{
|
||||
this.inventory_slots = inventory_slots;
|
||||
private final Map<Player, PagesManager> overworldCache = new HashMap<>();
|
||||
private final Map<Player, PagesManager> netherCache = new HashMap<>();
|
||||
private final Map<Player, PagesManager> endCache = new HashMap<>();
|
||||
|
||||
private final int inventorySlots;
|
||||
|
||||
public CacheUtil(int inventorySlots) {
|
||||
if (inventorySlots <= 0) {
|
||||
throw new IllegalArgumentException("Inventory slots must be greater than zero.");
|
||||
}
|
||||
this.inventorySlots = inventorySlots;
|
||||
}
|
||||
|
||||
public synchronized void saveHard(Player player)
|
||||
{
|
||||
for (World world : Bukkit.getWorlds())
|
||||
{
|
||||
String nameWorld = world.getName();
|
||||
Map<Player, PagesManager> map = this.getWorlMap(nameWorld);
|
||||
/**
|
||||
* Saves the player's data across all three known dimensions.
|
||||
*
|
||||
* @param player the player whose data to save
|
||||
*/
|
||||
public synchronized void saveHard(Player player) {
|
||||
if (player == null) return;
|
||||
|
||||
if (map == null) { continue; }
|
||||
EntityPlayer entityPlayer = BasicNBT.getPlayer(player);
|
||||
|
||||
PagesManager pages = map.get(player);
|
||||
for (Map.Entry<String, Map<Player, PagesManager>> entry : getWorldCaches().entrySet()) {
|
||||
String worldName = entry.getKey();
|
||||
Map<Player, PagesManager> cache = entry.getValue();
|
||||
|
||||
if (pages == null) { continue; }
|
||||
PagesManager pages = cache.get(player);
|
||||
if (pages == null) continue;
|
||||
|
||||
NBTTagList tag = pages.getTag();
|
||||
EntityPlayer entityPlayer = BasicNBT.getPlayer(player);
|
||||
|
||||
BasicNBT basicNBT = new BasicNBT(nameWorld);
|
||||
|
||||
BasicNBT basicNBT = new BasicNBT(worldName);
|
||||
NBTTagCompound compound = basicNBT.readPlayerNBT(entityPlayer);
|
||||
|
||||
compound.set("giftapi", tag);
|
||||
basicNBT.writePlayerNBT(compound, entityPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void saveAllHard()
|
||||
{
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
{
|
||||
/**
|
||||
* Saves all player data in all world caches.
|
||||
*/
|
||||
public synchronized void saveAllHard() {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
saveHard(player);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Player, PagesManager> getWorlMap(String worldName)
|
||||
{
|
||||
switch(worldName)
|
||||
{
|
||||
case "world": { return this.world_map; }
|
||||
case "world_nether": { return this.nether_map; }
|
||||
case "world_the_end": { return this.end_map; }
|
||||
/**
|
||||
* Retrieves the proper world-specific player cache map.
|
||||
*
|
||||
* @param worldName name of the world
|
||||
* @return the map of players and their PagesManager for that world
|
||||
*/
|
||||
public Map<Player, PagesManager> getWorlMap(String worldName) {
|
||||
switch (worldName) {
|
||||
case OVERWORLD:
|
||||
return overworldCache;
|
||||
case NETHER:
|
||||
return netherCache;
|
||||
case END:
|
||||
return endCache;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and caches a PagesManager instance from NBT data for the given player.
|
||||
*
|
||||
* @param player the player
|
||||
* @param world the world name
|
||||
* @param entityPlayer the NMS player instance
|
||||
* @param worldMap the cache map where the data will be stored
|
||||
* @return loaded and cached PagesManager instance
|
||||
*/
|
||||
public synchronized PagesManager loadAndCachePageManager(Player player, String world, EntityPlayer entityPlayer, Map<Player, PagesManager> worldMap) {
|
||||
NBTTagCompound compound = new BasicNBT(world).readPlayerNBT(entityPlayer);
|
||||
NBTTagList giftList = compound.getList("giftapi");
|
||||
PagesManager pagesManager = new PagesManager(giftList, inventorySlots);
|
||||
|
||||
PagesManager pagesManager = new PagesManager(giftList, this.inventory_slots);
|
||||
worldMap.put(player, pagesManager);
|
||||
return pagesManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to aggregate all internal world caches.
|
||||
*
|
||||
* @return map of worldName -> (player, pagesManager map)
|
||||
*/
|
||||
private Map<String, Map<Player, PagesManager>> getWorldCaches() {
|
||||
Map<String, Map<Player, PagesManager>> worldCaches = new HashMap<>();
|
||||
worldCaches.put(OVERWORLD, overworldCache);
|
||||
worldCaches.put(NETHER, netherCache);
|
||||
worldCaches.put(END, endCache);
|
||||
return worldCaches;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,54 +0,0 @@
|
||||
package tokarotik.giftapi.dev;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
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 tokarotik.giftapi.Main;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
|
||||
public class AddCommand implements CommandExecutor {
|
||||
private final Main plugin;
|
||||
|
||||
public AddCommand(Main plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String s, String[] strings) {
|
||||
|
||||
Player player;
|
||||
|
||||
if (sender == null) {
|
||||
if (Bukkit.getOnlinePlayers().length == 0) { return true; }
|
||||
player = Bukkit.getOnlinePlayers()[0];
|
||||
}
|
||||
else {
|
||||
player = (Player) sender;
|
||||
}
|
||||
|
||||
int count = 1;
|
||||
if (strings.length > 0)
|
||||
{
|
||||
count = Integer.parseInt(strings[0]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Material[] values = Material.values();
|
||||
int id = values[ThreadLocalRandom.current().nextInt(values.length)].getId();
|
||||
ItemStack item = new ItemStack(id);
|
||||
|
||||
this.plugin.add(player, item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package tokarotik.giftapi.dev;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import tokarotik.giftapi.APIManager;
|
||||
import tokarotik.giftapi.Main;
|
||||
|
||||
public class GiftCommand implements CommandExecutor {
|
||||
|
||||
private final Main plugin;
|
||||
|
||||
public GiftCommand(Main plugin)
|
||||
{
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String s, String[] strings) {
|
||||
Player player;
|
||||
|
||||
if (!(sender instanceof Player))
|
||||
{
|
||||
sender.sendMessage("This command can use only player!");
|
||||
player = Bukkit.getOnlinePlayers()[0];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
player = (Player) sender;
|
||||
}
|
||||
|
||||
this.plugin.openGUI(player);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -7,53 +7,92 @@ import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import tokarotik.giftapi.cache.CacheManager;
|
||||
import tokarotik.giftapi.NBT.pages.PagesManager;
|
||||
|
||||
/**
|
||||
* Manages interactions with the custom inventory GUI, including
|
||||
* rendering, handling clicks, and modifying player item pages.
|
||||
*/
|
||||
public class InventoryManager implements Listener {
|
||||
|
||||
public class InventoryManager implements Listener
|
||||
{
|
||||
private final String nameGUI;
|
||||
private final int inventory_slots;
|
||||
private final String guiTitle;
|
||||
private final int inventorySlots;
|
||||
private final CacheManager cacheManager;
|
||||
private final InventoryUtil inventoryUtil;
|
||||
|
||||
|
||||
public InventoryManager(CacheManager cacheManager, int inventory_slots, String nameGUI, String nameArrowRight, String nameArrowLeft, String nameExit)
|
||||
{
|
||||
this.nameGUI = nameGUI;
|
||||
this.inventory_slots = inventory_slots;
|
||||
/**
|
||||
* Constructs an InventoryManager responsible for handling a custom inventory UI.
|
||||
*
|
||||
* @param cacheManager player cache handler
|
||||
* @param inventorySlots total number of inventory slots
|
||||
* @param guiTitle title of the GUI
|
||||
* @param arrowRight text for the right-arrow button
|
||||
* @param arrowLeft text for the left-arrow button
|
||||
* @param exitButton text for the exit button
|
||||
*/
|
||||
public InventoryManager(CacheManager cacheManager, int inventorySlots, String guiTitle,
|
||||
String arrowRight, String arrowLeft, String exitButton) {
|
||||
this.guiTitle = guiTitle;
|
||||
this.inventorySlots = inventorySlots;
|
||||
this.cacheManager = cacheManager;
|
||||
int items_slots = inventory_slots - 9;
|
||||
inventoryUtil = new InventoryUtil(cacheManager, nameGUI, nameArrowRight, nameArrowLeft, nameExit, items_slots);
|
||||
|
||||
int itemSlots = inventorySlots - 9; // reserve 9 for controls
|
||||
this.inventoryUtil = new InventoryUtil(cacheManager, guiTitle, arrowRight, arrowLeft, exitButton, itemSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles player clicking within a managed custom inventory.
|
||||
*
|
||||
* @param event the inventory click event
|
||||
*/
|
||||
@EventHandler
|
||||
public synchronized void onInventoryClick(InventoryClickEvent event)
|
||||
{
|
||||
this.inventoryUtil.onInventoryClick(event);
|
||||
public void handleInventoryClick(InventoryClickEvent event) {
|
||||
inventoryUtil.onInventoryClick(event);
|
||||
}
|
||||
|
||||
public synchronized void add(Player player, ItemStack item)
|
||||
{
|
||||
PagesManager pages = this.cacheManager.load(player);
|
||||
/**
|
||||
* Adds an item to the player's PagesManager.
|
||||
*
|
||||
* @param player the target player
|
||||
* @param item the item to add
|
||||
*/
|
||||
public void add(Player player, ItemStack item) {
|
||||
if (player == null || item == null) return;
|
||||
|
||||
PagesManager pages = cacheManager.load(player);
|
||||
pages.add(item);
|
||||
}
|
||||
|
||||
public synchronized void remove(Player player, ItemStack item)
|
||||
{
|
||||
PagesManager pages = this.cacheManager.load(player);
|
||||
/**
|
||||
* Removes an item from the player's PagesManager.
|
||||
*
|
||||
* @param player the target player
|
||||
* @param item the item to remove
|
||||
*/
|
||||
public void remove(Player player, ItemStack item) {
|
||||
if (player == null || item == null) return;
|
||||
|
||||
PagesManager pages = cacheManager.load(player);
|
||||
pages.remove(item);
|
||||
}
|
||||
|
||||
public synchronized void openCustomInventory(Player player)
|
||||
{
|
||||
this.cacheManager.load(player).setCurrentPage(0);
|
||||
Inventory inventory = Bukkit.createInventory(null, this.inventory_slots, this.nameGUI);
|
||||
/**
|
||||
* Opens the custom GUI for the specified player.
|
||||
* Resets to the first page before rendering.
|
||||
*
|
||||
* @param player the player to open the GUI for
|
||||
*/
|
||||
public void openCustomInventory(Player player) {
|
||||
if (player == null) return;
|
||||
|
||||
this.inventoryUtil.updateInventory(inventory, player);
|
||||
PagesManager pages = cacheManager.load(player);
|
||||
pages.setCurrentPage(0);
|
||||
|
||||
Inventory inventory = Bukkit.createInventory(null, inventorySlots, guiTitle);
|
||||
inventoryUtil.updateInventory(inventory, player);
|
||||
|
||||
player.openInventory(inventory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,124 +12,112 @@ import tokarotik.giftapi.NBT.pages.PagesManager;
|
||||
import tokarotik.giftapi.inventory.inventoryitems.InventoryItems;
|
||||
import tokarotik.giftapi.inventory.inventoryitems.InventoryItemsUtil;
|
||||
|
||||
public class InventoryUtil
|
||||
{
|
||||
/**
|
||||
* Utility class for managing the inventory GUI elements and user interaction.
|
||||
*/
|
||||
public class InventoryUtil {
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
private final InventoryItems inventoryItems;
|
||||
private final String nameGUI;
|
||||
private final String nameArrowRight;
|
||||
private final String nameArrowLeft;
|
||||
private final String nameExit;
|
||||
private final int inventory_slots;
|
||||
private final String guiTitle;
|
||||
private final String arrowRightLabel;
|
||||
private final String arrowLeftLabel;
|
||||
private final String exitLabel;
|
||||
private final int inventorySlots;
|
||||
|
||||
public InventoryUtil(CacheManager cacheManager, String guiTitle,
|
||||
String arrowRightLabel, String arrowLeftLabel,
|
||||
String exitLabel, int inventorySlots) {
|
||||
|
||||
public InventoryUtil(CacheManager cacheManager, String nameGUI, String nameArrowRight, String nameArrowLeft, String nameExit, int inventory_slots)
|
||||
{
|
||||
this.cacheManager = cacheManager;
|
||||
this.nameGUI = nameGUI;
|
||||
this.nameArrowRight = nameArrowRight;
|
||||
this.nameArrowLeft = nameArrowLeft;
|
||||
this.nameExit = nameExit;
|
||||
this.inventory_slots = inventory_slots;
|
||||
this.inventoryItems = new InventoryItems(inventory_slots);
|
||||
this.guiTitle = guiTitle;
|
||||
this.arrowRightLabel = arrowRightLabel;
|
||||
this.arrowLeftLabel = arrowLeftLabel;
|
||||
this.exitLabel = exitLabel;
|
||||
this.inventorySlots = inventorySlots;
|
||||
this.inventoryItems = new InventoryItems(inventorySlots);
|
||||
}
|
||||
|
||||
public void onInventoryClick(InventoryClickEvent event)
|
||||
{
|
||||
/**
|
||||
* Handles clicks in the custom GUI and controls pagination or exiting.
|
||||
*/
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
Inventory inventory = event.getInventory();
|
||||
if (!event.getView().getTitle().equals(this.guiTitle)) return;
|
||||
|
||||
if (inventory.getTitle().equals(this.nameGUI)) {
|
||||
event.setCancelled(true);
|
||||
event.setCancelled(true);
|
||||
|
||||
ItemStack currentItem = event.getCurrentItem();
|
||||
ItemStack clickedItem = event.getCurrentItem();
|
||||
if (clickedItem == null || !clickedItem.hasItemMeta()) return;
|
||||
|
||||
if (currentItem == null) { return; }
|
||||
String nameCurrentItem = null;
|
||||
if (currentItem.hasItemMeta()) {
|
||||
if (currentItem.getItemMeta().hasDisplayName()) {
|
||||
nameCurrentItem = currentItem.getItemMeta().getDisplayName();
|
||||
}
|
||||
String displayName = clickedItem.getItemMeta().getDisplayName();
|
||||
if (displayName == null) return;
|
||||
|
||||
Player player = (Player) event.getWhoClicked();
|
||||
PagesManager pagesManager = cacheManager.load(player);
|
||||
|
||||
if (displayName.equals(arrowRightLabel)) {
|
||||
if (pagesManager.getCurrentPage() < pagesManager.getCountPages()) {
|
||||
pagesManager.nextPage();
|
||||
updateInventory(inventory, player);
|
||||
}
|
||||
|
||||
Player player = (Player) event.getWhoClicked();
|
||||
|
||||
if (nameCurrentItem != null)
|
||||
{
|
||||
if (nameCurrentItem.equals(this.nameArrowRight)) {
|
||||
PagesManager pagesManager = this.cacheManager.load(player);
|
||||
|
||||
if (pagesManager.getCountPages() > pagesManager.getCurrentPage())
|
||||
{
|
||||
pagesManager.nextPage();
|
||||
updateInventory(inventory, player);
|
||||
}
|
||||
}
|
||||
|
||||
if (nameCurrentItem.equals(this.nameArrowLeft)) {
|
||||
PagesManager pagesManager = this.cacheManager.load(player);
|
||||
|
||||
if (0 < pagesManager.getCurrentPage())
|
||||
{
|
||||
pagesManager.backPage();
|
||||
updateInventory(inventory, player);
|
||||
}
|
||||
}
|
||||
|
||||
if (nameCurrentItem.equals(this.nameExit)) {
|
||||
player.closeInventory();
|
||||
}
|
||||
} else if (displayName.equals(arrowLeftLabel)) {
|
||||
if (pagesManager.getCurrentPage() > 0) {
|
||||
pagesManager.backPage();
|
||||
updateInventory(inventory, player);
|
||||
}
|
||||
|
||||
} else if (displayName.equals(exitLabel)) {
|
||||
player.closeInventory();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateInventory(Inventory inventory, Player player)
|
||||
{
|
||||
/**
|
||||
* Refreshes the inventory GUI with items and controls for the current page.
|
||||
*/
|
||||
public void updateInventory(Inventory inventory, Player player) {
|
||||
inventory.clear();
|
||||
PagesManager pagesManager = this.cacheManager.load(player);
|
||||
PagesManager pagesManager = cacheManager.load(player);
|
||||
|
||||
this.inventoryItems.renderPage(pagesManager, inventory);
|
||||
setUI(inventory, pagesManager);
|
||||
inventoryItems.renderPage(pagesManager, inventory);
|
||||
renderControls(inventory, pagesManager);
|
||||
}
|
||||
|
||||
private synchronized void setUI(Inventory inventory, PagesManager pagesManager)
|
||||
{
|
||||
int height = (this.inventory_slots / 9);
|
||||
/**
|
||||
* Adds navigation and exit controls to the inventory UI.
|
||||
*/
|
||||
private void renderControls(Inventory inventory, PagesManager pagesManager) {
|
||||
int lastRow = inventorySlots / 9;
|
||||
|
||||
// exit
|
||||
setItem(inventory, Material.REDSTONE, this.nameExit, 4, height);
|
||||
// Exit button in the center
|
||||
addItem(inventory, Material.REDSTONE, exitLabel, 4, lastRow);
|
||||
|
||||
int count = pagesManager.getCountPages();
|
||||
int currentPage = pagesManager.getCurrentPage();
|
||||
int totalPages = pagesManager.getCountPages();
|
||||
|
||||
if (count == 0) { return; }
|
||||
|
||||
if (pagesManager.getCurrentPage() < count)
|
||||
{
|
||||
// arrow right
|
||||
setArrow(inventory, this.nameArrowRight, 8, height);
|
||||
if (currentPage < totalPages) {
|
||||
addItem(inventory, Material.ARROW, arrowRightLabel, 8, lastRow);
|
||||
}
|
||||
|
||||
if (pagesManager.getCurrentPage() > 0)
|
||||
{
|
||||
// arrow left
|
||||
setArrow(inventory, this.nameArrowLeft, 0, height);
|
||||
if (currentPage > 0) {
|
||||
addItem(inventory, Material.ARROW, arrowLeftLabel, 0, lastRow);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void setArrow(Inventory inventory, String name, int x, int y)
|
||||
{
|
||||
setItem(inventory, Material.ARROW, name, x, y);
|
||||
}
|
||||
/**
|
||||
* Places an item at a specific coordinate within the inventory.
|
||||
*/
|
||||
private void addItem(Inventory inventory, Material material, String name, int x, int y) {
|
||||
ItemStack item = new ItemStack(material);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
|
||||
private synchronized void setItem(Inventory inventory, Material material, String name, int x, int y)
|
||||
{
|
||||
ItemStack item = new ItemStack(material, 1);
|
||||
ItemMeta meta_item = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(name);
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
|
||||
meta_item.setDisplayName(name);
|
||||
item.setItemMeta(meta_item);
|
||||
|
||||
inventory.setItem(
|
||||
InventoryItemsUtil.getCordinates(this.inventory_slots, x, y),
|
||||
item
|
||||
);
|
||||
int slot = InventoryItemsUtil.getSlotFromCoordinates(inventorySlots, x, y);
|
||||
inventory.setItem(slot, item);
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,37 @@ package tokarotik.giftapi.inventory.inventoryitems;
|
||||
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import tokarotik.giftapi.NBT.pages.PagesManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InventoryItems
|
||||
{
|
||||
private final int items_slots;
|
||||
/**
|
||||
* Handles rendering of item pages into a Bukkit Inventory.
|
||||
*/
|
||||
public class InventoryItems {
|
||||
|
||||
public InventoryItems(int items_slots) { this.items_slots = items_slots; }
|
||||
private final int itemSlotLimit;
|
||||
|
||||
public synchronized void renderPage(PagesManager pagesManager, Inventory inventory)
|
||||
{
|
||||
List<ItemStack> array = InventoryItemsUtil.getArrayToRender(pagesManager, items_slots);
|
||||
InventoryItemsUtil.renderList(array, inventory, this.items_slots);
|
||||
/**
|
||||
* @param itemSlotLimit Number of item slots available for rendering (typically inventory size minus control slots).
|
||||
*/
|
||||
public InventoryItems(int itemSlotLimit) {
|
||||
if (itemSlotLimit <= 0) {
|
||||
throw new IllegalArgumentException("Item slot limit must be greater than 0.");
|
||||
}
|
||||
this.itemSlotLimit = itemSlotLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a page of items from the PagesManager into the provided inventory.
|
||||
*
|
||||
* @param pagesManager the source of paginated items
|
||||
* @param inventory the Bukkit inventory to be populated
|
||||
*/
|
||||
public void renderPage(PagesManager pagesManager, Inventory inventory) {
|
||||
if (pagesManager == null || inventory == null) return;
|
||||
|
||||
List<ItemStack> itemsToRender = InventoryItemsUtil.getArrayToRender(pagesManager, itemSlotLimit);
|
||||
InventoryItemsUtil.renderList(itemsToRender, inventory, itemSlotLimit);
|
||||
}
|
||||
}
|
||||
|
@ -2,67 +2,108 @@ package tokarotik.giftapi.inventory.inventoryitems;
|
||||
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import tokarotik.giftapi.NBT.pages.PagesManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class InventoryItemsUtil
|
||||
{
|
||||
public synchronized static List<ItemStack> getArrayToRender(PagesManager pagesManager, int items_slots)
|
||||
{
|
||||
ItemStack[] items = pagesManager.getList();
|
||||
/**
|
||||
* Utility class for rendering item pages into inventories.
|
||||
*/
|
||||
public final class InventoryItemsUtil {
|
||||
|
||||
int items_count = items.length;
|
||||
int current_page = pagesManager.getCurrentPage();
|
||||
private static final int INVENTORY_COLUMNS = 9;
|
||||
private static final int MAX_ROW_WIDTH = 10;
|
||||
|
||||
int start_item = getStartItemIndex(current_page, items_slots);
|
||||
int end_item = getEndItemIndex(current_page, items_slots, items_count);
|
||||
|
||||
return Arrays.asList(items).subList(start_item, end_item);
|
||||
private InventoryItemsUtil() {
|
||||
throw new UnsupportedOperationException("Utility class should not be instantiated.");
|
||||
}
|
||||
|
||||
public synchronized static void renderList(List<ItemStack> array, Inventory inventory, int items_slots)
|
||||
{
|
||||
short x = 0;
|
||||
short y = 0;
|
||||
/**
|
||||
* Returns a sublist of the items for the current page from the PagesManager.
|
||||
*
|
||||
* @param pagesManager the source of item pages
|
||||
* @param slotsPerPage how many item slots are available per page
|
||||
* @return list of items to render on the current page
|
||||
*/
|
||||
public static List<ItemStack> getArrayToRender(PagesManager pagesManager, int slotsPerPage) {
|
||||
if (pagesManager == null || slotsPerPage <= 0) return new ArrayList<>();
|
||||
|
||||
for (ItemStack itemStack : array) {
|
||||
ItemStack[] allItems = pagesManager.getList();
|
||||
int totalItems = allItems.length;
|
||||
int currentPage = pagesManager.getCurrentPage();
|
||||
|
||||
if (x >= 10) {
|
||||
int startIndex = getStartItemIndex(currentPage, slotsPerPage);
|
||||
int endIndex = getEndItemIndex(currentPage, slotsPerPage, totalItems);
|
||||
|
||||
return Arrays.asList(allItems).subList(startIndex, endIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the list of items into the inventory based on a coordinate system.
|
||||
*
|
||||
* @param itemsToRender list of items to place into the inventory
|
||||
* @param inventory the target Bukkit inventory
|
||||
* @param slotsPerPage number of item slots available for rendering
|
||||
*/
|
||||
public static void renderList(List<ItemStack> itemsToRender, Inventory inventory, int slotsPerPage) {
|
||||
if (itemsToRender == null || inventory == null) return;
|
||||
|
||||
int x = 0, y = 0;
|
||||
int maxSlot = slotsPerPage - 1;
|
||||
|
||||
for (ItemStack item : itemsToRender) {
|
||||
if (x >= MAX_ROW_WIDTH) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
int slot = getCordinates(items_slots + 9, x, y);
|
||||
|
||||
if (slot > items_slots - 1) {
|
||||
return;
|
||||
int slot = getSlotFromCoordinates(slotsPerPage + 9, x, y); // 9 added for controls
|
||||
|
||||
if (slot > maxSlot) {
|
||||
return; // Exceeded usable inventory area
|
||||
}
|
||||
|
||||
inventory.setItem(slot, itemStack);
|
||||
|
||||
inventory.setItem(slot, item);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized static int getStartItemIndex(int current_page, int items_slots)
|
||||
{
|
||||
return current_page * items_slots;
|
||||
/**
|
||||
* Calculates the start index for a page.
|
||||
*/
|
||||
private static int getStartItemIndex(int currentPage, int slotsPerPage) {
|
||||
return currentPage * slotsPerPage;
|
||||
}
|
||||
|
||||
private synchronized static int getEndItemIndex(int current_page, int items_slots, int items_count)
|
||||
{
|
||||
items_slots += 4;
|
||||
int literly_end_page = (current_page + 1) * items_slots;
|
||||
/**
|
||||
* Calculates the end index for a page, ensuring it doesn't exceed item count.
|
||||
*/
|
||||
private static int getEndItemIndex(int currentPage, int slotsPerPage, int totalItems) {
|
||||
int paddedPageSize = slotsPerPage + 4; // May account for GUI spacing or reserved rows
|
||||
int endIndex = (currentPage + 1) * paddedPageSize;
|
||||
|
||||
if (literly_end_page <= items_count) { return literly_end_page; }
|
||||
return literly_end_page - (literly_end_page - items_count);
|
||||
return Math.min(endIndex, totalItems);
|
||||
}
|
||||
|
||||
public static synchronized int getCordinates(int inventory_slots, int x, int y)
|
||||
{
|
||||
if (y > 0 && y != inventory_slots / 9) { x += y; }
|
||||
return ((y * 9) + x) % (inventory_slots + 9);
|
||||
/**
|
||||
* Maps 2D coordinates into a 1D inventory slot index.
|
||||
* Accounts for skipped rows or padding.
|
||||
*
|
||||
* @param totalInventorySlots total size of the inventory
|
||||
* @param x column index
|
||||
* @param y row index
|
||||
* @return calculated 1D slot index
|
||||
*/
|
||||
public static int getSlotFromCoordinates(int totalInventorySlots, int x, int y) {
|
||||
// Adjust X if the row is beyond the first (some layouts may shift items right)
|
||||
if (y > 0 && y != totalInventorySlots / INVENTORY_COLUMNS) {
|
||||
x += y;
|
||||
}
|
||||
|
||||
int slot = (y * INVENTORY_COLUMNS) + x;
|
||||
return slot % totalInventorySlots;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,6 @@
|
||||
# For colors:
|
||||
# do not use § use only &
|
||||
|
||||
|
||||
# Will turn command /gift for dev. test
|
||||
dev-test: true
|
||||
|
||||
gui:
|
||||
# Set Title Only In English
|
||||
# Max title length is 32 letters
|
||||
|
@ -1,11 +1,3 @@
|
||||
name: GiftAPI
|
||||
version: 1.0
|
||||
main: tokarotik.giftapi.Main
|
||||
|
||||
commands:
|
||||
gift:
|
||||
description: for test
|
||||
usage: /<command>
|
||||
add:
|
||||
description: for test
|
||||
usage: /<command>
|
Loading…
x
Reference in New Issue
Block a user