mirror of
https://e.coding.net/circlecloud/QuickShop.git
synced 2025-10-02 12:37:27 +00:00
Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
8edb488ab2 | |||
686dc054de | |||
72d3e24b8a | |||
0948887e4b | |||
82c2591c9b | |||
a11686a839 | |||
de3fa516e1 | |||
2b82a8430d | |||
4a18053026 | |||
d768f93c6c | |||
ccedc8cfea | |||
7a74e88cd1 | |||
4b41bdff57 | |||
4f3ee83b47 | |||
1a624a8384 | |||
1b519fe90f | |||
c193f2bc95 | |||
5dd8dd78af | |||
e42701f376 | |||
55cd9e386c | |||
ea61beb719 | |||
bd2c2aedf9 | |||
664e5c7734 |
33
pom.xml
33
pom.xml
@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.maxgamer</groupId>
|
||||
<artifactId>QuickShop</artifactId>
|
||||
<version>2.1.1</version>
|
||||
<version>2.3.1</version>
|
||||
<description>快捷商店重置版本...</description>
|
||||
<build>
|
||||
<finalName>${project.name}</finalName>
|
||||
@ -50,12 +50,28 @@
|
||||
</ciManagement>
|
||||
<properties>
|
||||
<env.GIT_COMMIT>DEBUG</env.GIT_COMMIT>
|
||||
<update.description>§a全新版本 §c虚拟悬浮物(橙子提供 对 就是那个汉化COI的逗比)§e7老板修复逗比BUG...</update.description>
|
||||
<update.description>§a全新版本 §c修复大量问题§e(有问题找jiongjiong)...</update.description>
|
||||
<update.changes>
|
||||
§b2.3.1 - §cfix: 修复低版本服务端不兼容的问题;
|
||||
§b2.3.0 - §afeat: 修复大量BUG(感谢jiongjiong提交的PR 下面都是他修复的);
|
||||
§b - §cfix: 修复不必要的获取离线玩家导致创建大量getProfile线程导致服务端崩溃的问题;
|
||||
§b - §cfix: 连接池线程安全修正;
|
||||
§b - §cfix: 让修改价格等行为异步操作数据库;
|
||||
§b - §cfix: 异步调用数据库删除方法(BlockBreakEvent触发);
|
||||
§b - §cfix: 异步保存商店到数据库;
|
||||
§b - §cfix: 修复收购商店不触发ShopPurchaseEvent的问题,并且调整触发的时机,避免没钱购买也触发这个事件;
|
||||
§b - §cfix: 修复一些奇怪的问题;
|
||||
§b - §cfix: 修复一处线程安全问题;
|
||||
§b - §cfix: 修复匹配两个物品是否相同的问题;
|
||||
§b - §cfix: 删除不必要的监听器
|
||||
</update.changes>
|
||||
<update.changelog>
|
||||
§b2.2.0 - §cfix: 修复 1.12 不兼容的问题;
|
||||
§b2.1.2 - §e移除过期的监听方法...;
|
||||
§b2.1.0 - §c修复大箱子刷物品的问题...;
|
||||
§b2.0.1 - §a使用新类库 兼容最新版本...;
|
||||
§b1.9.5 - §a1.10+兼容虚拟悬浮物...;
|
||||
</update.changes>
|
||||
</update.changelog>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
@ -63,14 +79,14 @@
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>yumc-repo</id>
|
||||
<url>http://repo.yumc.pw/content/groups/public/</url>
|
||||
<url>https://repo.yumc.pw/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>jtb</id>
|
||||
<name>YUMC</name>
|
||||
<url>http://repo.yumc.pw/content/repositories/yumcenter/</url>
|
||||
<url>https://repo.yumc.pw/repository/yumcenter/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<dependencies>
|
||||
@ -78,12 +94,7 @@
|
||||
<groupId>pw.yumc</groupId>
|
||||
<artifactId>YumCore</artifactId>
|
||||
<type>jar</type>
|
||||
<version>1.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.Cnly.WowSuchCleaner</groupId>
|
||||
<artifactId>WowSuchCleaner</artifactId>
|
||||
<version>1.6.5</version>
|
||||
<version>[1.8.1,)</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -5,14 +5,18 @@ import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MySQLCore implements DatabaseCore {
|
||||
private String url;
|
||||
/** The connection properties... user, pass, autoReconnect.. */
|
||||
/**
|
||||
* The connection properties... user, pass, autoReconnect..
|
||||
*/
|
||||
private Properties info;
|
||||
private static final int MAX_CONNECTIONS = 8;
|
||||
private static ArrayList<Connection> pool = new ArrayList<>();
|
||||
private static final List<Connection> POOL = Collections.synchronizedList(new ArrayList<Connection>());
|
||||
|
||||
public MySQLCore(String host, String user, String pass, String database, String port) {
|
||||
info = new Properties();
|
||||
@ -22,8 +26,7 @@ public class MySQLCore implements DatabaseCore {
|
||||
info.put("useUnicode", "true");
|
||||
info.put("characterEncoding", "utf8");
|
||||
this.url = "jdbc:mysql://" + host + ":" + port + "/" + database;
|
||||
for (int i = 0; i < MAX_CONNECTIONS; i++)
|
||||
pool.add(null);
|
||||
for (int i = 0; i < MAX_CONNECTIONS; i++) {POOL.add(null);}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -31,9 +34,10 @@ public class MySQLCore implements DatabaseCore {
|
||||
*
|
||||
* @return The database connection
|
||||
*/
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
for (int i = 0; i < MAX_CONNECTIONS; i++) {
|
||||
Connection connection = pool.get(i);
|
||||
Connection connection = POOL.get(i);
|
||||
try {
|
||||
// If we have a current connection, fetch it
|
||||
if (connection != null && !connection.isClosed()) {
|
||||
@ -43,7 +47,7 @@ public class MySQLCore implements DatabaseCore {
|
||||
// Else, it is invalid, so we return another connection.
|
||||
}
|
||||
connection = DriverManager.getConnection(this.url, info);
|
||||
pool.set(i, connection);
|
||||
POOL.set(i, connection);
|
||||
return connection;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.maxgamer.QuickShop.Listeners;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
@ -7,12 +8,10 @@ import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.maxgamer.QuickShop.QuickShop;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Netherfoam
|
||||
*
|
||||
*/
|
||||
public class ChatListener implements Listener {
|
||||
QuickShop plugin;
|
||||
private QuickShop plugin;
|
||||
|
||||
public ChatListener(final QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -23,7 +22,7 @@ public class ChatListener implements Listener {
|
||||
if (!plugin.getShopManager().getActions().containsKey(e.getPlayer().getName())) {
|
||||
return;
|
||||
}
|
||||
plugin.getShopManager().handleChat(e.getPlayer(), e.getMessage());
|
||||
e.setCancelled(true);
|
||||
Bukkit.getScheduler().runTask(plugin, () -> plugin.getShopManager().handleChat(e.getPlayer(), e.getMessage()));
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package org.maxgamer.QuickShop.Listeners;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
@ -65,7 +65,7 @@ public class PlayerListener implements Listener {
|
||||
p.openInventory(in);
|
||||
}
|
||||
// Add the new action
|
||||
final HashMap<String, Info> actions = plugin.getShopManager().getActions();
|
||||
final Map<String, Info> actions = plugin.getShopManager().getActions();
|
||||
final Info info = new Info(shop.getLocation(), ShopAction.BUY, null, null, shop);
|
||||
actions.put(p.getName(), info);
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
package org.maxgamer.QuickShop.Listeners;
|
||||
|
||||
import io.github.Cnly.WowSuchCleaner.WowSuchCleaner.ItemPreCleanEvent;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.maxgamer.QuickShop.Util.MarkUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WowSuchCleanerListener implements Listener {
|
||||
@EventHandler
|
||||
public void onWSCClear(final ItemPreCleanEvent e) {
|
||||
final List<Item> clearList = new ArrayList<>();
|
||||
final List<ItemStack> aucList = new ArrayList<>();
|
||||
final List<Item> cleanList = e.getItemsToClean();
|
||||
final List<ItemStack> acList = e.getItemsToAuction();
|
||||
if (cleanList != null) {
|
||||
for (final Item item : cleanList) {
|
||||
if (MarkUtil.hasMark(item.getItemStack())) {
|
||||
clearList.add(item);
|
||||
}
|
||||
}
|
||||
e.getItemsToClean().removeAll(clearList);
|
||||
}
|
||||
if (acList != null) {
|
||||
for (final ItemStack itemStack : acList) {
|
||||
if (MarkUtil.hasMark(itemStack)) {
|
||||
aucList.add(itemStack);
|
||||
}
|
||||
}
|
||||
e.getItemsToAuction().removeAll(aucList);
|
||||
}
|
||||
}
|
||||
}
|
@ -36,12 +36,10 @@ import org.maxgamer.QuickShop.Economy.EconomyCore;
|
||||
import org.maxgamer.QuickShop.Economy.Economy_Vault;
|
||||
import org.maxgamer.QuickShop.Listeners.BlockListener;
|
||||
import org.maxgamer.QuickShop.Listeners.ChatListener;
|
||||
import org.maxgamer.QuickShop.Listeners.ChunkListener;
|
||||
import org.maxgamer.QuickShop.Listeners.LockListener;
|
||||
import org.maxgamer.QuickShop.Listeners.PlayerListener;
|
||||
import org.maxgamer.QuickShop.Listeners.ProtectListener;
|
||||
import org.maxgamer.QuickShop.Listeners.WorldListener;
|
||||
import org.maxgamer.QuickShop.Listeners.WowSuchCleanerListener;
|
||||
import org.maxgamer.QuickShop.Shop.ContainerShop;
|
||||
import org.maxgamer.QuickShop.Shop.Shop;
|
||||
import org.maxgamer.QuickShop.Shop.ShopManager;
|
||||
@ -301,16 +299,6 @@ public class QuickShop extends JavaPlugin {
|
||||
MsgUtil.clean();
|
||||
// Register events
|
||||
final PluginManager pm = this.getServer().getPluginManager();
|
||||
final Plugin wsc = pm.getPlugin("WowSuchCleaner");
|
||||
if (wsc != null && wsc.isEnabled()) {
|
||||
getLogger().info("发现 WowSuchCleaner 插件 开启相关功能...");
|
||||
try {
|
||||
Class.forName("io.github.Cnly.WowSuchCleaner.WowSuchCleaner.ItemPreCleanEvent");
|
||||
pm.registerEvents(new WowSuchCleanerListener(), this);
|
||||
} catch (final ClassNotFoundException e) {
|
||||
getLogger().info("WowSuchCleaner 版本过低 可能造成悬浮物上架...");
|
||||
}
|
||||
}
|
||||
pm.registerEvents(new BlockListener(this), this);
|
||||
pm.registerEvents(new PlayerListener(this), this);
|
||||
pm.registerEvents(new WorldListener(this), this);
|
||||
|
@ -195,7 +195,13 @@ public class ContainerShop implements Shop {
|
||||
final int y = this.getLocation().getBlockY();
|
||||
final int z = this.getLocation().getBlockZ();
|
||||
final String world = this.getLocation().getWorld().getName();
|
||||
// Async database execute
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
plugin.getDB().execute("DELETE FROM shops WHERE x = '" + x + "' AND y = '" + y + "' AND z = '" + z + "' AND world = '" + world + "'");
|
||||
}
|
||||
});
|
||||
// Refund if necessary
|
||||
if (plugin.getConfig().getBoolean("shop.refund")) {
|
||||
plugin.getEcon().deposit(this.getOwner(), plugin.getConfig().getDouble("shop.cost"));
|
||||
@ -650,14 +656,23 @@ public class ContainerShop implements Shop {
|
||||
final String world = this.getLocation().getWorld().getName();
|
||||
final int unlimited = this.isUnlimited() ? 1 : 0;
|
||||
final String q = "UPDATE shops SET owner = ?, itemConfig = ?, unlimited = ?, type = ?, price = ? WHERE x = ? AND y = ? and z = ? and world = ?";
|
||||
final String owner = this.getOwner();
|
||||
final String item = Util.serialize(this.getItem());
|
||||
double price = this.getPrice();
|
||||
// Async database execute
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
plugin.getDB().execute(q, this.getOwner(), Util.serialize(this.getItem()), unlimited, shopType.toID(), this.getPrice(), x, y, z, world);
|
||||
plugin.getDB().execute(q, owner, item, unlimited, shopType.toID(), price, x, y, z, world);
|
||||
} catch (final Exception e) {
|
||||
plugin.getLogger().warning("无法保存商店到数据库!!!");
|
||||
plugin.getLogger().warning("错误信息: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void checkDisplay() {
|
||||
if (!plugin.getConfigManager().isDisplay()) { return; }
|
||||
|
@ -5,7 +5,6 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
@ -75,24 +74,7 @@ public abstract class FakeItem extends DisplayItem {
|
||||
}
|
||||
}
|
||||
};
|
||||
final PacketAdapter chunkBulkPacketListener = new PacketAdapter(plugin, PacketType.Play.Server.MAP_CHUNK_BULK) {
|
||||
@Override
|
||||
public void onPacketSending(final PacketEvent event) {
|
||||
try {
|
||||
final PacketContainer packet = event.getPacket();
|
||||
final Player p = event.getPlayer();
|
||||
final int[] chunksX = packet.getIntegerArrays().read(0);
|
||||
final int[] chunksZ = packet.getIntegerArrays().read(1);
|
||||
for (int i = 0; i < chunksX.length; i++) {
|
||||
final List<FakeItem> fakesInChunk = fakes.get(getChunkIdentifyString(p.getWorld().getChunkAt(chunksX[i], chunksZ[i])));
|
||||
sendChunkPacket(p, fakesInChunk);
|
||||
}
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
};
|
||||
ProtocolLibrary.getProtocolManager().addPacketListener(chunkPacketListener);
|
||||
ProtocolLibrary.getProtocolManager().addPacketListener(chunkBulkPacketListener);
|
||||
registered = true;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,18 @@
|
||||
package org.maxgamer.QuickShop.Shop;
|
||||
|
||||
import org.bukkit.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
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.BlockState;
|
||||
@ -14,14 +26,9 @@ import org.maxgamer.QuickShop.QuickShop;
|
||||
import org.maxgamer.QuickShop.Util.MsgUtil;
|
||||
import org.maxgamer.QuickShop.Util.Util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
public class ShopManager {
|
||||
private final HashMap<String, Info> actions = new HashMap<>();
|
||||
|
||||
private final Map<String, Info> actions = new ConcurrentHashMap<String, Info>();
|
||||
private final QuickShop plugin;
|
||||
private final HashMap<String, HashMap<ShopChunk, HashMap<Location, Shop>>> shops = new HashMap<>();
|
||||
|
||||
@ -60,7 +67,11 @@ public class ShopManager {
|
||||
}
|
||||
/* 修复其他插件调用产生的报错... */
|
||||
try {
|
||||
final PlayerInteractEvent pie = new PlayerInteractEvent(p, Action.RIGHT_CLICK_BLOCK, new ItemStack(Material.AIR), b, bf); // PIE = PlayerInteractEvent - What else?
|
||||
final PlayerInteractEvent pie = new PlayerInteractEvent(p,
|
||||
Action.RIGHT_CLICK_BLOCK,
|
||||
new ItemStack(Material.AIR),
|
||||
b,
|
||||
bf); // PIE = PlayerInteractEvent - What else?
|
||||
Bukkit.getPluginManager().callEvent(pie);
|
||||
pie.getPlayer().closeInventory(); // If the player has chat open, this will close their chat.
|
||||
if (pie.isCancelled()) { return false; }
|
||||
@ -96,27 +107,38 @@ public class ShopManager {
|
||||
public void createShop(final Shop shop) {
|
||||
final Location loc = shop.getLocation();
|
||||
final ItemStack item = shop.getItem();
|
||||
final String serializeItem = Util.serialize(item);
|
||||
final String worldName = loc.getWorld().getName();
|
||||
final int x = loc.getBlockX();
|
||||
final int y = loc.getBlockY();
|
||||
final int z = loc.getBlockZ();
|
||||
// Async database execute
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Write it to the database
|
||||
final String q = "INSERT INTO shops (owner, price, itemConfig, x, y, z, world, unlimited, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
plugin.getDB().execute(q,
|
||||
shop.getOwner(),
|
||||
shop.getPrice(),
|
||||
Util.serialize(item),
|
||||
loc.getBlockX(),
|
||||
loc.getBlockY(),
|
||||
loc.getBlockZ(),
|
||||
loc.getWorld().getName(),
|
||||
serializeItem,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
worldName,
|
||||
(shop.isUnlimited() ? 1 : 0),
|
||||
shop.getShopType().toID());
|
||||
// Add it to the world
|
||||
addShop(loc.getWorld().getName(), shop);
|
||||
} catch (final Exception e) {
|
||||
plugin.getLogger().warning("无法保存商店到数据库! 下次重启商店将会消失!");
|
||||
plugin.getLogger().warning("错误信息: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
// Add it to the world
|
||||
addShop(worldName, shop);
|
||||
}
|
||||
|
||||
public String format(final double d) {
|
||||
return plugin.getEcon().format(d);
|
||||
@ -126,7 +148,7 @@ public class ShopManager {
|
||||
* @return Returns the HashMap<Player name, shopInfo>. Info contains what
|
||||
* their last question etc was.
|
||||
*/
|
||||
public HashMap<String, Info> getActions() {
|
||||
public Map<String, Info> getActions() {
|
||||
return this.actions;
|
||||
}
|
||||
|
||||
@ -205,10 +227,11 @@ public class ShopManager {
|
||||
|
||||
public void handleChat(final Player p, final String msgs) {
|
||||
final String message = ChatColor.stripColor(msgs);
|
||||
final HashMap<String, Info> actions = getActions();
|
||||
final Map<String, Info> actions = getActions();
|
||||
// They wanted to do something.
|
||||
final Info info = actions.remove(p.getName());
|
||||
if (info == null) { return; // multithreaded means this can happen
|
||||
if (info == null) {
|
||||
return; // multithreaded means this can happen
|
||||
}
|
||||
/* Creation handling */
|
||||
if (info.getAction() == ShopAction.CREATE) {
|
||||
@ -330,12 +353,21 @@ public class ShopManager {
|
||||
// Tries to check their balance nicely to see if
|
||||
// they can afford it.
|
||||
if (plugin.getEcon().getBalance(shop.getOwner()) < amount * shop.getPrice()) {
|
||||
p.sendMessage(MsgUtil.p("the-owner-cant-afford-to-buy-from-you", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(shop.getOwner()))));
|
||||
p.sendMessage(MsgUtil.p("the-owner-cant-afford-to-buy-from-you",
|
||||
format(amount * shop.getPrice()),
|
||||
format(plugin.getEcon().getBalance(shop.getOwner()))));
|
||||
return;
|
||||
}
|
||||
final ShopPurchaseEvent e = new ShopPurchaseEvent(shop, p, amount);
|
||||
Bukkit.getPluginManager().callEvent(e);
|
||||
if (e.isCancelled()) {
|
||||
return; // Cancelled
|
||||
}
|
||||
// Check for plugins faking econ.has(amount)
|
||||
if (!plugin.getEcon().withdraw(shop.getOwner(), total)) {
|
||||
p.sendMessage(MsgUtil.p("the-owner-cant-afford-to-buy-from-you", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(shop.getOwner()))));
|
||||
p.sendMessage(MsgUtil.p("the-owner-cant-afford-to-buy-from-you",
|
||||
format(amount * shop.getPrice()),
|
||||
format(plugin.getEcon().getBalance(shop.getOwner()))));
|
||||
return;
|
||||
}
|
||||
if (tax != 0) {
|
||||
@ -347,7 +379,10 @@ public class ShopManager {
|
||||
// Notify the owner of the purchase.
|
||||
String msg = MsgUtil.p("player-sold-to-your-store", p.getName(), "" + amount, shop.getDataName());
|
||||
if (space == amount) {
|
||||
msg += "\n" + MsgUtil.p("shop-out-of-space", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ());
|
||||
msg += "\n" + MsgUtil.p("shop-out-of-space",
|
||||
"" + shop.getLocation().getBlockX(),
|
||||
"" + shop.getLocation().getBlockY(),
|
||||
"" + shop.getLocation().getBlockZ());
|
||||
}
|
||||
MsgUtil.send(shop.getOwner(), msg);
|
||||
}
|
||||
@ -409,7 +444,13 @@ public class ShopManager {
|
||||
createShop(shop);
|
||||
p.sendMessage(MsgUtil.p("success-created-shop"));
|
||||
final Location loc = shop.getLocation();
|
||||
plugin.log(String.format("玩家: %s 创建了一个 %s 商店 在 (%s - %s, %s, %s)", p.getName(), shop.getDataName(), loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ()));
|
||||
plugin.log(String.format("玩家: %s 创建了一个 %s 商店 在 (%s - %s, %s, %s)",
|
||||
p.getName(),
|
||||
shop.getDataName(),
|
||||
loc.getWorld().getName(),
|
||||
loc.getX(),
|
||||
loc.getY(),
|
||||
loc.getZ()));
|
||||
if (!plugin.getConfig().getBoolean("shop.lock")) {
|
||||
// Warn them if they haven't been warned since
|
||||
// reboot
|
||||
@ -445,8 +486,7 @@ public class ShopManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
/* They didn't enter a number. */
|
||||
catch (final NumberFormatException ex) {
|
||||
/* They didn't enter a number. */ catch (final NumberFormatException ex) {
|
||||
p.sendMessage(MsgUtil.p("shop-creation-cancelled"));
|
||||
}
|
||||
}
|
||||
@ -463,25 +503,30 @@ public class ShopManager {
|
||||
p.sendMessage(MsgUtil.p("not-enough-space", "" + pSpace));
|
||||
return;
|
||||
}
|
||||
final ShopPurchaseEvent e = new ShopPurchaseEvent(shop, p, amount);
|
||||
Bukkit.getPluginManager().callEvent(e);
|
||||
if (e.isCancelled()) { return; // Cancelled
|
||||
}
|
||||
// Money handling
|
||||
if (!p.getName().equals(shop.getOwner())) {
|
||||
// Check their balance. Works with *most* economy
|
||||
// plugins*
|
||||
if (plugin.getEcon().getBalance(p.getName()) < amount * shop.getPrice()) {
|
||||
p.sendMessage(MsgUtil.p("you-cant-afford-to-buy", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(p.getName()))));
|
||||
p.sendMessage(MsgUtil.p("you-cant-afford-to-buy",
|
||||
format(amount * shop.getPrice()),
|
||||
format(plugin.getEcon().getBalance(p.getName()))));
|
||||
return;
|
||||
}
|
||||
final ShopPurchaseEvent e = new ShopPurchaseEvent(shop, p, amount);
|
||||
Bukkit.getPluginManager().callEvent(e);
|
||||
if (e.isCancelled()) {
|
||||
return; // Cancelled
|
||||
}
|
||||
// Don't tax them if they're purchasing from
|
||||
// themselves.
|
||||
// Do charge an amount of tax though.
|
||||
final double tax = plugin.getConfigManager().getTax();
|
||||
final double total = amount * shop.getPrice();
|
||||
if (!plugin.getEcon().withdraw(p.getName(), total)) {
|
||||
p.sendMessage(MsgUtil.p("you-cant-afford-to-buy", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(p.getName()))));
|
||||
p.sendMessage(MsgUtil.p("you-cant-afford-to-buy",
|
||||
format(amount * shop.getPrice()),
|
||||
format(plugin.getEcon().getBalance(p.getName()))));
|
||||
return;
|
||||
}
|
||||
if (!shop.isUnlimited() || plugin.getConfig().getBoolean("shop.pay-unlimited-shop-owners")) {
|
||||
@ -494,13 +539,21 @@ public class ShopManager {
|
||||
if (plugin.getConfigManager().isShowTax()) {
|
||||
String msg = MsgUtil.p("player-bought-from-your-store-tax", p.getName(), "" + amount, shop.getDataName(), Util.format((tax * total)));
|
||||
if (stock == amount) {
|
||||
msg += "\n" + MsgUtil.p("shop-out-of-stock", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ(), shop.getDataName());
|
||||
msg += "\n" + MsgUtil.p("shop-out-of-stock",
|
||||
"" + shop.getLocation().getBlockX(),
|
||||
"" + shop.getLocation().getBlockY(),
|
||||
"" + shop.getLocation().getBlockZ(),
|
||||
shop.getDataName());
|
||||
}
|
||||
MsgUtil.send(shop.getOwner(), msg);
|
||||
} else {
|
||||
String msg = MsgUtil.p("player-bought-from-your-store", p.getName(), "" + amount, shop.getDataName());
|
||||
if (stock == amount) {
|
||||
msg += "\n" + MsgUtil.p("shop-out-of-stock", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ(), shop.getDataName());
|
||||
msg += "\n" + MsgUtil.p("shop-out-of-stock",
|
||||
"" + shop.getLocation().getBlockX(),
|
||||
"" + shop.getLocation().getBlockY(),
|
||||
"" + shop.getLocation().getBlockZ(),
|
||||
shop.getDataName());
|
||||
}
|
||||
MsgUtil.send(shop.getOwner(), msg);
|
||||
}
|
||||
@ -564,7 +617,8 @@ public class ShopManager {
|
||||
}
|
||||
shops = chunks.next().values().iterator();
|
||||
}
|
||||
if (!shops.hasNext()) { return this.next(); // Skip to the next one (Empty iterator?)
|
||||
if (!shops.hasNext()) {
|
||||
return this.next(); // Skip to the next one (Empty iterator?)
|
||||
}
|
||||
current = shops.next();
|
||||
return current;
|
||||
|
@ -106,7 +106,7 @@ public class MsgUtil {
|
||||
*/
|
||||
public static void send(final String player, final String message) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
final OfflinePlayer p = Bukkit.getOfflinePlayer(player);
|
||||
final Player p = Bukkit.getPlayerExact(player);
|
||||
if (p == null || !p.isOnline()) {
|
||||
LinkedList<String> msgs = player_messages.computeIfAbsent(player, k -> new LinkedList<>());
|
||||
msgs.add(message);
|
||||
@ -166,8 +166,7 @@ public class MsgUtil {
|
||||
p.sendMessage("");
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+---------------------------------------------------+");
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.p("menu.shop-information"));
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| "
|
||||
+ MsgUtil.p("menu.owner", Bukkit.getOfflinePlayer(shop.getOwner()).getName() == null ? (shop.isUnlimited() ? "系统商店" : "未知") : Bukkit.getOfflinePlayer(shop.getOwner()).getName()));
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.p("menu.owner", shop.getOwner() == null ? (shop.isUnlimited() ? "系统商店" : "未知") : shop.getOwner()));
|
||||
final String msg = ChatColor.DARK_PURPLE + "| " + MsgUtil.p("menu.item", shop.getDataName());
|
||||
sendItemMessage(p, shop.getItem(), msg);
|
||||
if (Util.isTool(item.getType())) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.maxgamer.QuickShop.Util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -18,7 +19,9 @@ import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.material.MaterialData;
|
||||
import org.bukkit.material.Sign;
|
||||
import org.maxgamer.QuickShop.QuickShop;
|
||||
@ -27,6 +30,7 @@ import pw.yumc.YumCore.global.L10N;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Util {
|
||||
|
||||
private static HashSet<Material> blacklist = new HashSet<>();
|
||||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##");
|
||||
private static QuickShop plugin;
|
||||
@ -34,6 +38,9 @@ public class Util {
|
||||
private static HashSet<Material> tools = new HashSet<>();
|
||||
private static HashSet<Material> transparent = new HashSet<>();
|
||||
|
||||
private static boolean hasCheckedMethodCompatibility = false;
|
||||
private static boolean useNewGetContentMethod = false;
|
||||
|
||||
public static void addTransparentBlock(final Material m) {
|
||||
if (!transparent.add(m)) {
|
||||
System.out.println("已添加透明方块: " + m.toString());
|
||||
@ -70,6 +77,21 @@ public class Util {
|
||||
* @return The number of items that match in this inventory.
|
||||
*/
|
||||
public static int countItems(final Inventory inv, final ItemStack item) {
|
||||
if (!hasCheckedMethodCompatibility) {
|
||||
if (hasGetStorageContentsMethod()) {
|
||||
useNewGetContentMethod = true;
|
||||
hasCheckedMethodCompatibility = true;
|
||||
return newCountItems(inv, item);
|
||||
} else {
|
||||
useNewGetContentMethod = false;
|
||||
hasCheckedMethodCompatibility = true;
|
||||
return oldCountItems(inv, item);
|
||||
}
|
||||
}
|
||||
return useNewGetContentMethod ? newCountItems(inv, item) : oldCountItems(inv, item);
|
||||
}
|
||||
|
||||
public static int oldCountItems(final Inventory inv, final ItemStack item) {
|
||||
int items = 0;
|
||||
for (final ItemStack iStack : inv.getContents()) {
|
||||
if (iStack == null) {
|
||||
@ -82,6 +104,19 @@ public class Util {
|
||||
return items;
|
||||
}
|
||||
|
||||
public static int newCountItems(final Inventory inv, final ItemStack item) {
|
||||
int items = 0;
|
||||
for (final ItemStack iStack : inv.getStorageContents()) {
|
||||
if (iStack == null) {
|
||||
continue;
|
||||
}
|
||||
if (Util.matches(item, iStack)) {
|
||||
items += iStack.getAmount();
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items that can be given to the inventory safely.
|
||||
*
|
||||
@ -93,6 +128,33 @@ public class Util {
|
||||
* @return The number of items that can be given to the inventory safely.
|
||||
*/
|
||||
public static int countSpace(final Inventory inv, final ItemStack item) {
|
||||
if (!hasCheckedMethodCompatibility) {
|
||||
if (hasGetStorageContentsMethod()) {
|
||||
useNewGetContentMethod = true;
|
||||
hasCheckedMethodCompatibility = true;
|
||||
return newCountSpace(inv, item);
|
||||
} else {
|
||||
useNewGetContentMethod = false;
|
||||
hasCheckedMethodCompatibility = true;
|
||||
return oldCountSpace(inv, item);
|
||||
}
|
||||
}
|
||||
return useNewGetContentMethod ? newCountSpace(inv, item) : oldCountSpace(inv, item);
|
||||
}
|
||||
|
||||
public static int newCountSpace(final Inventory inv, final ItemStack item) {
|
||||
int space = 0;
|
||||
for (final ItemStack iStack : inv.getStorageContents()) {
|
||||
if (iStack == null || iStack.getType() == Material.AIR) {
|
||||
space += item.getMaxStackSize();
|
||||
} else if (matches(item, iStack)) {
|
||||
space += item.getMaxStackSize() - iStack.getAmount();
|
||||
}
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
public static int oldCountSpace(final Inventory inv, final ItemStack item) {
|
||||
int space = 0;
|
||||
for (final ItemStack iStack : inv.getContents()) {
|
||||
if (iStack == null || iStack.getType() == Material.AIR) {
|
||||
@ -111,7 +173,9 @@ public class Util {
|
||||
}
|
||||
|
||||
public static String firstUppercase(final String string) {
|
||||
if (string.length() > 1) { return Character.toUpperCase(string.charAt(0)) + string.substring(1).toLowerCase(); }
|
||||
if (string.length() > 1) {
|
||||
return Character.toUpperCase(string.charAt(0)) + string.substring(1).toLowerCase();
|
||||
}
|
||||
return string.toUpperCase();
|
||||
}
|
||||
|
||||
@ -177,14 +241,18 @@ public class Util {
|
||||
* @return the block which is also a chest and connected to b.
|
||||
*/
|
||||
public static Block getSecondHalf(final Block b) {
|
||||
if (!b.getType().toString().contains("CHEST")) { return null; }
|
||||
if (!b.getType().toString().contains("CHEST")) {
|
||||
return null;
|
||||
}
|
||||
final Block[] blocks = new Block[4];
|
||||
blocks[0] = b.getRelative(1, 0, 0);
|
||||
blocks[1] = b.getRelative(-1, 0, 0);
|
||||
blocks[2] = b.getRelative(0, 0, 1);
|
||||
blocks[3] = b.getRelative(0, 0, -1);
|
||||
for (final Block c : blocks) {
|
||||
if (c.getType() == b.getType()) { return c; }
|
||||
if (c.getType() == b.getType()) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -392,9 +460,7 @@ public class Util {
|
||||
}
|
||||
// Calculate the chunks coordinates. These are 1,2,3 for each chunk, NOT
|
||||
// location rounded to the nearest 16.
|
||||
final int x = (int) Math.floor((loc.getBlockX()) / 16.0);
|
||||
final int z = (int) Math.floor((loc.getBlockZ()) / 16.0);
|
||||
if (loc.getWorld().isChunkLoaded(x, z)) {
|
||||
if (loc.getWorld().isChunkLoaded(loc.getBlockX() >> 4, loc.getBlockZ() >> 4)) {
|
||||
// System.out.println("Chunk is loaded " + x + ", " + z);
|
||||
return true;
|
||||
}
|
||||
@ -455,39 +521,114 @@ public class Util {
|
||||
* The first item stack
|
||||
* @param stack2
|
||||
* The second item stack
|
||||
* @return true if the itemstacks match. (Material, durability, enchants, name)
|
||||
* @return true if the itemstacks match. (Material, durability, enchants,
|
||||
* name)
|
||||
*/
|
||||
public static boolean matches(final ItemStack stack1, final ItemStack stack2) {
|
||||
if (stack1 == stack2) { return true; // Referring to the same thing, or both are null.
|
||||
if (stack1 == stack2) {
|
||||
return true; // Referring to the same thing, or both are null.
|
||||
}
|
||||
if (stack1 == null || stack2 == null) { return false; // One of them is null (Can't be both, see above)
|
||||
if (stack1 == null || stack2 == null) {
|
||||
return false; // One of them is null (Can't be both, see above)
|
||||
}
|
||||
if (stack1.getType() != stack2.getType()) { return false; // Not the same material
|
||||
if (stack1.getType() != stack2.getType()) {
|
||||
return false; // Not the same material
|
||||
}
|
||||
if (stack1.getDurability() != stack2.getDurability()) { return false; // Not the same durability
|
||||
if (stack1.getDurability() != stack2.getDurability()) {
|
||||
return false; // Not the same durability
|
||||
}
|
||||
if (!stack1.getEnchantments().equals(stack2.getEnchantments())) { return false; // They have the same enchants
|
||||
if (!stack1.getEnchantments().equals(stack2.getEnchantments())) {
|
||||
return false; // They have the same enchants
|
||||
}
|
||||
if (stack1.getItemMeta().hasDisplayName() || stack2.getItemMeta().hasDisplayName()) {
|
||||
if (stack1.getItemMeta().hasDisplayName() && stack2.getItemMeta().hasDisplayName()) {
|
||||
if (!stack1.getItemMeta().getDisplayName().equals(stack2.getItemMeta().getDisplayName())) { return false; // items have different display name
|
||||
if (!stack1.getItemMeta().getDisplayName().equals(stack2.getItemMeta().getDisplayName())) {
|
||||
return false; // items have different display name
|
||||
}
|
||||
} else {
|
||||
return false; // one of the item stacks have a display name
|
||||
}
|
||||
}
|
||||
if (stack1.getItemMeta().hasLore() || stack2.getItemMeta().hasLore()) {
|
||||
if (stack1.getItemMeta().hasLore() && stack2.getItemMeta().hasLore()) {
|
||||
if (!stack1.getItemMeta().getLore().equals(stack2.getItemMeta().getLore())) {
|
||||
return false; // items have different lore
|
||||
}
|
||||
} else {
|
||||
return false; // one of the item stacks have lore
|
||||
}
|
||||
}
|
||||
// Not the same material and not same durability has returned to false
|
||||
if (stack1.getType() == Material.SKULL_ITEM) {
|
||||
if (stack1.getDurability() == 3) {
|
||||
SkullMeta skullMeta1 = (SkullMeta) stack1.getItemMeta();
|
||||
SkullMeta skullMeta2 = (SkullMeta) stack2.getItemMeta();
|
||||
if (skullMeta1.hasOwner() || skullMeta2.hasOwner()) {
|
||||
if (skullMeta1.hasOwner() && skullMeta2.hasOwner()) {
|
||||
if (!skullMeta1.getOwner().equals(skullMeta1.getOwner())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (stack1.getType() == Material.WRITTEN_BOOK) {
|
||||
if (stack1.hasItemMeta() || stack2.hasItemMeta()) {
|
||||
if (stack1.hasItemMeta() && stack2.hasItemMeta()) {
|
||||
BookMeta bookMeta1 = (BookMeta) stack1.getItemMeta();
|
||||
BookMeta bookMeta2 = (BookMeta) stack2.getItemMeta();
|
||||
// title
|
||||
if (bookMeta1.hasTitle() || bookMeta2.hasTitle()) {
|
||||
if (bookMeta1.hasTitle() && bookMeta2.hasTitle()) {
|
||||
if (!bookMeta1.getTitle().equals(bookMeta2.getTitle())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// author
|
||||
if (bookMeta1.hasAuthor() || bookMeta2.hasAuthor()) {
|
||||
if (bookMeta1.hasAuthor() && bookMeta2.hasAuthor()) {
|
||||
if (!bookMeta1.getAuthor().equals(bookMeta2.getAuthor())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// content
|
||||
if (bookMeta1.hasPages() || bookMeta2.hasPages()) {
|
||||
if (bookMeta1.hasPages() && bookMeta2.hasPages()) {
|
||||
if (!bookMeta1.getPages().equals(bookMeta2.getPages())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
Class.forName("org.bukkit.inventory.meta.EnchantmentStorageMeta");
|
||||
final boolean book1 = stack1.getItemMeta() instanceof EnchantmentStorageMeta;
|
||||
final boolean book2 = stack2.getItemMeta() instanceof EnchantmentStorageMeta;
|
||||
if (book1 != book2) { return false;// One has enchantment meta, the other does not.
|
||||
if (book1 != book2) {
|
||||
return false;// One has enchantment meta, the other does not.
|
||||
}
|
||||
if (book1) { // They are the same here (both true or both
|
||||
// false). So if one is true, the other is
|
||||
// true.
|
||||
final Map<Enchantment, Integer> ench1 = ((EnchantmentStorageMeta) stack1.getItemMeta()).getStoredEnchants();
|
||||
final Map<Enchantment, Integer> ench2 = ((EnchantmentStorageMeta) stack2.getItemMeta()).getStoredEnchants();
|
||||
if (!ench1.equals(ench2)) { return false; // Enchants aren't the same.
|
||||
if (!ench1.equals(ench2)) {
|
||||
return false; // Enchants aren't the same.
|
||||
}
|
||||
}
|
||||
} catch (final ClassNotFoundException e) {
|
||||
@ -513,4 +654,20 @@ public class Util {
|
||||
cfg.set("item", iStack);
|
||||
return cfg.saveToString();
|
||||
}
|
||||
|
||||
public static boolean hasGetStorageContentsMethod() {
|
||||
try {
|
||||
Class<?> inventory = Class.forName("org.bukkit.inventory.Inventory");
|
||||
if (inventory != null) {
|
||||
for (Method method : inventory.getDeclaredMethods()) {
|
||||
if (method != null && method.getName().equals("getStorageContents")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user