Compare commits

...

2 Commits

Author SHA1 Message Date
10bf8c7123 0.1 2024-07-21 01:10:25 +08:00
04dd29edec 0.1 2024-07-18 18:47:23 +08:00
20 changed files with 831 additions and 91 deletions

18
pom.xml
View File

@ -32,21 +32,21 @@
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.18.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.io.yutian.pixelpaper</groupId>
<artifactId>pixelpaper-api</artifactId>
<groupId>com.io.yutian</groupId>
<artifactId>pixelpaper</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.io.yutian</groupId>
<artifactId>AuLib</artifactId>
<version>1.4.1</version>
<version>1.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.milkbowl</groupId>
<artifactId>Vault</artifactId>
<version>1.7.3</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -2,34 +2,64 @@ package com.io.yutian.auchestshop;
import com.io.yutian.auchestshop.command.CommandManager;
import com.io.yutian.auchestshop.database.SQLIO;
import com.io.yutian.auchestshop.listener.ShopListener;
import com.io.yutian.auchestshop.manager.InteractiveManager;
import com.io.yutian.auchestshop.manager.ShopManager;
import com.io.yutian.aulib.lang.Lang;
import net.milkbowl.vault.Vault;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.math.BigInteger;
public class AuChestShop extends JavaPlugin {
private static AuChestShop instance;
private static Economy economy;
private static SQLIO sqlIO;
private static ShopManager shopManager;
private static CommandManager commandManager;
private static InteractiveManager interactiveManager;
@Override
public void onEnable() {
instance = this;
RegisteredServiceProvider<Economy> economyProvider = null;
try {
economyProvider = Bukkit.getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
} catch (Exception e) {
e.printStackTrace();
}
if (economyProvider != null) {
economy = economyProvider.getProvider();
}
sqlIO = new SQLIO();
commandManager = new CommandManager();
shopManager = new ShopManager();
interactiveManager = new InteractiveManager();
commandManager.registerBukkitCommand(this, "auchestshop");
Lang.reload(this);
registerListeners();
Lang.registerLangFile(this);
reload();
try {
sqlIO.init();
} catch (Exception e) {
e.printStackTrace();
}
shopManager.loadAll(sqlIO);
}
public void reload() {
Lang.reload(this);
}
@Override
@ -37,14 +67,30 @@ public class AuChestShop extends JavaPlugin {
sqlIO.close();
}
private void registerListeners() {
new ShopListener(this);
}
public static SQLIO getSQLIO() {
return sqlIO;
}
public static Economy getEconomy() {
return economy;
}
public static ShopManager getShopManager() {
return shopManager;
}
public static CommandManager getCommandManager() {
return commandManager;
}
public static InteractiveManager getInteractiveManager() {
return interactiveManager;
}
public static AuChestShop inst() {
return instance;
}

View File

@ -1,5 +1,6 @@
package com.io.yutian.auchestshop.command;
import com.io.yutian.auchestshop.command.subs.CommandReload;
import com.io.yutian.auchestshop.command.subs.CommandTest;
import com.io.yutian.aulib.command.SimpleCommandManager;
@ -8,6 +9,7 @@ public class CommandManager extends SimpleCommandManager {
public CommandManager() {
super("chestshop");
register(new CommandTest());
register(new CommandReload());
}
}

View File

@ -0,0 +1,21 @@
package com.io.yutian.auchestshop.command.subs;
import com.io.yutian.auchestshop.AuChestShop;
import com.io.yutian.aulib.command.CommandContext;
import com.io.yutian.aulib.command.ICommand;
import org.bukkit.command.CommandSender;
public class CommandReload extends ICommand {
public CommandReload() {
super("reload");
}
@Override
public void executes(CommandContext commandContext) {
CommandSender sender = commandContext.getSender();
AuChestShop.inst().reload();
sender.sendMessage("§a重载成功");
}
}

View File

@ -1,19 +1,17 @@
package com.io.yutian.auchestshop.command.subs;
import com.io.yutian.auchestshop.AuChestShop;
import com.io.yutian.auchestshop.shop.Shop;
import com.io.yutian.auchestshop.shop.ShopLocation;
import com.io.yutian.auchestshop.shop.ShopType;
import com.io.yutian.auchestshop.shop.log.ShopLog;
import com.io.yutian.auchestshop.util.ItemStackUtil;
import com.io.yutian.aulib.command.CommandContext;
import com.io.yutian.aulib.command.ICommand;
import com.io.yutian.aulib.command.argument.Argument;
import com.io.yutian.aulib.command.argument.ArgumentTypes;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class CommandTest extends ICommand {
@ -28,23 +26,11 @@ public class CommandTest extends ICommand {
public void executes(CommandContext commandContext) {
CommandSender sender = commandContext.getSender();
String type = commandContext.getArgumentsValue("type").getString();
if (type.equalsIgnoreCase("create")) {
Shop shop = new Shop(UUID.randomUUID(), UUID.randomUUID(), new ShopLocation("world", 0, 10, 0), null, 10, ShopType.BUYING, false, new ShopLog());
shops.add(shop);
sender.sendMessage("Shop created!");
} else if (type.equalsIgnoreCase("save")) {
AuChestShop.getSQLIO().saveAllShops(shops);
sender.sendMessage("Shop saved!");
} else if (type.equalsIgnoreCase("list")) {
System.out.println(shops);
for (Shop shop : shops) {
sender.sendMessage(shop.getUUID());
sender.sendMessage(shop.getOwner());
}
} else if (type.equalsIgnoreCase("load")) {
shops = AuChestShop.getSQLIO().loadAllShops();
System.out.println(shops);
sender.sendMessage("Loaded all shops!");
if (type.equalsIgnoreCase("eq")) {
Player player = (Player) sender;
ItemStack itemStack = player.getInventory().getItemInMainHand();
ItemStack itemStack1 = player.getInventory().getItemInOffHand();
sender.sendMessage("result: "+ItemStackUtil.matches(itemStack, itemStack1));
}
}

View File

@ -40,24 +40,27 @@ public class SQLIO {
.addColumn("data", "TEXT NOT NULL")
.setIndex("uuid", IndexType.PRIMARY_KEY)
.setTableSettings("")
.build().execute(null);
.build().executeAsync(null);
}
public List<Shop> loadAllShops() {
List<Shop> shops = new ArrayList<>();
sqlManager.createQuery()
try (SQLQuery sqlQuery = sqlManager.createQuery()
.inTable("shops")
.build().executeAsync(sqlQuery -> {
ResultSet resultSet = sqlQuery.getResultSet();
while (resultSet.next()) {
String uuid = resultSet.getString("uuid");
String owner = resultSet.getString("owner");
String data = resultSet.getString("data");
JSONObject dataJson = new JSONObject(data);
Shop shop = SerializeHelper.deserialize(Shop.class, dataJson);
shops.add(shop);
}
});
.build().execute()) {
try (ResultSet resultSet = sqlQuery.getResultSet()) {
while (resultSet.next()) {
String uuid = resultSet.getString("uuid");
String owner = resultSet.getString("owner");
String data = resultSet.getString("data");
JSONObject dataJson = new JSONObject(data);
Shop shop = SerializeHelper.deserialize(Shop.class, dataJson);
shops.add(shop);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return shops;
}
@ -71,10 +74,9 @@ public class SQLIO {
JSONObject dataJson = SerializeHelper.serialize(shop);
sqlManager.createUpdate("shops")
.addCondition("uuid", shop.getUUID().toString())
.setLimit(1)
.setColumnValues("owner", shop.getOwner().toString())
.setColumnValues("data", dataJson.toString())
.build().execute();
.build().executeAsync();
} else {
insertedShops.add(shop);
}
@ -94,6 +96,37 @@ public class SQLIO {
}
}
public void saveShop(Shop shop) {
try (SQLQuery sqlQuery = sqlManager.createQuery().inTable("shops").addCondition("uuid", shop.getUUID().toString()).setLimit(1).build().execute()) {
try (ResultSet resultSet = sqlQuery.getResultSet()) {
if (resultSet.next()) {
JSONObject dataJson = SerializeHelper.serialize(shop);
sqlManager.createUpdate("shops")
.addCondition("uuid", shop.getUUID().toString())
.setColumnValues("data", dataJson.toString())
.build().executeAsync();
} else {
JSONObject dataJson = SerializeHelper.serialize(shop);
sqlManager.createInsert("shops").setIgnore(false).setColumnNames("uuid", "owner", "data").setParams(new Object[]{shop.getUUID().toString(), shop.getOwner().toString(), dataJson.toString()}).executeAsync();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteShop(Shop shop) {
try (SQLQuery sqlQuery = sqlManager.createQuery().inTable("shops").addCondition("uuid", shop.getUUID().toString()).setLimit(1).build().execute()) {
try (ResultSet resultSet = sqlQuery.getResultSet()) {
if (resultSet.next()) {
sqlManager.createDelete("shops").addCondition("uuid", shop.getUUID().toString()).build().executeAsync();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void close() {
if (sqlManager == null) {
return;

View File

@ -0,0 +1,61 @@
package com.io.yutian.auchestshop.gui;
import com.io.yutian.auchestshop.AuChestShop;
import com.io.yutian.auchestshop.shop.ShopType;
import com.io.yutian.aulib.gui.Gui;
import com.io.yutian.aulib.gui.button.Button;
import com.io.yutian.aulib.lang.Lang;
import com.io.yutian.aulib.util.ItemStackBuilder;
import com.io.yutian.aulib.util.PlayerInventoryUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.ItemStack;
public class CreateShopGui extends Gui {
private Block block;
private ItemStack itemStack;
private Runnable runnable;
protected boolean created = false;
public CreateShopGui(Player player, Block block, ItemStack itemStack, Runnable runnable) {
super(player, Component.text("创建商店"), 27);
this.block = block;
this.itemStack = itemStack.clone();
this.itemStack.setAmount(1);
this.runnable = runnable;
init();
initButton();
}
@Override
public void init() {
addButton(11, new Button(new ItemStackBuilder(Material.DIAMOND).setDisplayName("§a出售").build()).click((player1, clickType) -> {
created = true;
AuChestShop.getShopManager().createShop(player1, block, ShopType.SELLING);
player1.closeInventory();
player.sendMessage(Lang.get("auchestshop.create-shop.success"));
runnable.run();
}));
addButton(15, new Button(new ItemStackBuilder(Material.EMERALD).setDisplayName("§a收购").build()).click((player1, clickType) -> {
created = true;
AuChestShop.getShopManager().createShop(player1, block, ShopType.BUYING);
player1.closeInventory();
player.sendMessage(Lang.get("auchestshop.create-shop.success"));
runnable.run();
}));
}
@Override
public void close(InventoryCloseEvent event) {
if (!created) {
block.setType(Material.AIR);
PlayerInventoryUtil.giveItemStack(player, itemStack);
}
}
}

View File

@ -1,14 +1,184 @@
package com.io.yutian.auchestshop.listener;
import com.io.yutian.auchestshop.AuChestShop;
import com.io.yutian.auchestshop.gui.CreateShopGui;
import com.io.yutian.auchestshop.shop.Shop;
import com.io.yutian.auchestshop.util.ExpiringSet;
import com.io.yutian.auchestshop.util.ShopUtil;
import com.io.yutian.aulib.lang.Lang;
import com.io.yutian.aulib.listener.IListener;
import it.unimi.dsi.fastutil.Pair;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.Chest;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerAnimationEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ShopListener extends IListener {
private final ExpiringSet<UUID> adventureWorkaround = new ExpiringSet<>(1, TimeUnit.SECONDS);
private final Set<Location> cachedLocations = new HashSet<>();
public ShopListener(Plugin plugin) {
super(plugin);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onChat(AsyncPlayerChatEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
if (!AuChestShop.getInteractiveManager().containsKey(uuid)) {
return;
}
AuChestShop.getInteractiveManager().handler(player, event.getMessage());
event.setCancelled(true);
AuChestShop.getInteractiveManager().remove(player.getUniqueId());
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onBlockPlace(BlockPlaceEvent event) {
Player player = event.getPlayer();
Block block = event.getBlock();
ItemStack itemStack = event.getItemInHand();
if (ShopUtil.isShopItem(itemStack)) {
Block block1 = block.getRelative(BlockFace.UP, 1);
if (!block1.getType().equals(Material.AIR)) {
player.sendMessage(Lang.get("auchestshop.create-shop.up-has-block"));
event.setCancelled(true);
return;
}
CreateShopGui createShopGui = new CreateShopGui(player, block, itemStack, ()->{
cachedLocations.remove(block.getLocation());
});
createShopGui.open();
cachedLocations.add(block.getLocation());
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
Block block = event.getBlock();
if (cachedLocations.contains(block.getLocation())) {
event.setCancelled(true);
return;
}
Pair<Shop, ClickType> shopSearched = searchShop(player, block);
if (shopSearched.key() == null || shopSearched.value() == ClickType.AIR) {
return;
}
ClickType clickType = shopSearched.value();
if (clickType == ClickType.LOWER) {
Block block1 = block.getRelative(BlockFace.UP, 1);
block1.setType(Material.AIR);
} else if (clickType == ClickType.UPPER) {
Block block1 = block.getRelative(BlockFace.DOWN, 1);
block.setType(Material.AIR);
block1.breakNaturally(true);
event.setCancelled(true);
}
Shop shop = shopSearched.key();
AuChestShop.getShopManager().removeShop(shop);
player.sendMessage(Lang.get("auchestshop.remove-shop"));
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onAdventureClick(PlayerAnimationEvent event) {
if (event.getPlayer().getGameMode() != GameMode.ADVENTURE) {
return;
}
if (adventureWorkaround.contains(event.getPlayer().getUniqueId())) {
return;
}
adventureWorkaround.add(event.getPlayer().getUniqueId());
Block focused = event.getPlayer().getTargetBlockExact(5);
if (focused != null) {
PlayerInteractEvent interactEvent
= new PlayerInteractEvent(event.getPlayer(),
focused.getType() == Material.AIR ? Action.LEFT_CLICK_AIR : Action.LEFT_CLICK_BLOCK,
event.getPlayer().getInventory().getItemInMainHand(),
focused,
event.getPlayer().getFacing().getOppositeFace());
onPlayerInteract(interactEvent);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getHand() != EquipmentSlot.HAND) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.ADVENTURE) {
if (!adventureWorkaround.contains(player.getUniqueId())) {
return;
}
adventureWorkaround.add(player.getUniqueId());
}
Action action = event.getAction();
Block clickedBlock = event.getClickedBlock();
if (clickedBlock == null) {
return;
}
if (cachedLocations.contains(clickedBlock.getLocation())) {
event.setCancelled(true);
return;
}
Pair<Shop, ClickType> shopSearched = searchShop(player, clickedBlock);
if (shopSearched.key() == null || shopSearched.value() == ClickType.AIR) {
return;
}
Shop shop = shopSearched.key();
ClickType clickType = shopSearched.value();
AuChestShop.getShopManager().handleInteract(player, event, shop, clickType);
}
private Pair<Shop, ClickType> searchShop(Player player, Block block) {
if (block == null || block.getType() == Material.AIR) {
return Pair.of(null, ClickType.AIR);
}
Shop shop = AuChestShop.getShopManager().getShop(block.getLocation());
Material material = block.getType();
if (shop == null) {
if (material == Material.CHEST || material == Material.TRAPPED_CHEST) {
Chest chest = (Chest) block.getState().getBlockData();
Chest.ChestDoubleBlockHalfType halfType = chest.getDoubleBlockHalf();
if (halfType == Chest.ChestDoubleBlockHalfType.LOWER) {
return Pair.of(null, ClickType.AIR);
} else if (halfType == Chest.ChestDoubleBlockHalfType.UPPER) {
Block attached = block.getRelative(BlockFace.DOWN, 1);
shop = AuChestShop.getShopManager().getShop(attached.getLocation());
return Pair.of(shop, ClickType.UPPER);
}
} else {
return Pair.of(null, ClickType.AIR);
}
}
return Pair.of(shop, ClickType.LOWER);
}
public enum ClickType {
UPPER,
LOWER,
AIR;
}
}

View File

@ -0,0 +1,82 @@
package com.io.yutian.auchestshop.manager;
import com.io.yutian.auchestshop.AuChestShop;
import com.io.yutian.auchestshop.shop.Shop;
import com.io.yutian.auchestshop.shop.ShopAction;
import com.io.yutian.auchestshop.shop.ShopInteractiveInfo;
import com.io.yutian.aulib.lang.Lang;
import com.io.yutian.aulib.util.StringUtil;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class InteractiveManager {
private final Map<UUID, ShopInteractiveInfo> actions = new HashMap<>();
public void handler(Player player, String message) {
if (message.equalsIgnoreCase("cancel") || message.equalsIgnoreCase("取消")) {
player.sendMessage(Lang.get("auchestshop.action.cancel"));
return;
}
ShopInteractiveInfo info = actions.get(player.getUniqueId());
ShopAction action = info.getAction();
String arg = message.trim();
Shop shop = info.getShop();
if (AuChestShop.getShopManager().getShop(shop.getUUID()) == null) {
player.sendMessage(Lang.get("auchestshop.action.cancel"));
return;
}
if (action == ShopAction.CREATE) {
if (!StringUtil.isDouble(arg)) {
player.sendMessage(Lang.get("auchestshop.action.error-1"));
return;
}
double i = Double.parseDouble(arg);
if (i <= 0) {
player.sendMessage(Lang.get("auchestshop.action.error-2"));
return;
}
int length = arg.contains(".") ? arg.length() - arg.indexOf(".") + 1 : 0;
if (length >= 3) {
player.sendMessage(Lang.get("auchestshop.action.error-3"));
return;
}
if (i >= Double.MAX_VALUE) {
player.sendMessage(Lang.get("auchestshop.action.error-4"));
return;
}
ItemStack itemStack = info.getItemStack();
shop.setItem(itemStack);
shop.setPrice(i);
AuChestShop.getSQLIO().saveShop(shop);
player.sendMessage(Lang.get("auchestshop.action.success-create"));
} else if (action == ShopAction.PURCHASE) {
}
}
public ShopInteractiveInfo put(UUID uuid, ShopInteractiveInfo info) {
return this.actions.put(uuid, info);
}
public ShopInteractiveInfo remove(UUID uuid) {
return this.actions.remove(uuid);
}
public ShopInteractiveInfo get(UUID uuid) {
return this.actions.get(uuid);
}
public boolean containsKey(UUID uuid) {
return this.actions.containsKey(uuid);
}
public Map<UUID, ShopInteractiveInfo> getActions() {
return actions;
}
}

View File

@ -2,15 +2,26 @@ package com.io.yutian.auchestshop.manager;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.io.yutian.auchestshop.shop.Shop;
import com.io.yutian.auchestshop.shop.ShopChunk;
import com.io.yutian.auchestshop.shop.ShopLoader;
import com.io.yutian.auchestshop.shop.ShopLocation;
import com.io.yutian.auchestshop.AuChestShop;
import com.io.yutian.auchestshop.database.SQLIO;
import com.io.yutian.auchestshop.listener.ShopListener;
import com.io.yutian.auchestshop.shop.*;
import com.io.yutian.auchestshop.util.ItemStackUtil;
import com.io.yutian.auchestshop.util.ShopUtil;
import com.io.yutian.aulib.lang.Lang;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.Chest;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -18,17 +29,74 @@ import java.util.*;
public class ShopManager {
private final Map<String, Map<ShopChunk, Map<Location, Shop>>> shops = Maps.newConcurrentMap();
private final Map<String, Map<ShopChunk, Map<ShopLocation, Shop>>> shops = Maps.newConcurrentMap();
private final Set<Shop> loadedShops = Sets.newConcurrentHashSet();
public void loadAll(ShopLoader shopLoader) {
public void loadAll(SQLIO sqlio) {
List<Shop> shopList = sqlio.loadAllShops();
shopList.forEach(this::addShop);
}
public void saveAll(SQLIO sqlio) {
sqlio.saveAllShops(getAllShops());
}
public Shop createShop(Player player, Block block, ShopType shopType) {
Location location = block.getLocation();
ShopLocation shopLocation = ShopLocation.fromLocation(location);
Shop shop = new Shop(player.getUniqueId(), shopLocation, shopType);
addShop(shop);
AuChestShop.getSQLIO().saveShop(shop);
Block block1 = block.getRelative(BlockFace.UP, 1);
block1.setType(block.getType());
Chest chest = (Chest) block1.getState().getBlockData();
chest.setDoubleBlockHalf(Chest.ChestDoubleBlockHalfType.UPPER);
chest.setShapeLevel(12);
block1.setBlockData(chest);
Chest chest1 = (Chest) block.getState().getBlockData();
chest1.setDoubleBlockHalf(Chest.ChestDoubleBlockHalfType.LOWER);
chest1.setShapeLevel(12);
block.setBlockData(chest1);
return shop;
}
public void handleInteract(Player player, PlayerInteractEvent event, Shop shop, ShopListener.ClickType clickType) {
Action action = event.getAction();
if (action == Action.LEFT_CLICK_BLOCK) {
if (shop.getItem() == null) {
if (event.getItem() != null && !event.getItem().getType().equals(Material.AIR)) {
ShopInteractiveInfo interactiveInfo = new ShopInteractiveInfo(event.getItem().clone(), shop.getLocation(), shop, ShopAction.CREATE, clickType, System.currentTimeMillis());
ItemStack item = event.getItem().clone();
player.sendMessage(Lang.get("auchestshop.action.message-1", ItemStackUtil.getItemName(item)));
AuChestShop.getInteractiveManager().put(player.getUniqueId(), interactiveInfo);
return;
}
return;
}
ItemStack item = shop.getItem();
Inventory inventory = shop.getInventory();
int i = shop.getShopType() == ShopType.BUYING ? ItemStackUtil.countSpace(inventory, item) : ItemStackUtil.countItems(inventory, item);
player.sendMessage(Lang.get("auchestshop.action.message-" + (shop.getShopType() == ShopType.BUYING ? 2 : 3), ItemStackUtil.getItemName(item), i));
ShopInteractiveInfo interactiveInfo = new ShopInteractiveInfo(null, shop.getLocation(), shop, ShopAction.PURCHASE, clickType, System.currentTimeMillis());
AuChestShop.getInteractiveManager().put(player.getUniqueId(), interactiveInfo);
} else if (action == Action.RIGHT_CLICK_BLOCK) {
if (player.isSneaking() && shop.getOwner().equals(player.getUniqueId())) {
return;
}
ItemStack item = shop.getItem();
Inventory inventory = shop.getInventory();
int i = shop.getShopType() == ShopType.BUYING ? ItemStackUtil.countSpace(inventory, item) : ItemStackUtil.countItems(inventory, item);
player.sendMessage(Lang.get("auchestshop.action.message-" + (shop.getShopType() == ShopType.BUYING ? 2 : 3), ItemStackUtil.getItemName(item), i));
ShopInteractiveInfo interactiveInfo = new ShopInteractiveInfo(null, shop.getLocation(), shop, ShopAction.PURCHASE, clickType, System.currentTimeMillis());
AuChestShop.getInteractiveManager().put(player.getUniqueId(), interactiveInfo);
event.setCancelled(true);
}
}
public @NotNull List<Shop> getAllShops() {
List<Shop> shopsCollected = new ArrayList<>();
for (Map<ShopChunk, Map<Location, Shop>> shopMapData : getShops().values()) {
for (Map<Location, Shop> shopData : shopMapData.values()) {
for (Map<ShopChunk, Map<ShopLocation, Shop>> shopMapData : getShops().values()) {
for (Map<ShopLocation, Shop> shopData : shopMapData.values()) {
shopsCollected.addAll(shopData.values());
}
}
@ -61,38 +129,35 @@ public class ShopManager {
return null;
}
ShopChunk shopChunk = ShopChunk.fromLocation(location);
final Map<Location, Shop> inChunk = getShops(shopChunk);
final Map<ShopLocation, Shop> inChunk = getShops(shopChunk);
if (inChunk == null) {
return null;
}
location = location.clone();
location.setX(location.getBlockX());
location.setY(location.getBlockY());
location.setZ(location.getBlockZ());
return inChunk.get(location);
ShopLocation shopLocation = ShopLocation.fromLocation(location);
return inChunk.get(shopLocation);
}
public Map<String, Map<ShopChunk, Map<Location, Shop>>> getShops() {
public Map<String, Map<ShopChunk, Map<ShopLocation, Shop>>> getShops() {
return this.shops;
}
public Map<Location, Shop> getShops(@NotNull Chunk chunk) {
public Map<ShopLocation, Shop> getShops(@NotNull Chunk chunk) {
return getShops(chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
}
public Map<Location, Shop> getShops(@NotNull String world, int chunkX, int chunkZ) {
final Map<ShopChunk, Map<Location, Shop>> inWorld = this.getShops(world);
public Map<ShopLocation, Shop> getShops(@NotNull String world, int chunkX, int chunkZ) {
final Map<ShopChunk, Map<ShopLocation, Shop>> inWorld = this.getShops(world);
if (inWorld == null) {
return null;
}
return inWorld.get(new ShopChunk(world, chunkX, chunkZ));
}
public Map<Location, Shop> getShops(@NotNull ShopChunk shopChunk) {
public Map<ShopLocation, Shop> getShops(@NotNull ShopChunk shopChunk) {
return getShops(shopChunk.getWorld(), shopChunk.getX(), shopChunk.getZ());
}
public Map<ShopChunk, Map<Location, Shop>> getShops(@NotNull String world) {
public Map<ShopChunk, Map<ShopLocation, Shop>> getShops(@NotNull String world) {
return this.shops.get(world);
}
@ -118,12 +183,27 @@ public class ShopManager {
return worldShops;
}
public void loadShop(@NotNull Shop shop) {
public void addShop(@NotNull Shop shop) {
ShopLocation shopLocation = shop.getLocation();
String world = shopLocation.getWorld();
Map<ShopChunk, Map<ShopLocation, Shop>> map = shops.computeIfAbsent(world, k -> new HashMap<>());
int x = (int) Math.floor((shopLocation.getX()) / 16.0);
int z = (int) Math.floor((shopLocation.getZ()) / 16.0);
ShopChunk shopChunk = new ShopChunk(world, x, z);
Map<ShopLocation, Shop> inChunk = map.computeIfAbsent(shopChunk, k -> new HashMap<>());
inChunk.put(shopLocation, shop);
loadedShops.add(shop);
}
public void unloadShop(@NotNull Shop shop) {
public void removeShop(@NotNull Shop shop) {
ShopLocation location = shop.getLocation();
ShopChunk shopChunk = ShopChunk.fromLocation(location);
final Map<ShopLocation, Shop> inChunk = getShops(shopChunk);
if (inChunk != null) {
inChunk.remove(location);
}
this.loadedShops.remove(shop);
AuChestShop.getSQLIO().deleteShop(shop);
}
public Set<Shop> getLoadedShops() {

View File

@ -1,6 +1,13 @@
package com.io.yutian.auchestshop.shop;
import com.io.yutian.auchestshop.shop.log.ShopLog;
import com.io.yutian.aulib.serialize.SerializeIgnore;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.util.Objects;
@ -17,6 +24,15 @@ public class Shop {
private boolean unlimited;
private ShopLog shopLog;
@SerializeIgnore
protected Location chestLocation;
@SerializeIgnore
protected Block chestBlock;
public Shop(UUID owner, ShopLocation location, ShopType shopType) {
this(UUID.randomUUID(), owner, location, null, 0, shopType, false, new ShopLog());
}
public Shop(UUID uuid, UUID owner, ShopLocation location, ItemStack item, double price, ShopType shopType, boolean unlimited, ShopLog shopLog) {
this.uuid = uuid;
this.owner = owner;
@ -26,6 +42,33 @@ public class Shop {
this.shopType = shopType;
this.unlimited = unlimited;
this.shopLog = shopLog;
updateChest();
}
public void updateChest() {
World world = Bukkit.getWorld(location.getWorld());
this.chestLocation = new Location(world, location.getX(), location.getY(), location.getZ());
this.chestBlock = this.chestLocation.getBlock();
}
public Inventory getInventory() {
if (chestLocation == null) {
updateChest();
}
Chest chest = (Chest) chestBlock.getState();
return chest.getInventory();
}
public int getInventoryCount() {
return 0;
}
public void setItem(ItemStack item) {
this.item = item;
}
public void setPrice(double price) {
this.price = price;
}
public UUID getUUID() {

View File

@ -0,0 +1,8 @@
package com.io.yutian.auchestshop.shop;
public enum ShopAction {
PURCHASE,
CREATE;
}

View File

@ -38,6 +38,9 @@ public class ShopChunk {
return equals(world.getName(), x, z);
}
public static ShopChunk fromLocation(@NotNull ShopLocation location) {
return new ShopChunk(location.getWorld(), location.getX() >> 4, location.getZ() >> 4);
}
public static ShopChunk fromLocation(@NotNull Location location) {
return new ShopChunk(location.getWorld().getName(), location.getBlockX() >> 4, location.getBlockZ() >> 4);

View File

@ -0,0 +1,47 @@
package com.io.yutian.auchestshop.shop;
import com.io.yutian.auchestshop.listener.ShopListener;
import org.bukkit.inventory.ItemStack;
public class ShopInteractiveInfo {
private ItemStack itemStack;
private ShopLocation shopLocation;
private Shop shop;
private ShopAction action;
private ShopListener.ClickType clickType;
private long time;
public ShopInteractiveInfo(ItemStack itemStack, ShopLocation shopLocation, Shop shop, ShopAction action, ShopListener.ClickType clickType, long time) {
this.itemStack = itemStack;
this.shopLocation = shopLocation;
this.shop = shop;
this.action = action;
this.clickType = clickType;
this.time = time;
}
public ItemStack getItemStack() {
return itemStack;
}
public ShopLocation getShopLocation() {
return shopLocation;
}
public Shop getShop() {
return shop;
}
public ShopAction getAction() {
return action;
}
public ShopListener.ClickType getClickType() {
return clickType;
}
public long getTime() {
return time;
}
}

View File

@ -1,10 +0,0 @@
package com.io.yutian.auchestshop.shop;
public class ShopLoader {
public void load() {
}
}

View File

@ -1,6 +1,8 @@
package com.io.yutian.auchestshop.shop;
import org.json.JSONObject;
import org.bukkit.Location;
import java.util.Objects;
public class ShopLocation {
@ -16,17 +18,8 @@ public class ShopLocation {
this.z = z;
}
public static ShopLocation fromJSON(JSONObject json) {
return new ShopLocation(json.getString("world"), json.getInt("x"), json.getInt("y"), json.getInt("z"));
}
public JSONObject toJSON() {
JSONObject json = new JSONObject();
json.put("world", world);
json.put("x", x);
json.put("y", y);
json.put("z", z);
return json;
public static ShopLocation fromLocation(Location location) {
return new ShopLocation(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ());
}
public String getWorld() {
@ -45,4 +38,21 @@ public class ShopLocation {
return z;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShopLocation that = (ShopLocation) o;
return getX() == that.getX() && getY() == that.getY() && getZ() == that.getZ() && Objects.equals(getWorld(), that.getWorld());
}
@Override
public int hashCode() {
int result = Objects.hashCode(getWorld());
result = 31 * result + getX();
result = 31 * result + getY();
result = 31 * result + getZ();
return result;
}
}

View File

@ -0,0 +1,35 @@
package com.io.yutian.auchestshop.util;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
public class ExpiringSet<T> {
private final Cache<T, Long> cache;
private final long lifetime;
public ExpiringSet(long lifetime, TimeUnit timeUnit) {
this.cache = CacheBuilder
.newBuilder()
.expireAfterWrite(lifetime, timeUnit).build();
this.lifetime = timeUnit.toMillis(lifetime);
}
public void add(T item) {
this.cache.put(item, System.currentTimeMillis() + this.lifetime);
}
public boolean contains(T item) {
Long timeout = this.cache.getIfPresent(item);
return timeout != null && timeout > System.currentTimeMillis();
}
public void remove(T item) {
this.cache.invalidate(item);
}
public long size() {
return this.cache.size();
}
}

View File

@ -0,0 +1,77 @@
package com.io.yutian.auchestshop.util;
import com.io.yutian.aulib.nbt.NBTCompound;
import com.io.yutian.aulib.nbt.NBTItem;
import com.io.yutian.aulib.util.LangUtil;
import org.bukkit.Material;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ItemStackUtil {
public static boolean matches(@Nullable ItemStack original, @Nullable ItemStack tester) {
if (original == null || tester == null) {
return false;
}
original = original.clone();
original.setAmount(1);
tester = tester.clone();
tester.setAmount(1);
if (!original.isSimilar(tester)) {
return false;
}
NBTItem nbtOriginal = new NBTItem(original);
NBTItem nbtTester = new NBTItem(tester);
NBTCompound nbtCompound0 = nbtOriginal.getTag();
NBTCompound nbtCompound1 = nbtTester.getTag();
return nbtCompound0.equals(nbtCompound1);
}
public static int countItems(Inventory inventory, @NotNull ItemStack item) {
if (inventory == null) {
return 0;
}
int count = 0;
for (ItemStack itemStack : inventory) {
if (itemStack == null || itemStack.getType() == Material.AIR) {
continue;
}
if (matches(item, itemStack)) {
count+= itemStack.getAmount();
}
}
return count;
}
public static int countSpace(Inventory inventory, @NotNull ItemStack item) {
if (inventory == null) {
return 0;
}
int count = 0;
int itemMaxStackSize = item.getMaxStackSize();
for (ItemStack itemStack : inventory) {
if (itemStack == null || itemStack.getType() == Material.AIR) {
count+=itemMaxStackSize;
} else if (matches(item, itemStack)) {
count+=itemStack.getAmount() >= itemMaxStackSize ? 0 : itemMaxStackSize - itemStack.getAmount();
}
}
return count;
}
public static String getItemName(ItemStack itemStack) {
ItemMeta meta = itemStack.getItemMeta();
if (meta != null && meta.hasDisplayName()) {
return meta.getDisplayName();
}
Material material = itemStack.getType();
String id = material.name().toLowerCase();
boolean isBlock = material.isBlock();
String prefix = isBlock ? "block" : "item";
return LangUtil.getLang(prefix+".minecraft."+id);
}
}

View File

@ -1,6 +1,13 @@
package com.io.yutian.auchestshop.util;
import com.io.yutian.aulib.nbt.NBTItem;
import com.io.yutian.aulib.nbt.NBTString;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.EnumSet;
@ -14,6 +21,28 @@ public class ShopUtil {
SHOPABLES.add(Material.TRAPPED_CHEST);
}
public static boolean isShopItem(ItemStack itemStack) {
NBTItem nbtItem = new NBTItem(itemStack);
if (!nbtItem.has("item_type", NBTString.TYPE_ID)) {
return false;
}
String itemType = (String) nbtItem.get("item_type").getValue();
return itemType.equalsIgnoreCase("chest_shop");
}
public static Block getAttached(@NotNull Block block) {
BlockData blockData = block.getBlockData();
if (blockData instanceof final Directional directional) {
return block.getRelative(directional.getFacing().getOppositeFace());
} else {
return null;
}
}
public static boolean isWallSign(@NotNull Material material) {
return Tag.WALL_SIGNS.isTagged(material);
}
public static boolean isShoppables(@NotNull Material material) {
return SHOPABLES.contains(material);
}

View File

@ -0,0 +1,17 @@
auchestshop:
create-shop:
up-has-block: "§c创建失败,商店上方存在非法方块"
success: "§a成功创建商店"
error-1: "§c商店创建已取消"
remove-shop: "§a成功移除商店"
action:
cancel: "§a操作已取消"
message-1: "§a请在聊天中输入交易1个§e$0§a所需的价格。"
message-2: "§a聊天栏中输入想出售§e$0§a的物品数量,您现在可以卖出§e$1§a件物品。输入§ball§a来出售全部物品"
message-3: "§a聊天栏中输入想购买§e$0§a的物品数量,您可以购买§e$1§a件物品。输入§ball§a来购买全部物品"
error-1: "§c请输入有效的数字"
error-2: "§c请输入大于0的整数"
error-3: "§c您的价格中的小数点后位数超过上限"
error-4: "§c该值超过了最大值"
error-5: "§c请输入一个有效的价格"
success-create: "§a成功设置商店物品"