Move plugin src directory to a sub module

This commit is contained in:
Flowsqy 2021-12-27 15:36:23 +01:00
parent a77926b3b1
commit 51bee2dd20
92 changed files with 6 additions and 21562 deletions

View File

@ -5,8 +5,13 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>de.epiceric</groupId> <groupId>de.epiceric</groupId>
<artifactId>ShopChest</artifactId> <artifactId>ShopChest-parent</artifactId>
<version>1.14.0-SNAPSHOT</version> <version>1.14.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>plugin</module>
</modules>
<name>ShopChest</name> <name>ShopChest</name>
<url>https://www.spigotmc.org/resources/shopchest.11431/</url> <url>https://www.spigotmc.org/resources/shopchest.11431/</url>

View File

@ -1,740 +0,0 @@
package de.epiceric.shopchest;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import com.palmergames.bukkit.towny.Towny;
import com.wasteofplastic.askyblock.ASkyBlock;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.AdvancedPie;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.codemc.worldguardwrapper.WorldGuardWrapper;
import de.epiceric.shopchest.command.ShopCommand;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.config.HologramFormat;
import de.epiceric.shopchest.event.ShopInitializedEvent;
import de.epiceric.shopchest.external.BentoBoxShopFlag;
import de.epiceric.shopchest.external.PlotSquaredOldShopFlag;
import de.epiceric.shopchest.external.PlotSquaredShopFlag;
import de.epiceric.shopchest.external.WorldGuardShopFlag;
import de.epiceric.shopchest.external.listeners.ASkyBlockListener;
import de.epiceric.shopchest.external.listeners.GriefPreventionListener;
import de.epiceric.shopchest.external.listeners.IslandWorldListener;
import de.epiceric.shopchest.external.listeners.PlotSquaredListener;
import de.epiceric.shopchest.external.listeners.TownyListener;
import de.epiceric.shopchest.external.listeners.USkyBlockListener;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.listeners.AreaShopListener;
import de.epiceric.shopchest.listeners.BentoBoxListener;
import de.epiceric.shopchest.listeners.BlockExplodeListener;
import de.epiceric.shopchest.listeners.ChestProtectListener;
import de.epiceric.shopchest.listeners.CreativeModeListener;
import de.epiceric.shopchest.listeners.NotifyPlayerOnJoinListener;
import de.epiceric.shopchest.listeners.ShopInteractListener;
import de.epiceric.shopchest.listeners.ShopItemListener;
import de.epiceric.shopchest.listeners.ShopUpdateListener;
import de.epiceric.shopchest.listeners.WorldGuardListener;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.sql.Database;
import de.epiceric.shopchest.sql.MySQL;
import de.epiceric.shopchest.sql.SQLite;
import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.ClickType;
import de.epiceric.shopchest.utils.Permissions;
import de.epiceric.shopchest.utils.ShopUpdater;
import de.epiceric.shopchest.utils.ShopUtils;
import de.epiceric.shopchest.utils.UpdateChecker;
import de.epiceric.shopchest.utils.UpdateChecker.UpdateCheckerResult;
import de.epiceric.shopchest.utils.Utils;
import fr.xephi.authme.AuthMe;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
import me.wiefferink.areashop.AreaShop;
import net.milkbowl.vault.economy.Economy;
import pl.islandworld.IslandWorld;
import us.talabrek.ultimateskyblock.api.uSkyBlockAPI;
import world.bentobox.bentobox.BentoBox;
public class ShopChest extends JavaPlugin {
private static ShopChest instance;
private Config config;
private HologramFormat hologramFormat;
private ShopCommand shopCommand;
private Economy econ = null;
private Database database;
private boolean isUpdateNeeded = false;
private String latestVersion = "";
private String downloadLink = "";
private ShopUtils shopUtils;
private FileWriter fw;
private Plugin worldGuard;
private Towny towny;
private AuthMe authMe;
private uSkyBlockAPI uSkyBlock;
private ASkyBlock aSkyBlock;
private IslandWorld islandWorld;
private GriefPrevention griefPrevention;
private AreaShop areaShop;
private BentoBox bentoBox;
private ShopUpdater updater;
private ExecutorService shopCreationThreadPool;
/**
* @return An instance of ShopChest
*/
public static ShopChest getInstance() {
return instance;
}
/**
* Sets up the economy of Vault
* @return Whether an economy plugin has been registered
*/
private boolean setupEconomy() {
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
if (rsp == null) {
return false;
}
econ = rsp.getProvider();
return econ != null;
}
@Override
public void onLoad() {
instance = this;
config = new Config(this);
if (Config.enableDebugLog) {
File debugLogFile = new File(getDataFolder(), "debug.txt");
try {
if (!debugLogFile.exists()) {
debugLogFile.createNewFile();
}
new PrintWriter(debugLogFile).close();
fw = new FileWriter(debugLogFile, true);
} catch (IOException e) {
getLogger().info("Failed to instantiate FileWriter");
e.printStackTrace();
}
}
debug("Loading ShopChest version " + getDescription().getVersion());
worldGuard = Bukkit.getServer().getPluginManager().getPlugin("WorldGuard");
if (worldGuard != null) {
WorldGuardShopFlag.register(this);
}
}
@Override
public void onEnable() {
debug("Enabling ShopChest version " + getDescription().getVersion());
if (!getServer().getPluginManager().isPluginEnabled("Vault")) {
debug("Could not find plugin \"Vault\"");
getLogger().severe("Could not find plugin \"Vault\"");
getServer().getPluginManager().disablePlugin(this);
return;
}
if (!setupEconomy()) {
debug("Could not find any Vault economy dependency!");
getLogger().severe("Could not find any Vault economy dependency!");
getServer().getPluginManager().disablePlugin(this);
return;
}
switch (Utils.getServerVersion()) {
case "v1_8_R1":
case "v1_8_R2":
case "v1_8_R3":
case "v1_9_R1":
case "v1_9_R2":
case "v1_10_R1":
case "v1_11_R1":
case "v1_12_R1":
case "v1_13_R1":
case "v1_13_R2":
case "v1_14_R1":
case "v1_15_R1":
case "v1_16_R1":
case "v1_16_R2":
case "v1_16_R3":
break;
default:
debug("Server version not officially supported: " + Utils.getServerVersion() + "!");
debug("Plugin may still work, but more errors are expected!");
getLogger().warning("Server version not officially supported: " + Utils.getServerVersion() + "!");
getLogger().warning("Plugin may still work, but more errors are expected!");
}
shopUtils = new ShopUtils(this);
saveResource("item_names.txt", true);
LanguageUtils.load();
File hologramFormatFile = new File(getDataFolder(), "hologram-format.yml");
if (!hologramFormatFile.exists()) {
saveResource("hologram-format.yml", false);
}
hologramFormat = new HologramFormat(this);
shopCommand = new ShopCommand(this);
shopCreationThreadPool = new ThreadPoolExecutor(0, 8,
5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
loadExternalPlugins();
loadMetrics();
initDatabase();
checkForUpdates();
registerListeners();
registerExternalListeners();
initializeShops();
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
updater = new ShopUpdater(this);
updater.start();
}
@Override
public void onDisable() {
debug("Disabling ShopChest...");
if (shopUtils == null) {
// Plugin has not been fully enabled (probably due to errors),
// so only close file writer.
if (fw != null && Config.enableDebugLog) {
try {
fw.close();
} catch (IOException e) {
getLogger().severe("Failed to close FileWriter");
e.printStackTrace();
}
}
return;
}
if (getShopCommand() != null) {
getShopCommand().unregister();
}
ClickType.clear();
if (updater != null) {
debug("Stopping updater");
updater.stop();
}
if (shopCreationThreadPool != null) {
shopCreationThreadPool.shutdown();
}
shopUtils.removeShops();
debug("Removed shops");
if (database != null && database.isInitialized()) {
if (database instanceof SQLite) {
((SQLite) database).vacuum();
}
database.disconnect();
}
if (fw != null && Config.enableDebugLog) {
try {
fw.close();
} catch (IOException e) {
getLogger().severe("Failed to close FileWriter");
e.printStackTrace();
}
}
}
private void loadExternalPlugins() {
Plugin townyPlugin = Bukkit.getServer().getPluginManager().getPlugin("Towny");
if (townyPlugin instanceof Towny) {
towny = (Towny) townyPlugin;
}
Plugin authMePlugin = Bukkit.getServer().getPluginManager().getPlugin("AuthMe");
if (authMePlugin instanceof AuthMe) {
authMe = (AuthMe) authMePlugin;
}
Plugin uSkyBlockPlugin = Bukkit.getServer().getPluginManager().getPlugin("uSkyBlock");
if (uSkyBlockPlugin instanceof uSkyBlockAPI) {
uSkyBlock = (uSkyBlockAPI) uSkyBlockPlugin;
}
Plugin aSkyBlockPlugin = Bukkit.getServer().getPluginManager().getPlugin("ASkyBlock");
if (aSkyBlockPlugin instanceof ASkyBlock) {
aSkyBlock = (ASkyBlock) aSkyBlockPlugin;
}
Plugin islandWorldPlugin = Bukkit.getServer().getPluginManager().getPlugin("IslandWorld");
if (islandWorldPlugin instanceof IslandWorld) {
islandWorld = (IslandWorld) islandWorldPlugin;
}
Plugin griefPreventionPlugin = Bukkit.getServer().getPluginManager().getPlugin("GriefPrevention");
if (griefPreventionPlugin instanceof GriefPrevention) {
griefPrevention = (GriefPrevention) griefPreventionPlugin;
}
Plugin areaShopPlugin = Bukkit.getServer().getPluginManager().getPlugin("AreaShop");
if (areaShopPlugin instanceof AreaShop) {
areaShop = (AreaShop) areaShopPlugin;
}
Plugin bentoBoxPlugin = getServer().getPluginManager().getPlugin("BentoBox");
if (bentoBoxPlugin instanceof BentoBox) {
bentoBox = (BentoBox) bentoBoxPlugin;
}
if (hasWorldGuard()) {
WorldGuardWrapper.getInstance().registerEvents(this);
}
if (hasPlotSquared()) {
try {
Class.forName("com.plotsquared.core.PlotSquared");
PlotSquaredShopFlag.register(this);
} catch (ClassNotFoundException ex) {
PlotSquaredOldShopFlag.register(this);
}
}
if (hasBentoBox()) {
BentoBoxShopFlag.register(this);
}
}
private void loadMetrics() {
debug("Initializing Metrics...");
Metrics metrics = new Metrics(this, 1726);
metrics.addCustomChart(new SimplePie("creative_setting", () -> Config.creativeSelectItem ? "Enabled" : "Disabled"));
metrics.addCustomChart(new SimplePie("database_type", () -> Config.databaseType.toString()));
metrics.addCustomChart(new AdvancedPie("shop_type", () -> {
int normal = 0;
int admin = 0;
for (Shop shop : shopUtils.getShops()) {
if (shop.getShopType() == ShopType.NORMAL) normal++;
else if (shop.getShopType() == ShopType.ADMIN) admin++;
}
Map<String, Integer> result = new HashMap<>();
result.put("Admin", admin);
result.put("Normal", normal);
return result;
}));
}
private void initDatabase() {
if (Config.databaseType == Database.DatabaseType.SQLite) {
debug("Using database type: SQLite");
getLogger().info("Using SQLite");
database = new SQLite(this);
} else {
debug("Using database type: MySQL");
getLogger().info("Using MySQL");
database = new MySQL(this);
if (Config.databaseMySqlPingInterval > 0) {
Bukkit.getScheduler().runTaskTimer(this, new Runnable() {
@Override
public void run() {
if (database instanceof MySQL) {
((MySQL) database).ping();
}
}
}, Config.databaseMySqlPingInterval * 20L, Config.databaseMySqlPingInterval * 20L);
}
}
}
private void checkForUpdates() {
if (!Config.enableUpdateChecker) {
return;
}
new BukkitRunnable() {
@Override
public void run() {
UpdateChecker uc = new UpdateChecker(ShopChest.this);
UpdateCheckerResult result = uc.check();
switch (result) {
case TRUE:
latestVersion = uc.getVersion();
downloadLink = uc.getLink();
isUpdateNeeded = true;
getLogger().warning(String.format("Version %s is available! You are running version %s.",
latestVersion, getDescription().getVersion()));
for (Player p : getServer().getOnlinePlayers()) {
if (p.hasPermission(Permissions.UPDATE_NOTIFICATION)) {
Utils.sendUpdateMessage(ShopChest.this, p);
}
}
break;
case FALSE:
latestVersion = "";
downloadLink = "";
isUpdateNeeded = false;
break;
case ERROR:
latestVersion = "";
downloadLink = "";
isUpdateNeeded = false;
getLogger().severe("An error occurred while checking for updates.");
break;
}
}
}.runTaskAsynchronously(this);
}
private void registerListeners() {
debug("Registering listeners...");
getServer().getPluginManager().registerEvents(new ShopUpdateListener(this), this);
getServer().getPluginManager().registerEvents(new ShopItemListener(this), this);
getServer().getPluginManager().registerEvents(new ShopInteractListener(this), this);
getServer().getPluginManager().registerEvents(new NotifyPlayerOnJoinListener(this), this);
getServer().getPluginManager().registerEvents(new ChestProtectListener(this), this);
getServer().getPluginManager().registerEvents(new CreativeModeListener(this), this);
if (!Utils.getServerVersion().equals("v1_8_R1")) {
getServer().getPluginManager().registerEvents(new BlockExplodeListener(this), this);
}
if (hasWorldGuard()) {
getServer().getPluginManager().registerEvents(new WorldGuardListener(this), this);
if (hasAreaShop()) {
getServer().getPluginManager().registerEvents(new AreaShopListener(this), this);
}
}
if (hasBentoBox()) {
getServer().getPluginManager().registerEvents(new BentoBoxListener(this), this);
}
}
private void registerExternalListeners() {
if (hasASkyBlock())
getServer().getPluginManager().registerEvents(new ASkyBlockListener(this), this);
if (hasGriefPrevention())
getServer().getPluginManager().registerEvents(new GriefPreventionListener(this), this);
if (hasIslandWorld())
getServer().getPluginManager().registerEvents(new IslandWorldListener(this), this);
if (hasPlotSquared())
getServer().getPluginManager().registerEvents(new PlotSquaredListener(this), this);
if (hasTowny())
getServer().getPluginManager().registerEvents(new TownyListener(this), this);
if (hasUSkyBlock())
getServer().getPluginManager().registerEvents(new USkyBlockListener(this), this);
if (hasWorldGuard())
getServer().getPluginManager().registerEvents(new de.epiceric.shopchest.external.listeners.WorldGuardListener(this), this);
if (hasBentoBox())
getServer().getPluginManager().registerEvents(new de.epiceric.shopchest.external.listeners.BentoBoxListener(this), this);
}
/**
* Initializes the shops
*/
private void initializeShops() {
getShopDatabase().connect(new Callback<Integer>(this) {
@Override
public void onResult(Integer result) {
Chunk[] loadedChunks = getServer().getWorlds().stream().map(World::getLoadedChunks)
.flatMap(Stream::of).toArray(Chunk[]::new);
shopUtils.loadShopAmounts(new Callback<Map<UUID,Integer>>(ShopChest.this) {
@Override
public void onResult(Map<UUID, Integer> result) {
getLogger().info("Loaded shop amounts");
debug("Loaded shop amounts");
}
@Override
public void onError(Throwable throwable) {
getLogger().severe("Failed to load shop amounts. Shop limits will not be working correctly!");
if (throwable != null) getLogger().severe(throwable.getMessage());
}
});
shopUtils.loadShops(loadedChunks, new Callback<Integer>(ShopChest.this) {
@Override
public void onResult(Integer result) {
getServer().getPluginManager().callEvent(new ShopInitializedEvent(result));
getLogger().info("Loaded " + result + " shops in already loaded chunks");
debug("Loaded " + result + " shops in already loaded chunks");
}
@Override
public void onError(Throwable throwable) {
getLogger().severe("Failed to load shops in already loaded chunks");
if (throwable != null) getLogger().severe(throwable.getMessage());
}
});
}
@Override
public void onError(Throwable throwable) {
// Database connection probably failed => disable plugin to prevent more errors
getLogger().severe("No database access. Disabling ShopChest");
if (throwable != null) getLogger().severe(throwable.getMessage());
getServer().getPluginManager().disablePlugin(ShopChest.this);
}
});
}
/**
* Print a message to the <i>/plugins/ShopChest/debug.txt</i> file
* @param message Message to print
*/
public void debug(String message) {
if (Config.enableDebugLog && fw != null) {
try {
Calendar c = Calendar.getInstance();
String timestamp = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(c.getTime());
fw.write(String.format("[%s] %s\r\n", timestamp, message));
fw.flush();
} catch (IOException e) {
getLogger().severe("Failed to print debug message.");
e.printStackTrace();
}
}
}
/**
* Print a {@link Throwable}'s stacktrace to the <i>/plugins/ShopChest/debug.txt</i> file
* @param throwable {@link Throwable} whose stacktrace will be printed
*/
public void debug(Throwable throwable) {
if (Config.enableDebugLog && fw != null) {
PrintWriter pw = new PrintWriter(fw);
throwable.printStackTrace(pw);
pw.flush();
}
}
/**
* @return A thread pool for executing shop creation tasks
*/
public ExecutorService getShopCreationThreadPool() {
return shopCreationThreadPool;
}
public HologramFormat getHologramFormat() {
return hologramFormat;
}
public ShopCommand getShopCommand() {
return shopCommand;
}
/**
* @return The {@link ShopUpdater} that schedules hologram and item updates
*/
public ShopUpdater getUpdater() {
return updater;
}
/**
* @return Whether the plugin 'AreaShop' is enabled
*/
public boolean hasAreaShop() {
return Config.enableAreaShopIntegration && areaShop != null && areaShop.isEnabled();
}
/**
* @return Whether the plugin 'GriefPrevention' is enabled
*/
public boolean hasGriefPrevention() {
return Config.enableGriefPreventionIntegration && griefPrevention != null && griefPrevention.isEnabled();
}
/**
* @return An instance of {@link GriefPrevention} or {@code null} if GriefPrevention is not enabled
*/
public GriefPrevention getGriefPrevention() {
return griefPrevention;
}
/**
* @return Whether the plugin 'IslandWorld' is enabled
*/
public boolean hasIslandWorld() {
return Config.enableIslandWorldIntegration && islandWorld != null && islandWorld.isEnabled();
}
/**
* @return Whether the plugin 'ASkyBlock' is enabled
*/
public boolean hasASkyBlock() {
return Config.enableASkyblockIntegration && aSkyBlock != null && aSkyBlock.isEnabled();
}
/**
* @return Whether the plugin 'uSkyBlock' is enabled
*/
public boolean hasUSkyBlock() {
return Config.enableUSkyblockIntegration && uSkyBlock != null && uSkyBlock.isEnabled();
}
/**
* @return An instance of {@link uSkyBlockAPI} or {@code null} if uSkyBlock is not enabled
*/
public uSkyBlockAPI getUSkyBlock() {
return uSkyBlock;
}
/**
* @return Whether the plugin 'PlotSquared' is enabled
*/
public boolean hasPlotSquared() {
if (!Config.enablePlotsquaredIntegration) {
return false;
}
if (Utils.getMajorVersion() < 13) {
// Supported PlotSquared versions don't support versions below 1.13
return false;
}
Plugin p = getServer().getPluginManager().getPlugin("PlotSquared");
return p != null && p.isEnabled();
}
/**
* @return Whether the plugin 'AuthMe' is enabled
*/
public boolean hasAuthMe() {
return Config.enableAuthMeIntegration && authMe != null && authMe.isEnabled();
}
/**
* @return Whether the plugin 'Towny' is enabled
*/
public boolean hasTowny() {
return Config.enableTownyIntegration && towny != null && towny.isEnabled();
}
/**
* @return Whether the plugin 'WorldGuard' is enabled
*/
public boolean hasWorldGuard() {
return Config.enableWorldGuardIntegration && worldGuard != null && worldGuard.isEnabled();
}
/**
* @return Whether the plugin 'WorldGuard' is enabled
*/
public boolean hasBentoBox() {
return Config.enableBentoBoxIntegration && bentoBox != null && bentoBox.isEnabled();
}
/**
* @return ShopChest's {@link ShopUtils} containing some important methods
*/
public ShopUtils getShopUtils() {
return shopUtils;
}
/**
* @return Registered Economy of Vault
*/
public Economy getEconomy() {
return econ;
}
/**
* @return ShopChest's shop database
*/
public Database getShopDatabase() {
return database;
}
/**
* @return Whether an update is needed (will return false if not checked)
*/
public boolean isUpdateNeeded() {
return isUpdateNeeded;
}
/**
* Set whether an update is needed
* @param isUpdateNeeded Whether an update should be needed
*/
public void setUpdateNeeded(boolean isUpdateNeeded) {
this.isUpdateNeeded = isUpdateNeeded;
}
/**
* @return The latest version of ShopChest (will return null if not checked or if no update is available)
*/
public String getLatestVersion() {
return latestVersion;
}
/**
* Set the latest version
* @param latestVersion Version to set as latest version
*/
public void setLatestVersion(String latestVersion) {
this.latestVersion = latestVersion;
}
/**
* @return The download link of the latest version (will return null if not checked or if no update is available)
*/
public String getDownloadLink() {
return downloadLink;
}
/**
* Set the download Link of the latest version (will return null if not checked or if no update is available)
* @param downloadLink Link to set as Download Link
*/
public void setDownloadLink(String downloadLink) {
this.downloadLink = downloadLink;
}
/**
* @return The {@link Config} of ShopChest
*/
public Config getShopChestConfig() {
return config;
}
}

View File

@ -1,354 +0,0 @@
package de.epiceric.shopchest.command;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.utils.ClickType.SelectClickType;
import de.epiceric.shopchest.utils.Permissions;
public class ShopCommand {
private static boolean commandCreated = false;
private final ShopChest plugin;
private final String name;
private final String fallbackPrefix;
private final PluginCommand pluginCommand;
private final ShopCommandExecutor executor;
private final List<ShopSubCommand> subCommands = new ArrayList<>();
public ShopCommand(final ShopChest plugin) {
if (commandCreated) {
IllegalStateException e = new IllegalStateException("Command has already been registered");
plugin.debug(e);
throw e;
}
this.plugin = plugin;
this.name = Config.mainCommandName.toLowerCase(Locale.ENGLISH).trim();
this.fallbackPrefix = plugin.getName().toLowerCase(Locale.ENGLISH).trim();
this.pluginCommand = createPluginCommand();
this.executor = new ShopCommandExecutor(plugin);
ShopTabCompleter tabCompleter = new ShopTabCompleter(plugin);
final Replacement cmdReplacement = new Replacement(Placeholder.COMMAND, name);
addSubCommand(new ShopSubCommand("create", true, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
boolean receiveCreateMessage = sender.hasPermission(Permissions.CREATE);
if (!receiveCreateMessage) {
for (PermissionAttachmentInfo permInfo : sender.getEffectivePermissions()) {
String perm = permInfo.getPermission();
if (perm.startsWith(Permissions.CREATE) && sender.hasPermission(perm)) {
receiveCreateMessage = true;
break;
}
}
}
if (sender.hasPermission(Permissions.CREATE_ADMIN)) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_CREATE_ADMIN, cmdReplacement);
} else if (receiveCreateMessage) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_CREATE, cmdReplacement);
}
return "";
}
});
addSubCommand(new ShopSubCommand("remove", true, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_REMOVE, cmdReplacement);
}
});
addSubCommand(new ShopSubCommand("info", true, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_INFO, cmdReplacement);
}
});
addSubCommand(new ShopSubCommand("limits", true, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_LIMITS, cmdReplacement);
}
});
addSubCommand(new ShopSubCommand("open", true, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_OPEN, cmdReplacement);
}
});
addSubCommand(new ShopSubCommand("removeall", false, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
if (sender.hasPermission(Permissions.REMOVE_OTHER)) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_REMOVEALL, cmdReplacement);
} else {
return "";
}
}
});
addSubCommand(new ShopSubCommand("reload", false, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
if (sender.hasPermission(Permissions.RELOAD)) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_RELOAD, cmdReplacement);
} else {
return "";
}
}
});
addSubCommand(new ShopSubCommand("update", false, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
if (sender.hasPermission(Permissions.UPDATE)) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_UPDATE, cmdReplacement);
} else {
return "";
}
}
});
addSubCommand(new ShopSubCommand("config", false, executor, tabCompleter) {
@Override
public String getHelpMessage(CommandSender sender) {
if (sender.hasPermission(Permissions.CONFIG)) {
return LanguageUtils.getMessage(Message.COMMAND_DESC_CONFIG, cmdReplacement);
} else {
return "";
}
}
});
register();
commandCreated = true;
}
public PluginCommand getCommand() {
return pluginCommand;
}
/**
* Call the second part of the create method after the player
* has selected an item from the creative inventory.
*/
public void createShopAfterSelected(Player player, SelectClickType clickType) {
executor.create2(player, clickType);
}
private PluginCommand createPluginCommand() {
plugin.debug("Creating plugin command");
try {
Constructor<PluginCommand> c = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
c.setAccessible(true);
PluginCommand cmd = c.newInstance(name, plugin);
cmd.setDescription("Manage players' shops or this plugin.");
cmd.setUsage("/" + name);
cmd.setExecutor(new ShopBaseCommandExecutor());
cmd.setTabCompleter(new ShopBaseTabCompleter());
return cmd;
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
plugin.getLogger().severe("Failed to create command");
plugin.debug("Failed to create plugin command");
plugin.debug(e);
}
return null;
}
public void addSubCommand(ShopSubCommand subCommand) {
plugin.debug("Adding sub command \"" + subCommand.getName() + "\"");
this.subCommands.add(subCommand);
}
public List<ShopSubCommand> getSubCommands() {
return new ArrayList<>(subCommands);
}
private void register() {
if (pluginCommand == null) return;
plugin.debug("Registering command " + name);
try {
Field fCommandMap = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
fCommandMap.setAccessible(true);
Object commandMapObject = fCommandMap.get(Bukkit.getPluginManager());
if (commandMapObject instanceof CommandMap) {
CommandMap commandMap = (CommandMap) commandMapObject;
commandMap.register(fallbackPrefix, pluginCommand);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
plugin.getLogger().severe("Failed to register command");
plugin.debug("Failed to register plugin command");
plugin.debug(e);
}
}
public void unregister() {
if (pluginCommand == null) return;
plugin.debug("Unregistering command " + name);
try {
Field fCommandMap = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
fCommandMap.setAccessible(true);
Object commandMapObject = fCommandMap.get(Bukkit.getPluginManager());
if (commandMapObject instanceof CommandMap) {
CommandMap commandMap = (CommandMap) commandMapObject;
pluginCommand.unregister(commandMap);
Field fKnownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands");
fKnownCommands.setAccessible(true);
Object knownCommandsObject = fKnownCommands.get(commandMap);
if (knownCommandsObject instanceof Map) {
Map<?, ?> knownCommands = (Map<?, ?>) knownCommandsObject;
knownCommands.remove(fallbackPrefix + ":" + name);
if (pluginCommand.equals(knownCommands.get(name))) {
knownCommands.remove(name);
}
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
plugin.getLogger().severe("Failed to unregister command");
plugin.debug("Failed to unregister plugin command");
plugin.debug(e);
}
}
/**
* Sends the basic help message
*
* @param sender {@link CommandSender} who will receive the message
*/
private void sendBasicHelpMessage(CommandSender sender) {
plugin.debug("Sending basic help message to " + sender.getName());
sender.sendMessage(" ");
String header = LanguageUtils.getMessage(Message.COMMAND_DESC_HEADER,
new Replacement(Placeholder.COMMAND, Config.mainCommandName));
if (!header.trim().isEmpty()) sender.sendMessage(header);
for (ShopSubCommand subCommand : subCommands) {
String msg = subCommand.getHelpMessage(sender);
if (msg == null || msg.isEmpty()) {
continue;
}
sender.sendMessage(msg);
}
String footer = LanguageUtils.getMessage(Message.COMMAND_DESC_FOOTER,
new Replacement(Placeholder.COMMAND,Config.mainCommandName));
if (!footer.trim().isEmpty()) sender.sendMessage(footer);
sender.sendMessage(" ");
}
private class ShopBaseCommandExecutor implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
for (ShopSubCommand subCommand : subCommands) {
if (subCommand.getName().equalsIgnoreCase(args[0])) {
if (!(sender instanceof Player) && subCommand.isPlayerCommand()) {
sender.sendMessage(ChatColor.RED + "Only players can use this command.");
return true;
}
if (!subCommand.execute(sender, command, label, args)) {
sendBasicHelpMessage(sender);
}
return true;
}
}
sendBasicHelpMessage(sender);
} else {
sendBasicHelpMessage(sender);
}
return true;
}
}
private class ShopBaseTabCompleter implements TabCompleter {
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
List<String> subCommandNames = new ArrayList<>();
List<String> tabCompletions = new ArrayList<>();
for (ShopSubCommand subCommand : subCommands) {
subCommandNames.add(subCommand.getName());
}
if (args.length == 1) {
if (!args[0].isEmpty()) {
for (String s : subCommandNames) {
if (s.startsWith(args[0])) {
tabCompletions.add(s);
}
}
return tabCompletions;
} else {
return subCommandNames;
}
} else if (args.length > 1) {
for (ShopSubCommand subCmd : subCommands) {
if (subCmd.getName().equalsIgnoreCase(args[0])) {
return subCmd.getTabCompletions(sender, command, label, args);
}
}
}
return new ArrayList<>();
}
}
}

View File

@ -1,576 +0,0 @@
package de.epiceric.shopchest.command;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.GameMode;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.event.ShopPreCreateEvent;
import de.epiceric.shopchest.event.ShopPreInfoEvent;
import de.epiceric.shopchest.event.ShopPreOpenEvent;
import de.epiceric.shopchest.event.ShopPreRemoveEvent;
import de.epiceric.shopchest.event.ShopReloadEvent;
import de.epiceric.shopchest.event.ShopRemoveAllEvent;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.shop.ShopProduct;
import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.ClickType;
import de.epiceric.shopchest.utils.ClickType.CreateClickType;
import de.epiceric.shopchest.utils.ClickType.SelectClickType;
import de.epiceric.shopchest.utils.ItemUtils;
import de.epiceric.shopchest.utils.Permissions;
import de.epiceric.shopchest.utils.ShopUtils;
import de.epiceric.shopchest.utils.UpdateChecker;
import de.epiceric.shopchest.utils.Utils;
class ShopCommandExecutor implements CommandExecutor {
private ShopChest plugin;
private ShopUtils shopUtils;
ShopCommandExecutor(ShopChest plugin) {
this.plugin = plugin;
this.shopUtils = plugin.getShopUtils();
}
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
List<ShopSubCommand> subCommands = plugin.getShopCommand().getSubCommands();
if (args.length > 0) {
String _subCommand = args[0];
ShopSubCommand subCommand = null;
for (ShopSubCommand shopSubCommand : subCommands) {
if (shopSubCommand.getName().equalsIgnoreCase(_subCommand)) {
subCommand = shopSubCommand;
break;
}
}
if (subCommand == null) {
return false;
}
if (subCommand.getName().equalsIgnoreCase("reload")) {
if (sender.hasPermission(Permissions.RELOAD)) {
reload(sender);
} else {
sender.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_RELOAD));
}
} else if (subCommand.getName().equalsIgnoreCase("update")) {
if (sender.hasPermission(Permissions.UPDATE)) {
checkUpdates(sender);
} else {
sender.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_UPDATE));
}
} else if (subCommand.getName().equalsIgnoreCase("config")) {
if (sender.hasPermission(Permissions.CONFIG)) {
return args.length >= 4 && changeConfig(sender, args);
} else {
sender.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_CONFIG));
}
} else if (subCommand.getName().equalsIgnoreCase("removeall")) {
if (sender.hasPermission(Permissions.REMOVE_OTHER)) {
if (args.length >= 2) {
removeAll(sender, args);
} else {
return false;
}
} else {
sender.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_REMOVE_OTHERS));
}
} else {
if (sender instanceof Player) {
Player p = (Player) sender;
if (subCommand.getName().equalsIgnoreCase("create")) {
if (args.length == 4) {
create(args, Shop.ShopType.NORMAL, p);
} else if (args.length == 5) {
if (args[4].equalsIgnoreCase("normal")) {
create(args, Shop.ShopType.NORMAL, p);
} else if (args[4].equalsIgnoreCase("admin")) {
if (p.hasPermission(Permissions.CREATE_ADMIN)) {
create(args, Shop.ShopType.ADMIN, p);
} else {
p.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_CREATE_ADMIN));
}
} else {
return false;
}
} else {
return false;
}
} else if (subCommand.getName().equalsIgnoreCase("remove")) {
remove(p);
} else if (subCommand.getName().equalsIgnoreCase("info")) {
info(p);
} else if (subCommand.getName().equalsIgnoreCase("limits")) {
plugin.debug(p.getName() + " is viewing his shop limits: " + shopUtils.getShopAmount(p) + "/" + shopUtils.getShopLimit(p));
int limit = shopUtils.getShopLimit(p);
p.sendMessage(LanguageUtils.getMessage(Message.OCCUPIED_SHOP_SLOTS,
new Replacement(Placeholder.LIMIT, (limit < 0 ? "" : String.valueOf(limit))),
new Replacement(Placeholder.AMOUNT, String.valueOf(shopUtils.getShopAmount(p)))));
} else if (subCommand.getName().equalsIgnoreCase("open")) {
open(p);
} else {
return false;
}
}
}
return true;
}
return false;
}
/**
* A given player checks for updates
* @param sender The command executor
*/
private void checkUpdates(CommandSender sender) {
plugin.debug(sender.getName() + " is checking for updates");
sender.sendMessage(LanguageUtils.getMessage(Message.UPDATE_CHECKING));
UpdateChecker uc = new UpdateChecker(ShopChest.getInstance());
UpdateChecker.UpdateCheckerResult result = uc.check();
if (result == UpdateChecker.UpdateCheckerResult.TRUE) {
plugin.setLatestVersion(uc.getVersion());
plugin.setDownloadLink(uc.getLink());
plugin.setUpdateNeeded(true);
if (sender instanceof Player) {
Utils.sendUpdateMessage(plugin, (Player) sender);
} else {
sender.sendMessage(LanguageUtils.getMessage(Message.UPDATE_AVAILABLE, new Replacement(Placeholder.VERSION, uc.getVersion())));
}
} else if (result == UpdateChecker.UpdateCheckerResult.FALSE) {
plugin.setLatestVersion("");
plugin.setDownloadLink("");
plugin.setUpdateNeeded(false);
sender.sendMessage(LanguageUtils.getMessage(Message.UPDATE_NO_UPDATE));
} else {
plugin.setLatestVersion("");
plugin.setDownloadLink("");
plugin.setUpdateNeeded(false);
sender.sendMessage(LanguageUtils.getMessage(Message.UPDATE_ERROR));
}
}
/**
* A given player reloads the shops
* @param sender The command executor
*/
private void reload(final CommandSender sender) {
plugin.debug(sender.getName() + " is reloading the shops");
ShopReloadEvent event = new ShopReloadEvent(sender);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()){
plugin.debug("Reload event cancelled");
return;
}
// Reload configurations
plugin.getShopChestConfig().reload(false, true, true);
plugin.getHologramFormat().reload();
plugin.getUpdater().restart();
// Remove all shops
for (Shop shop : shopUtils.getShops()) {
shopUtils.removeShop(shop, false);
}
Chunk[] loadedChunks = Bukkit.getWorlds().stream().map(World::getLoadedChunks)
.flatMap(Stream::of).toArray(Chunk[]::new);
// Reconnect to the database and re-load shops in loaded chunks
plugin.getShopDatabase().connect(new Callback<Integer>(plugin) {
@Override
public void onResult(Integer result) {
shopUtils.loadShops(loadedChunks, new Callback<Integer>(plugin) {
@Override
public void onResult(Integer result) {
sender.sendMessage(LanguageUtils.getMessage(Message.RELOADED_SHOPS,
new Replacement(Placeholder.AMOUNT, String.valueOf(result))));
plugin.debug(sender.getName() + " has reloaded " + result + " shops");
}
@Override
public void onError(Throwable throwable) {
sender.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED,
new Replacement(Placeholder.ERROR, "Failed to load shops from database")));
plugin.getLogger().severe("Failed to load shops");
if (throwable != null) plugin.getLogger().severe(throwable.getMessage());
}
});
}
@Override
public void onError(Throwable throwable) {
// Database connection probably failed => disable plugin to prevent more errors
sender.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED,
new Replacement(Placeholder.ERROR, "No database access: Disabling ShopChest")));
plugin.getLogger().severe("No database access: Disabling ShopChest");
if (throwable != null) plugin.getLogger().severe(throwable.getMessage());
plugin.getServer().getPluginManager().disablePlugin(plugin);
}
});
}
/**
* A given player creates a shop
* @param args Arguments of the entered command
* @param shopType The {@link Shop.ShopType}, the shop will have
* @param p The command executor
*/
private void create(String[] args, Shop.ShopType shopType, final Player p) {
plugin.debug(p.getName() + " wants to create a shop");
int amount;
double buyPrice, sellPrice;
// Check if amount and prices are valid
try {
amount = Integer.parseInt(args[1]);
buyPrice = Double.parseDouble(args[2]);
sellPrice = Double.parseDouble(args[3]);
} catch (NumberFormatException e) {
p.sendMessage(LanguageUtils.getMessage(Message.AMOUNT_PRICE_NOT_NUMBER));
plugin.debug(p.getName() + " has entered an invalid amount and/or prices");
return;
}
if (!Utils.hasPermissionToCreateShop(p, Utils.getPreferredItemInHand(p), buyPrice > 0, sellPrice > 0)) {
p.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_CREATE));
plugin.debug(p.getName() + " is not permitted to create the shop");
return;
}
// Check for limits
int limit = shopUtils.getShopLimit(p);
if (limit != -1) {
if (shopUtils.getShopAmount(p) >= limit) {
if (shopType != Shop.ShopType.ADMIN) {
p.sendMessage(LanguageUtils.getMessage(Message.SHOP_LIMIT_REACHED, new Replacement(Placeholder.LIMIT, String.valueOf(limit))));
plugin.debug(p.getName() + " has reached the limit");
return;
}
}
}
if (amount <= 0) {
p.sendMessage(LanguageUtils.getMessage(Message.AMOUNT_IS_ZERO));
plugin.debug(p.getName() + " has entered an invalid amount");
return;
}
if (!Config.allowDecimalsInPrice && (buyPrice != (int) buyPrice || sellPrice != (int) sellPrice)) {
p.sendMessage(LanguageUtils.getMessage(Message.PRICES_CONTAIN_DECIMALS));
plugin.debug(p.getName() + " has entered an invalid price");
return;
}
boolean buyEnabled = buyPrice > 0;
boolean sellEnabled = sellPrice > 0;
if (!buyEnabled && !sellEnabled) {
p.sendMessage(LanguageUtils.getMessage(Message.BUY_SELL_DISABLED));
plugin.debug(p.getName() + " has disabled buying and selling");
return;
}
ItemStack inHand = Utils.getPreferredItemInHand(p);
// Check if item in hand
if (inHand == null) {
plugin.debug(p.getName() + " does not have an item in his hand");
if (!Config.creativeSelectItem) {
p.sendMessage(LanguageUtils.getMessage(Message.NO_ITEM_IN_HAND));
return;
}
if (!(ClickType.getPlayerClickType(p) instanceof SelectClickType)) {
// Don't set previous game mode to creative if player already has select click type
ClickType.setPlayerClickType(p, new SelectClickType(p.getGameMode(), amount, buyPrice, sellPrice, shopType));
p.setGameMode(GameMode.CREATIVE);
}
p.sendMessage(LanguageUtils.getMessage(Message.SELECT_ITEM));
} else {
SelectClickType ct = new SelectClickType(null, amount, buyPrice, sellPrice, shopType);
ct.setItem(inHand);
create2(p, ct);
}
}
/**
* <b>SHALL ONLY BE CALLED VIA {@link ShopCommand#createShopAfterSelected()}</b>
*/
protected void create2(Player p, SelectClickType selectClickType) {
ItemStack itemStack = selectClickType.getItem();
int amount = selectClickType.getAmount();
double buyPrice = selectClickType.getBuyPrice();
double sellPrice = selectClickType.getSellPrice();
boolean buyEnabled = buyPrice > 0;
boolean sellEnabled = sellPrice > 0;
ShopType shopType = selectClickType.getShopType();
// Check if item on blacklist
for (String item :Config.blacklist) {
ItemStack is = ItemUtils.getItemStack(item);
if (is == null) {
plugin.getLogger().warning("Invalid item found in blacklist: " + item);
plugin.debug("Invalid item in blacklist: " + item);
continue;
}
if (is.getType().equals(itemStack.getType()) && is.getDurability() == itemStack.getDurability()) {
p.sendMessage(LanguageUtils.getMessage(Message.CANNOT_SELL_ITEM));
plugin.debug(p.getName() + "'s item is on the blacklist");
return;
}
}
// Check if prices lower than minimum price
for (String key :Config.minimumPrices) {
ItemStack is = ItemUtils.getItemStack(key);
double minPrice = plugin.getConfig().getDouble("minimum-prices." + key);
if (is == null) {
plugin.getLogger().warning("Invalid item found in minimum-prices: " + key);
plugin.debug("Invalid item in minimum-prices: " + key);
continue;
}
if (is.getType().equals(itemStack.getType()) && is.getDurability() == itemStack.getDurability()) {
if (buyEnabled) {
if ((buyPrice < amount * minPrice) && (buyPrice > 0)) {
p.sendMessage(LanguageUtils.getMessage(Message.BUY_PRICE_TOO_LOW, new Replacement(Placeholder.MIN_PRICE, String.valueOf(amount * minPrice))));
plugin.debug(p.getName() + "'s buy price is lower than the minimum");
return;
}
}
if (sellEnabled) {
if ((sellPrice < amount * minPrice) && (sellPrice > 0)) {
p.sendMessage(LanguageUtils.getMessage(Message.SELL_PRICE_TOO_LOW, new Replacement(Placeholder.MIN_PRICE, String.valueOf(amount * minPrice))));
plugin.debug(p.getName() + "'s sell price is lower than the minimum");
return;
}
}
}
}
// Check if prices higher than maximum price
for (String key :Config.maximumPrices) {
ItemStack is = ItemUtils.getItemStack(key);
double maxPrice = plugin.getConfig().getDouble("maximum-prices." + key);
if (is == null) {
plugin.getLogger().warning("Invalid item found in maximum-prices: " + key);
plugin.debug("Invalid item in maximum-prices: " + key);
continue;
}
if (is.getType().equals(itemStack.getType()) && is.getDurability() == itemStack.getDurability()) {
if (buyEnabled) {
if ((buyPrice > amount * maxPrice) && (buyPrice > 0)) {
p.sendMessage(LanguageUtils.getMessage(Message.BUY_PRICE_TOO_HIGH, new Replacement(Placeholder.MAX_PRICE, String.valueOf(amount * maxPrice))));
plugin.debug(p.getName() + "'s buy price is higher than the maximum");
return;
}
}
if (sellEnabled) {
if ((sellPrice > amount * maxPrice) && (sellPrice > 0)) {
p.sendMessage(LanguageUtils.getMessage(Message.SELL_PRICE_TOO_HIGH, new Replacement(Placeholder.MAX_PRICE, String.valueOf(amount * maxPrice))));
plugin.debug(p.getName() + "'s sell price is higher than the maximum");
return;
}
}
}
}
if (sellEnabled && buyEnabled) {
if (Config.buyGreaterOrEqualSell) {
if (buyPrice < sellPrice) {
p.sendMessage(LanguageUtils.getMessage(Message.BUY_PRICE_TOO_LOW, new Replacement(Placeholder.MIN_PRICE, String.valueOf(sellPrice))));
plugin.debug(p.getName() + "'s buy price is lower than the sell price");
return;
}
}
}
if (Enchantment.DURABILITY.canEnchantItem(itemStack)) {
if (itemStack.getDurability() > 0 && !Config.allowBrokenItems) {
p.sendMessage(LanguageUtils.getMessage(Message.CANNOT_SELL_BROKEN_ITEM));
plugin.debug(p.getName() + "'s item is broken");
return;
}
}
double creationPrice = (shopType == Shop.ShopType.NORMAL) ?Config.shopCreationPriceNormal :Config.shopCreationPriceAdmin;
if (creationPrice > 0) {
if (plugin.getEconomy().getBalance(p, p.getWorld().getName()) < creationPrice) {
p.sendMessage(LanguageUtils.getMessage(Message.SHOP_CREATE_NOT_ENOUGH_MONEY, new Replacement(Placeholder.CREATION_PRICE, String.valueOf(creationPrice))));
plugin.debug(p.getName() + " can not pay the creation price");
return;
}
}
ShopProduct product = new ShopProduct(itemStack, amount);
ShopPreCreateEvent event = new ShopPreCreateEvent(p, new Shop(plugin, p, product, null, buyPrice, sellPrice, shopType));
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
ClickType.setPlayerClickType(p, new CreateClickType(product, buyPrice, sellPrice, shopType));
plugin.debug(p.getName() + " can now click a chest");
p.sendMessage(LanguageUtils.getMessage(Message.CLICK_CHEST_CREATE));
} else {
plugin.debug("Shop pre create event cancelled");
}
}
/**
* A given player removes a shop
* @param p The command executor
*/
private void remove(final Player p) {
plugin.debug(p.getName() + " wants to remove a shop");
ShopPreRemoveEvent event = new ShopPreRemoveEvent(p);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
plugin.debug("Shop pre remove event cancelled");
return;
}
plugin.debug(p.getName() + " can now click a chest");
p.sendMessage(LanguageUtils.getMessage(Message.CLICK_CHEST_REMOVE));
ClickType.setPlayerClickType(p, new ClickType(ClickType.EnumClickType.REMOVE));
}
/**
* A given player retrieves information about a shop
* @param p The command executor
*/
private void info(final Player p) {
plugin.debug(p.getName() + " wants to retrieve information");
ShopPreInfoEvent event = new ShopPreInfoEvent(p);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
plugin.debug("Shop pre info event cancelled");
return;
}
plugin.debug(p.getName() + " can now click a chest");
p.sendMessage(LanguageUtils.getMessage(Message.CLICK_CHEST_INFO));
ClickType.setPlayerClickType(p, new ClickType(ClickType.EnumClickType.INFO));
}
/**
* A given player opens a shop
* @param p The command executor
*/
private void open(final Player p) {
plugin.debug(p.getName() + " wants to open a shop");
ShopPreOpenEvent event = new ShopPreOpenEvent(p);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
plugin.debug("Shop pre open event cancelled");
return;
}
plugin.debug(p.getName() + " can now click a chest");
p.sendMessage(LanguageUtils.getMessage(Message.CLICK_CHEST_OPEN));
ClickType.setPlayerClickType(p, new ClickType(ClickType.EnumClickType.OPEN));
}
private boolean changeConfig(CommandSender sender, String[] args) {
plugin.debug(sender.getName() + " is changing the configuration");
String property = args[2];
String value = args[3];
if (args[1].equalsIgnoreCase("set")) {
plugin.getShopChestConfig().set(property, value);
sender.sendMessage(LanguageUtils.getMessage(Message.CHANGED_CONFIG_SET, new Replacement(Placeholder.PROPERTY, property), new Replacement(Placeholder.VALUE, value)));
} else if (args[1].equalsIgnoreCase("add")) {
plugin.getShopChestConfig().add(property, value);
sender.sendMessage(LanguageUtils.getMessage(Message.CHANGED_CONFIG_ADDED, new Replacement(Placeholder.PROPERTY, property), new Replacement(Placeholder.VALUE, value)));
} else if (args[1].equalsIgnoreCase("remove")) {
plugin.getShopChestConfig().remove(property, value);
sender.sendMessage(LanguageUtils.getMessage(Message.CHANGED_CONFIG_REMOVED, new Replacement(Placeholder.PROPERTY, property), new Replacement(Placeholder.VALUE, value)));
} else {
return false;
}
return true;
}
private void removeAll(CommandSender sender, String[] args) {
OfflinePlayer vendor = Bukkit.getOfflinePlayer(args[1]);
plugin.debug(sender.getName() + " is removing all shops of " + vendor.getName());
plugin.getShopUtils().getShops(vendor, new Callback<Collection<Shop>>(plugin) {
@Override
public void onResult(Collection<Shop> result) {
List<Shop> shops = new ArrayList<>(result);
ShopRemoveAllEvent event = new ShopRemoveAllEvent(sender, vendor, shops);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()){
plugin.debug("Remove all event cancelled");
return;
}
for (Shop shop : shops) {
shopUtils.removeShop(shop, true);
}
sender.sendMessage(LanguageUtils.getMessage(Message.ALL_SHOPS_REMOVED,
new Replacement(Placeholder.AMOUNT, String.valueOf(shops.size())),
new Replacement(Placeholder.VENDOR, vendor.getName())));
}
@Override
public void onError(Throwable throwable) {
sender.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED,
new Replacement(Placeholder.ERROR, "Failed to get player's shops")));
}
});
}
}

View File

@ -1,68 +0,0 @@
package de.epiceric.shopchest.command;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
public abstract class ShopSubCommand {
private String name;
private boolean playerCommand;
private CommandExecutor executor;
private TabCompleter tabCompleter;
public ShopSubCommand(String name, boolean playerCommand, CommandExecutor executor, TabCompleter tabCompleter) {
this.name = name;
this.playerCommand = playerCommand;
this.executor = executor;
this.tabCompleter = tabCompleter;
}
public String getName() {
return name;
}
/**
* @return Whether the command can only be used by players, not by the console
*/
public boolean isPlayerCommand() {
return playerCommand;
}
/**
* Execute the sub command
* @param sender Sender of the command
* @param command Command which was executed
* @param args Arguments of the command ({@code args[0]} is the sub command's name)
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return Whether the sender should be sent the help message
*/
public boolean execute(CommandSender sender, Command command, String label, String[] args) {
return executor.onCommand(sender, command, label, args);
}
/**
* @param sender Sender of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Arguments of the command ({@code args[0]} is the sub command's name)
* @return A list of tab completions for the sub command (may be an empty list)
*/
public List<String> getTabCompletions(CommandSender sender, Command command, String label, String[] args) {
if (tabCompleter == null) {
return new ArrayList<>();
}
return tabCompleter.onTabComplete(sender, command, label, args);
}
/**
* @param sender Sender to receive the help message
* @return The help message for the command.
*/
public abstract String getHelpMessage(CommandSender sender);
}

View File

@ -1,126 +0,0 @@
package de.epiceric.shopchest.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
class ShopTabCompleter implements TabCompleter {
private ShopChest plugin;
ShopTabCompleter(ShopChest plugin) {
this.plugin = plugin;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
if (command.getName().equalsIgnoreCase(Config.mainCommandName)) {
List<String> createSubCommands = Arrays.asList("admin");
List<String> configSubCommands = Arrays.asList("add", "remove", "set");
List<String> areaShopRemoveEvents = Arrays.asList("DELETE", "RESELL", "SELL", "UNRENT");
List<String> townyShopPlots = Arrays.asList("ARENA", "COMMERCIAL", "EMBASSY", "FARM", "INN", "JAIL", "RESIDENTIAL", "SPLEEF", "WILDS");
Set<String> configValues = plugin.getConfig().getKeys(true);
List<String> playerNames = Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
ArrayList<String> returnCompletions = new ArrayList<>();
if (args.length == 2) {
if (args[0].equals("config")) {
if (!args[1].equals("")) {
for (String s : configSubCommands) {
if (s.startsWith(args[1])) {
returnCompletions.add(s);
}
}
return returnCompletions;
} else {
return configSubCommands;
}
} else if (args[0].equals("removeall")) {
if (!args[1].equals("")) {
for (String name : playerNames) {
if (name.startsWith(args[1])) {
returnCompletions.add(name);
}
}
return returnCompletions;
} else {
return playerNames;
}
}
} else if (args.length == 3) {
if (args[0].equals("config")) {
if (!args[2].equals("")) {
for (String s : configValues) {
if (s.startsWith(args[2])) {
returnCompletions.add(s);
}
}
return returnCompletions;
} else {
return new ArrayList<>(configValues);
}
}
} else if (args.length == 4) {
if (args[0].equals("config")) {
if (args[2].equals("towny-shop-plots")) {
if (!args[3].equals("")) {
for (String s : townyShopPlots) {
if (s.startsWith(args[3])) {
returnCompletions.add(s);
}
}
return returnCompletions;
} else {
return townyShopPlots;
}
} else if (args[2].equals("areashop-remove-shops")) {
if (!args[3].equals("")) {
for (String s : areaShopRemoveEvents) {
if (s.startsWith(args[3])) {
returnCompletions.add(s);
}
}
return returnCompletions;
} else {
return areaShopRemoveEvents;
}
}
}
} else if (args.length == 5) {
if (args[0].equals("create")) {
if (!args[4].equals("")) {
for (String s : createSubCommands) {
if (s.startsWith(args[4])) {
returnCompletions.add(s);
}
}
return returnCompletions;
} else {
return createSubCommands;
}
}
}
}
return new ArrayList<>();
}
}

View File

@ -1,614 +0,0 @@
package de.epiceric.shopchest.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.inventory.ItemStack;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.sql.Database;
import de.epiceric.shopchest.utils.ItemUtils;
import de.epiceric.shopchest.utils.Utils;
public class Config {
/**
* The item with which a player can click a shop to retrieve information
**/
public static ItemStack shopInfoItem;
/**
* The default value for the custom WorldGuard flag 'create-shop'
**/
public static boolean wgAllowCreateShopDefault;
/**
* The default value for the custom WorldGuard flag 'use-admin-shop'
**/
public static boolean wgAllowUseAdminShopDefault;
/**
* The default value for the custom WorldGuard flag 'use-shop'
**/
public static boolean wgAllowUseShopDefault;
/**
* The types of town plots residents are allowed to create shops in
**/
public static List<String> townyShopPlotsResidents;
/**
* The types of town plots the mayor is allowed to create shops in
**/
public static List<String> townyShopPlotsMayor;
/**
* The types of town plots the king is allowed to create shops in
**/
public static List<String> townyShopPlotsKing;
/**
* The events of AreaShop when shops in that region should be removed
**/
public static List<String> areashopRemoveShopEvents;
/**
* The hostname used in ShopChest's MySQL database
**/
public static String databaseMySqlHost;
/**
* The port used for ShopChest's MySQL database
**/
public static int databaseMySqlPort;
/**
* The database used for ShopChest's MySQL database
**/
public static String databaseMySqlDatabase;
/**
* The username used in ShopChest's MySQL database
**/
public static String databaseMySqlUsername;
/**
* The password used in ShopChest's MySQL database
**/
public static String databaseMySqlPassword;
/**
* The prefix to be used for database tables
*/
public static String databaseTablePrefix;
/**
* The database type used for ShopChest
**/
public static Database.DatabaseType databaseType;
/**
* The interval in seconds, a ping is sent to the MySQL server
**/
public static int databaseMySqlPingInterval;
/**
* <p>The minimum prices for certain items</p>
* This returns a key set, which contains e.g "STONE", "STONE:1", of the <i>minimum-prices</i> section in ShopChest's config.
* To actually retrieve the minimum price for an item, you have to get the double {@code minimum-prices.<key>}.
**/
public static Set<String> minimumPrices;
/**
* <p>The maximum prices for certain items</p>
* This returns a key set, which contains e.g "STONE", "STONE:1", of the {@code maximum-prices} section in ShopChest's config.
* To actually retrieve the maximum price for an item, you have to get the double {@code maximum-prices.<key>}.
**/
public static Set<String> maximumPrices;
/**
* <p>List containing items, of which players can't create a shop</p>
* If this list contains an item (e.g "STONE", "STONE:1"), it's in the blacklist.
**/
public static List<String> blacklist;
/**
* Whether prices may contain decimals
**/
public static boolean allowDecimalsInPrice;
/**
* Whether the buy price of a shop must be greater than or equal the sell price
**/
public static boolean buyGreaterOrEqualSell;
/**
* Whether buys and sells must be confirmed
**/
public static boolean confirmShopping;
/**
* Whether the shop creation price should be refunded at removal.
*/
public static boolean refundShopCreation;
/**
* <p>Whether the update checker should run on start and notify players on join.</p>
* The command is not affected by this setting and will continue to check for updates.
**/
public static boolean enableUpdateChecker;
/**
* Whether the debug log file should be created
**/
public static boolean enableDebugLog;
/**
* Whether buys and sells should be logged in the database
**/
public static boolean enableEconomyLog;
/**
* Whether WorldGuard integration should be enabled
**/
public static boolean enableWorldGuardIntegration;
/**
* <p>Sets the time limit for cleaning up the economy log in days</p>
*
* If this equals to {@code 0}, the economy log will not be cleaned.
**/
public static int cleanupEconomyLogDays;
/**
* Whether Towny integration should be enabled
**/
public static boolean enableTownyIntegration;
/**
* Whether AuthMe integration should be enabled
**/
public static boolean enableAuthMeIntegration;
/**
* Whether PlotSquared integration should be enabled
**/
public static boolean enablePlotsquaredIntegration;
/**
* Whether uSkyBlock integration should be enabled
**/
public static boolean enableUSkyblockIntegration;
/**
* Whether ASkyBlock integration should be enabled
**/
public static boolean enableASkyblockIntegration;
/**
* Whether BentoBox integration should be enabled
**/
public static boolean enableBentoBoxIntegration;
/**
* Whether IslandWorld integration should be enabled
**/
public static boolean enableIslandWorldIntegration;
/**
* Whether GriefPrevention integration should be enabled
**/
public static boolean enableGriefPreventionIntegration;
/**
* Whether AreaShop integration should be enabled
**/
public static boolean enableAreaShopIntegration;
/**
* Whether the vendor of the shop should get messages about buys and sells
**/
public static boolean enableVendorMessages;
/**
* Whether the vendor of the shop should get messages on all servers about buys and sells
**/
public static boolean enableVendorBungeeMessages;
/**
* Whether the extension of a potion or tipped arrow (if available) should be appended to the item name.
**/
public static boolean appendPotionLevelToItemName;
/**
* Whether players are allowed to sell/buy broken items
**/
public static boolean allowBrokenItems;
/**
* Whether only the shop a player is pointing at should be shown
**/
public static boolean onlyShowShopsInSight;
/**
* <p>Whether shops should automatically be removed from the database if an error occurred while loading</p>
* (e.g. when no chest is found at a shop's location)
*/
public static boolean removeShopOnError;
/**
* Whether the item amount should be calculated to fit the available money or inventory space
**/
public static boolean autoCalculateItemAmount;
/**
* Whether players should be able to select an item from the creative inventory
*/
public static boolean creativeSelectItem;
/**
* <p>Whether the mouse buttons are inverted</p>
* <b>Default:</b><br>
* Right-Click: Buy<br>
* Left-Click: Sell
**/
public static boolean invertMouseButtons;
/**
* Whether the hologram's location should be fixed at the bottom
**/
public static boolean hologramFixedBottom;
/**
* Amount every hologram should be lifted
**/
public static double hologramLift;
/**
* The maximum distance between a player and a shop to see the hologram
**/
public static double maximalDistance;
/**
* The maximum distance between a player and a shop to see the shop item
**/
public static double maximalItemDistance;
/**
* The price a player has to pay in order to create a normal shop
**/
public static double shopCreationPriceNormal;
/**
* The price a player has to pay in order to create an admin shop
**/
public static double shopCreationPriceAdmin;
/**
* The default shop limit for players whose limit is not set via a permission
**/
public static int defaultLimit;
/**
* The main command of ShopChest <i>(default: shop)</i>
**/
public static String mainCommandName;
/**
* The language file to use (e.g <i>en_US</i>, <i>de_DE</i>)
**/
public static String languageFile;
/**
* The language configuration of the currently selected language file
*/
public static LanguageConfiguration langConfig;
private ShopChest plugin;
public Config(ShopChest plugin) {
this.plugin = plugin;
plugin.saveDefaultConfig();
reload(true, true, true);
}
/**
* <p>Set a configuration value</p>
* <i>Config is automatically reloaded</i>
*
* @param property Property to change
* @param value Value to set
*/
public void set(String property, String value) {
boolean langChange = (property.equalsIgnoreCase("language-file"));
try {
int intValue = Integer.parseInt(value);
plugin.getConfig().set(property, intValue);
plugin.saveConfig();
reload(false, langChange, false);
return;
} catch (NumberFormatException e) { /* Value not an integer */ }
try {
double doubleValue = Double.parseDouble(value);
plugin.getConfig().set(property, doubleValue);
plugin.saveConfig();
reload(false, langChange, false);
return;
} catch (NumberFormatException e) { /* Value not a double */ }
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
boolean boolValue = Boolean.parseBoolean(value);
plugin.getConfig().set(property, boolValue);
} else {
plugin.getConfig().set(property, value);
}
plugin.saveConfig();
reload(false, langChange, false);
}
/**
* Add a value to a list in the config.yml.
* If the list does not exist, a new list with the given value will be created
*
* @param property Location of the list
* @param value Value to add
*/
public void add(String property, String value) {
List list = (plugin.getConfig().getList(property) == null) ? new ArrayList<>() : plugin.getConfig().getList(property);
try {
int intValue = Integer.parseInt(value);
list.add(intValue);
plugin.saveConfig();
reload(false, false, false);
return;
} catch (NumberFormatException e) { /* Value not an integer */ }
try {
double doubleValue = Double.parseDouble(value);
list.add(doubleValue);
plugin.saveConfig();
reload(false, false, false);
return;
} catch (NumberFormatException e) { /* Value not a double */ }
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
boolean boolValue = Boolean.parseBoolean(value);
list.add(boolValue);
} else {
list.add(value);
}
plugin.saveConfig();
reload(false, false, false);
}
public void remove(String property, String value) {
List list = (plugin.getConfig().getList(property) == null) ? new ArrayList<>() : plugin.getConfig().getList(property);
try {
int intValue = Integer.parseInt(value);
list.remove(intValue);
plugin.saveConfig();
reload(false, false, false);
return;
} catch (NumberFormatException e) { /* Value not an integer */ }
try {
double doubleValue = Double.parseDouble(value);
list.remove(doubleValue);
plugin.saveConfig();
reload(false, false, false);
return;
} catch (NumberFormatException e) { /* Value not a double */ }
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
boolean boolValue = Boolean.parseBoolean(value);
list.remove(boolValue);
} else {
list.remove(value);
}
plugin.saveConfig();
reload(false, false, false);
}
/**
* Reload the configuration values from config.yml
* @param firstLoad Whether the config values have not been loaded before
* @param langReload Whether the language configuration should be reloaded
* @param showMessages Whether console (error) messages should be shown
*/
public void reload(boolean firstLoad, boolean langReload, boolean showMessages) {
plugin.reloadConfig();
shopInfoItem = ItemUtils.getItemStack(plugin.getConfig().getString("shop-info-item"));
wgAllowCreateShopDefault = plugin.getConfig().getBoolean("worldguard-default-flag-values.create-shop");
wgAllowUseAdminShopDefault = plugin.getConfig().getBoolean("worldguard-default-flag-values.use-admin-shop");
wgAllowUseShopDefault = plugin.getConfig().getBoolean("worldguard-default-flag-values.use-shop");
townyShopPlotsResidents = plugin.getConfig().getStringList("towny-shop-plots.residents");
townyShopPlotsMayor = plugin.getConfig().getStringList("towny-shop-plots.mayor");
townyShopPlotsKing = plugin.getConfig().getStringList("towny-shop-plots.king");
areashopRemoveShopEvents = plugin.getConfig().getStringList("areashop-remove-shops");
databaseMySqlPingInterval = plugin.getConfig().getInt("database.mysql.ping-interval");
databaseMySqlHost = plugin.getConfig().getString("database.mysql.hostname");
databaseMySqlPort = plugin.getConfig().getInt("database.mysql.port");
databaseMySqlDatabase = plugin.getConfig().getString("database.mysql.database");
databaseMySqlUsername = plugin.getConfig().getString("database.mysql.username");
databaseMySqlPassword = plugin.getConfig().getString("database.mysql.password");
databaseTablePrefix = plugin.getConfig().getString("database.table-prefix");
databaseType = Database.DatabaseType.valueOf(plugin.getConfig().getString("database.type"));
minimumPrices = (plugin.getConfig().getConfigurationSection("minimum-prices") == null) ? new HashSet<String>() : plugin.getConfig().getConfigurationSection("minimum-prices").getKeys(true);
maximumPrices = (plugin.getConfig().getConfigurationSection("maximum-prices") == null) ? new HashSet<String>() : plugin.getConfig().getConfigurationSection("maximum-prices").getKeys(true);
allowDecimalsInPrice = plugin.getConfig().getBoolean("allow-decimals-in-price");
allowBrokenItems = plugin.getConfig().getBoolean("allow-broken-items");
autoCalculateItemAmount = (allowDecimalsInPrice && plugin.getConfig().getBoolean("auto-calculate-item-amount"));
creativeSelectItem = plugin.getConfig().getBoolean("creative-select-item");
blacklist = (plugin.getConfig().getStringList("blacklist") == null) ? new ArrayList<String>() : plugin.getConfig().getStringList("blacklist");
buyGreaterOrEqualSell = plugin.getConfig().getBoolean("buy-greater-or-equal-sell");
confirmShopping = plugin.getConfig().getBoolean("confirm-shopping");
refundShopCreation = plugin.getConfig().getBoolean("refund-shop-creation");
enableUpdateChecker = plugin.getConfig().getBoolean("enable-update-checker");
enableDebugLog = plugin.getConfig().getBoolean("enable-debug-log");
enableEconomyLog = plugin.getConfig().getBoolean("enable-economy-log");
cleanupEconomyLogDays = plugin.getConfig().getInt("cleanup-economy-log-days");
enableWorldGuardIntegration = plugin.getConfig().getBoolean("enable-worldguard-integration");
enableTownyIntegration = plugin.getConfig().getBoolean("enable-towny-integration");
enableAuthMeIntegration = plugin.getConfig().getBoolean("enable-authme-integration");
enablePlotsquaredIntegration = plugin.getConfig().getBoolean("enable-plotsquared-integration");
enableUSkyblockIntegration = plugin.getConfig().getBoolean("enable-uskyblock-integration");
enableASkyblockIntegration = plugin.getConfig().getBoolean("enable-askyblock-integration");
enableBentoBoxIntegration = plugin.getConfig().getBoolean("enable-bentobox-integration");
enableIslandWorldIntegration = plugin.getConfig().getBoolean("enable-islandworld-integration");
enableGriefPreventionIntegration = plugin.getConfig().getBoolean("enable-griefprevention-integration");
enableAreaShopIntegration = plugin.getConfig().getBoolean("enable-areashop-integration");
enableVendorMessages = plugin.getConfig().getBoolean("enable-vendor-messages");
enableVendorBungeeMessages = plugin.getConfig().getBoolean("enable-vendor-bungee-messages");
onlyShowShopsInSight = plugin.getConfig().getBoolean("only-show-shops-in-sight");
appendPotionLevelToItemName = plugin.getConfig().getBoolean("append-potion-level-to-item-name");
removeShopOnError = plugin.getConfig().getBoolean("remove-shop-on-error");
invertMouseButtons = plugin.getConfig().getBoolean("invert-mouse-buttons");
hologramFixedBottom = plugin.getConfig().getBoolean("hologram-fixed-bottom");
hologramLift = plugin.getConfig().getDouble("hologram-lift");
maximalDistance = plugin.getConfig().getDouble("maximal-distance");
maximalItemDistance = plugin.getConfig().getDouble("maximal-item-distance");
shopCreationPriceNormal = plugin.getConfig().getDouble("shop-creation-price.normal");
shopCreationPriceAdmin = plugin.getConfig().getDouble("shop-creation-price.admin");
defaultLimit = plugin.getConfig().getInt("shop-limits.default");
mainCommandName = plugin.getConfig().getString("main-command-name");
languageFile = plugin.getConfig().getString("language-file");
if (firstLoad || langReload) loadLanguageConfig(showMessages);
if (!firstLoad && langReload) LanguageUtils.load();
}
/**
* @return ShopChest's {@link LanguageConfiguration}
*/
public LanguageConfiguration getLanguageConfig() {
return langConfig;
}
private Reader getTextResource(String file, boolean showMessages) {
try {
return (Reader) plugin.getClass().getDeclaredMethod("getTextResource", String.class).invoke(plugin, file);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
if (showMessages) plugin.getLogger().severe("Failed to get file from jar: " + file);
plugin.debug("Failed to get file from jar: " + file);
plugin.debug(e);
}
return null;
}
private void loadLanguageConfig(boolean showMessages) {
langConfig = new LanguageConfiguration(plugin, showMessages);
File langFolder = new File(plugin.getDataFolder(), "lang");
String legacy = Utils.getMajorVersion() < 13 ? "-legacy" : "";
if (!(new File(langFolder, "en_US" + legacy + ".lang")).exists())
plugin.saveResource("lang/en_US" + legacy + ".lang", false);
if (!(new File(langFolder, "de_DE" + legacy + ".lang")).exists())
plugin.saveResource("lang/de_DE" + legacy + ".lang", false);
File langConfigFile = new File(langFolder, languageFile + legacy + ".lang");
File langDefaultFile = new File(langFolder, "en_US" + legacy + ".lang");
if (!langConfigFile.exists()) {
if (!langDefaultFile.exists()) {
try {
Reader r = getTextResource("lang/" + langConfigFile.getName(), showMessages);
if (r == null) {
r = getTextResource("lang/en_US" + legacy + ".lang", showMessages);
if (showMessages) plugin.getLogger().info("Using locale \"en_US" + legacy + "\" (Streamed from jar file)");
} else {
if (showMessages)
plugin.getLogger().info("Using locale \"" + langConfigFile.getName().substring(0, langConfigFile.getName().length() - 5) + "\" (Streamed from jar file)");
}
if (r == null) {
if (showMessages) plugin.getLogger().warning("Using default language values");
plugin.debug("Using default language values (#1)");
}
BufferedReader br = new BufferedReader(r);
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append("\n");
line = br.readLine();
}
langConfig.loadFromString(sb.toString());
} catch (IOException | InvalidConfigurationException e) {
if (showMessages) {
plugin.getLogger().warning("Using default language values");
}
plugin.debug("Using default language values (#2)");
plugin.debug(e);
}
} else {
try {
langConfig.load(langDefaultFile);
if (showMessages) plugin.getLogger().info("Using locale \"en_US" + legacy + "\"");
} catch (IOException | InvalidConfigurationException e) {
if (showMessages) {
plugin.getLogger().warning("Using default language values");
}
plugin.debug("Using default language values (#3)");
plugin.debug(e);
}
}
} else {
try {
if (showMessages)
plugin.getLogger().info("Using locale \"" + langConfigFile.getName().substring(0, langConfigFile.getName().length() - 5) + "\"");
langConfig.load(langConfigFile);
} catch (IOException | InvalidConfigurationException e) {
if (showMessages) {
plugin.getLogger().warning("Using default language values");
}
plugin.debug("Using default language values (#4)");
plugin.debug(e);
}
}
}
}

View File

@ -1,245 +0,0 @@
package de.epiceric.shopchest.config;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.utils.Operator;
public class HologramFormat {
public enum Requirement {
VENDOR, AMOUNT, ITEM_TYPE, ITEM_NAME, HAS_ENCHANTMENT, BUY_PRICE,
SELL_PRICE, HAS_POTION_EFFECT, IS_MUSIC_DISC, IS_POTION_EXTENDED, IS_BANNER_PATTERN,
IS_WRITTEN_BOOK, ADMIN_SHOP, NORMAL_SHOP, IN_STOCK, MAX_STACK, CHEST_SPACE, DURABILITY
}
// no "-" sign since no variable can be negative
// e.g.: 100.0 >= 50.0
private static final Pattern SIMPLE_NUMERIC_CONDITION = Pattern.compile("^(\\d+(?:\\.\\d+)?) ([<>][=]?|[=!]=) (\\d+(?:\\.\\d+)?)$");
// e.g.: "STONE" == "DIAMOND_SWORD"
private static final Pattern SIMPLE_STRING_CONDITION = Pattern.compile("^\"([^\"]*)\" ([=!]=) \"([^\"]*)\"$");
private ScriptEngineManager manager = new ScriptEngineManager();
private ScriptEngine engine = manager.getEngineByName("JavaScript");
private ShopChest plugin;
private File configFile;
private YamlConfiguration config;
public HologramFormat(ShopChest plugin) {
this.configFile = new File(plugin.getDataFolder(), "hologram-format.yml");
this.config = YamlConfiguration.loadConfiguration(configFile);
this.plugin = plugin;
}
/**
* Get the format for the given line of the hologram
* @param line Line of the hologram
* @param reqMap Values of the requirements that might be needed by the format (contains {@code null} if not comparable)
* @param plaMap Values of the placeholders that might be needed by the format
* @return The format of the first working option, or an empty String if no option is working
* because of not fulfilled requirements
*/
public String getFormat(int line, Map<Requirement, Object> reqMap, Map<Placeholder, Object> plaMap) {
ConfigurationSection options = config.getConfigurationSection("lines." + line + ".options");
optionLoop:
for (String key : options.getKeys(false)) {
ConfigurationSection option = options.getConfigurationSection(key);
List<String> requirements = option.getStringList("requirements");
String format = option.getString("format");
for (String sReq : requirements) {
for (Requirement req : reqMap.keySet()) {
if (sReq.contains(req.toString())) {
if (!evalRequirement(sReq, reqMap)) {
continue optionLoop;
}
}
}
}
return evalPlaceholder(format, plaMap);
}
return "";
}
public void reload() {
config = YamlConfiguration.loadConfiguration(configFile);
}
/**
* @return Whether the hologram text has to change dynamically without reloading
*/
public boolean isDynamic() {
int count = getLineCount();
for (int i = 0; i < count; i++) {
ConfigurationSection options = config.getConfigurationSection("lines." + i + ".options");
for (String key : options.getKeys(false)) {
ConfigurationSection option = options.getConfigurationSection(key);
String format = option.getString("format");
if (format.contains(Placeholder.STOCK.toString()) || format.contains(Placeholder.CHEST_SPACE.toString())) {
return true;
}
for (String req : option.getStringList("requirements")) {
if (req.contains(Requirement.IN_STOCK.toString()) || req.contains(Requirement.CHEST_SPACE.toString())) {
return true;
}
}
}
}
return false;
}
/**
* @return Amount of lines in a hologram
*/
public int getLineCount() {
return config.getConfigurationSection("lines").getKeys(false).size();
}
/**
* @return Configuration of the "hologram-format.yml" file
*/
public YamlConfiguration getConfig() {
return config;
}
/**
* Parse and evaluate a condition
* @param condition Condition to evaluate
* @param values Values of the requirements
* @return Result of the condition
*/
public boolean evalRequirement(String condition, Map<Requirement, Object> values) {
String cond = condition;
for (HologramFormat.Requirement req : HologramFormat.Requirement.values()) {
if (cond.contains(req.toString()) && values.containsKey(req)) {
Object val = values.get(req);
String sVal = String.valueOf(val);
if (val instanceof String && !(sVal.startsWith("\"") && sVal.endsWith("\""))) {
sVal = String.format("\"%s\"", sVal);
}
cond = cond.replace(req.toString(), sVal);
}
}
if (cond.equals("true")) {
// e.g.: ADMIN_SHOP
return true;
} else if (cond.equals("false")) {
return false;
} else {
char firstChar = cond.charAt(0);
// numeric cond: first char must be a digit (no variable can be negative)
if (firstChar >= '0' && firstChar <= '9') {
Matcher matcher = SIMPLE_NUMERIC_CONDITION.matcher(cond);
if (matcher.find()) {
Double a, b;
Operator operator;
try {
a = Double.valueOf(matcher.group(1));
operator = Operator.from(matcher.group(2));
b = Double.valueOf(matcher.group(3));
return operator.compare(a, b);
} catch (IllegalArgumentException ignored) {
// should not happen, since regex checked that there is valid number and valid operator
}
}
}
// string cond: first char must be a: "
if (firstChar == '"') {
Matcher matcher = SIMPLE_STRING_CONDITION.matcher(cond);
if (matcher.find()) {
String a, b;
Operator operator;
try {
a = matcher.group(1);
operator = Operator.from(matcher.group(2));
b = matcher.group(3);
return operator.compare(a, b);
} catch (IllegalArgumentException | UnsupportedOperationException ignored) {
// should not happen, since regex checked that there is valid operator
}
}
}
// complex comparison
try {
return (boolean) engine.eval(cond);
} catch (ScriptException e) {
plugin.debug("Failed to eval condition: " + condition);
plugin.debug(e);
return false;
}
}
}
/**
* Parse and evaluate a condition
* @param string Message or hologram format whose containing scripts to execute
* @param values Values of the placeholders
* @return Result of the condition
*/
public String evalPlaceholder(String string, Map<Placeholder, Object> values) {
try {
Matcher matcher = Pattern.compile("\\{([^}]+)}").matcher(string);
String newString = string;
while (matcher.find()) {
String withBrackets = matcher.group();
String script = withBrackets.substring(1, withBrackets.length() - 1);
for (Placeholder placeholder : values.keySet()) {
if (script.contains(placeholder.toString())) {
Object val = values.get(placeholder);
String sVal = String.valueOf(val);
if (val instanceof String && !(sVal.startsWith("\"") && sVal.endsWith("\""))) {
sVal = String.format("\"%s\"", sVal);
}
script = script.replace(placeholder.toString(), sVal);
}
}
String result = String.valueOf(engine.eval(script));
newString = newString.replace(withBrackets, result);
}
return newString;
} catch (ScriptException e) {
plugin.debug("Failed to eval placeholder script in string: " + string);
plugin.debug(e);
}
return string;
}
}

View File

@ -1,132 +0,0 @@
package de.epiceric.shopchest.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import de.epiceric.shopchest.ShopChest;
public class LanguageConfiguration extends FileConfiguration {
private ArrayList<String> lines = new ArrayList<>();
private HashMap<String, String> values = new HashMap<>();
private ShopChest plugin;
private boolean showMessages;
private File file;
public LanguageConfiguration(ShopChest plugin, boolean showMessages) {
this.plugin = plugin;
this.showMessages = showMessages;
}
@Override
public String saveToString() {
StringBuilder sb = new StringBuilder("");
for (String line : lines) {
sb.append(line);
sb.append("\n");
}
return sb.toString();
}
@Override
public String getString(String path, String def) {
for (String key : values.keySet()) {
if (key.equals(path)) {
return values.get(key);
}
}
values.put(path, def);
if (file != null) {
// Append missing entry to loaded language file
try (FileWriter writer = new FileWriter(file, true)) {
writer.write(path + "=" + def + "\n");
if (showMessages)
plugin.getLogger().info("Missing translation for \"" + path + "\" has been added as \"" + def + "\" to the selected language file.");
} catch (IOException e) {
plugin.debug("Failed to add language entry");
plugin.debug(e);
if (showMessages)
plugin.getLogger().severe("Failed to add missing translation for \"" + path + "\" to the selected langauge file.");
}
}
return def;
}
@Override
public void load(File file) throws IOException, InvalidConfigurationException {
this.file = file;
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append("\n");
line = br.readLine();
}
fis.close();
isr.close();
br.close();
loadFromString(sb.toString());
}
@Override
public void loadFromString(String s) throws InvalidConfigurationException {
String[] lines = s.split("\n");
for (String line : lines) {
if (!line.isEmpty()) {
this.lines.add(line);
if (!line.startsWith("#")) {
if (line.contains("=")) {
if (line.split("=").length >= 2) {
String key = line.split("=")[0];
StringBuilder sbValue = new StringBuilder();
for (int i = 1; i < line.split("=").length; i++) {
if (i > 1) {
sbValue.append("=");
}
sbValue.append(line.split("=")[i]);
}
String value = sbValue.toString();
values.put(key, value);
} else if (line.split("=").length == 1) {
String key = line.split("=")[0];
values.put(key, "");
}
}
}
}
}
}
@Override
protected String buildHeader() {
return null;
}
}

View File

@ -1,43 +0,0 @@
package de.epiceric.shopchest.config;
public enum Placeholder {
VENDOR("%VENDOR%"),
AMOUNT("%AMOUNT%"),
ITEM_NAME("%ITEMNAME%"),
CREATION_PRICE("%CREATION-PRICE%"),
ERROR("%ERROR%"),
ENCHANTMENT("%ENCHANTMENT%"),
MIN_PRICE("%MIN-PRICE%"),
MAX_PRICE("%MAX-PRICE%"),
VERSION("%VERSION%"),
BUY_PRICE("%BUY-PRICE%"),
SELL_PRICE("%SELL-PRICE%"),
LIMIT("%LIMIT%"),
PLAYER("%PLAYER%"),
POTION_EFFECT("%POTION-EFFECT%"),
MUSIC_TITLE("%MUSIC-TITLE%"),
BANNER_PATTERN_NAME("%BANNER-PATTERN-NAME%"),
PROPERTY("%PROPERTY%"),
VALUE("%VALUE%"),
EXTENDED("%EXTENDED%"),
REVENUE("%REVENUE%"),
GENERATION("%GENERATION%"),
STOCK("%STOCK%"),
CHEST_SPACE("%CHEST-SPACE%"),
MAX_STACK("%MAX-STACK%"),
COMMAND("%COMMAND%"),
DURABILITY("%DURABILITY%");
private String name;
Placeholder(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}

View File

@ -1,59 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when a player buys or sells something from or to a shop
*/
public class ShopBuySellEvent extends ShopEvent implements Cancellable {
private Type type;
private int newAmount;
private double newPrice;
private boolean cancelled;
public ShopBuySellEvent(Player player, Shop shop, Type type, int newAmount, double newPrice) {
super(player, shop);
this.type = type;
this.newAmount = newAmount;
this.newPrice = newPrice;
}
/**
* @return Whether the player buys or sells something
*/
public Type getType() {
return type;
}
/**
* @return The amount which might be modified because of automatic item amount calculation
*/
public int getNewAmount() {
return newAmount;
}
/**
* @return The price which might be modified because of automatic item amount calculation
*/
public double getNewPrice() {
return newPrice;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
public enum Type {
BUY,
SELL;
}
}

View File

@ -1,36 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when a player creates a shop (clicks on a chest)
*/
public class ShopCreateEvent extends ShopEvent implements Cancellable {
private double creationPrice;
private boolean cancelled;
public ShopCreateEvent(Player player, Shop shop, double creationPrice) {
super(player, shop);
this.creationPrice = creationPrice;
}
/**
* @return The price the player has to pay in order to create the shop (only if the event is not cancelled)
*/
public double getCreationPrice() {
return creationPrice;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,43 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import de.epiceric.shopchest.shop.Shop;
public abstract class ShopEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private Shop shop;
private Player player;
public ShopEvent(Player player, Shop shop) {
this.player = player;
this.shop = shop;
}
/**
* @return Shop which is involved in this event
*/
public Shop getShop() {
return shop;
}
/**
* @return Player who is involved in this event
*/
public Player getPlayer() {
return player;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -1,37 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when a player extends a shop (making a chest a double chest)
*/
public class ShopExtendEvent extends ShopEvent implements Cancellable {
private boolean cancelled;
private Location newChestLocation;
public ShopExtendEvent(Player player, Shop shop, Location newChest) {
super(player, shop);
this.newChestLocation = newChest;
}
/**
* @return Location of the placed chest
*/
public Location getNewChestLocation() {
return newChestLocation;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,27 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when a player retrieves information about a shop (clicks on a chest)
*/
public class ShopInfoEvent extends ShopEvent implements Cancellable {
private boolean cancelled;
public ShopInfoEvent(Player player, Shop shop) {
super(player, shop);
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,32 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* @deprecated Use {@link ShopsLoadedEvent} instead since shops are loaded
* dynamically based on chunk loading
*/
@Deprecated
public class ShopInitializedEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private int amount;
public ShopInitializedEvent(int amount) {
this.amount = amount;
}
public int getAmount() {
return amount;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -1,27 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when a player opens a shop (clicks on a chest)
*/
public class ShopOpenEvent extends ShopEvent implements Cancellable {
private boolean cancelled;
public ShopOpenEvent(Player player, Shop shop) {
super(player, shop);
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,27 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when a player wants to create a shop (enters the command)
*/
public class ShopPreCreateEvent extends ShopEvent implements Cancellable {
private boolean cancelled;
public ShopPreCreateEvent(Player player, Shop shop) {
super(player, shop);
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,46 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Called when a player wants to retrieve information about a shop (enters the command)
*/
public class ShopPreInfoEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Player player;
private boolean cancelled;
public ShopPreInfoEvent(Player player) {
this.player = player;
}
/**
* @return Player who is involved in this event
*/
public Player getPlayer() {
return player;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -1,46 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Called when a player wants to open a shop (enters the command)
*/
public class ShopPreOpenEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Player player;
private boolean cancelled;
public ShopPreOpenEvent(Player player) {
this.player = player;
}
/**
* @return Player who is involved in this event
*/
public Player getPlayer() {
return player;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -1,46 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Called when a player wants to remove a shop (enters the command)
*/
public class ShopPreRemoveEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Player player;
private boolean cancelled;
public ShopPreRemoveEvent(Player player) {
this.player = player;
}
/**
* @return Player who is involved in this event
*/
public Player getPlayer() {
return player;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -1,46 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Called when a player reloads the shops
*/
public class ShopReloadEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private CommandSender sender;
private boolean cancelled;
public ShopReloadEvent(CommandSender sender) {
this.sender = sender;
}
/**
* @return Sender, who caused the reload ({@link org.bukkit.entity.Player} or {@link org.bukkit.command.ConsoleCommandSender})
*/
public CommandSender getSender() {
return sender;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancelled;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,57 +0,0 @@
package de.epiceric.shopchest.event;
import java.util.List;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import de.epiceric.shopchest.shop.Shop;
public class ShopRemoveAllEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private CommandSender sender;
private OfflinePlayer vendor;
private List<Shop> shops;
private boolean cancelled;
public ShopRemoveAllEvent(CommandSender sender, OfflinePlayer vendor, List<Shop> shops) {
this.sender = sender;
this.vendor = vendor;
this.shops = shops;
}
public CommandSender getSender() {
return sender;
}
public OfflinePlayer getVendor() {
return vendor;
}
public List<Shop> getShops() {
return shops;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancelled;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@ -1,27 +0,0 @@
package de.epiceric.shopchest.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when a player removes a shop (clicks on a chest)
*/
public class ShopRemoveEvent extends ShopEvent implements Cancellable {
private boolean cancelled;
public ShopRemoveEvent(Player player, Shop shop) {
super(player, shop);
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,33 +0,0 @@
package de.epiceric.shopchest.event;
import java.util.Collection;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import de.epiceric.shopchest.shop.Shop;
/**
* Called when shops have been loaded and added to the server
*/
public class ShopsLoadedEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private Collection<Shop> shops;
public ShopsLoadedEvent(Collection<Shop> shops) {
this.shops = shops;
}
public Collection<Shop> getShops() {
return shops;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -1,9 +0,0 @@
package de.epiceric.shopchest.exceptions;
public class ChestNotFoundException extends Exception {
private static final long serialVersionUID = -6446875473671870708L;
public ChestNotFoundException(String message) {
super(message);
}
}

View File

@ -1,9 +0,0 @@
package de.epiceric.shopchest.exceptions;
public class NotEnoughSpaceException extends Exception {
private static final long serialVersionUID = 3718475607700458355L;
public NotEnoughSpaceException(String message) {
super(message);
}
}

View File

@ -1,9 +0,0 @@
package de.epiceric.shopchest.exceptions;
public class WorldNotFoundException extends Exception {
private static final long serialVersionUID = -555886332156936972L;
public WorldNotFoundException(String worldName) {
super("Could not find world with name \"" + worldName + "\"");
}
}

View File

@ -1,25 +0,0 @@
package de.epiceric.shopchest.external;
import org.bukkit.Material;
import de.epiceric.shopchest.ShopChest;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.managers.RanksManager;
public class BentoBoxShopFlag {
public static final Flag SHOP_FLAG = new Flag.Builder("CREATE_SHOPS", Material.CHEST)
.type(Flag.Type.PROTECTION)
.mode(Flag.Mode.BASIC)
.defaultRank(RanksManager.TRUSTED_RANK)
.build();
public static void register(ShopChest plugin) {
if (BentoBox.getInstance().getFlagsManager().registerFlag(SHOP_FLAG)) {
plugin.debug("Registered BentoBox shop flag");
} else {
plugin.getLogger().warning("Failed to register BentoBox shop flag");
plugin.debug("Failed to register BentoBox shop flag");
}
}
}

View File

@ -1,112 +0,0 @@
package de.epiceric.shopchest.external;
import com.github.intellectualsites.plotsquared.plot.flag.Flag;
import com.github.intellectualsites.plotsquared.plot.flag.Flags;
import com.github.intellectualsites.plotsquared.plot.object.Plot;
import org.bukkit.entity.Player;
import de.epiceric.shopchest.ShopChest;
import java.util.Locale;
public class PlotSquaredOldShopFlag {
public enum Group {
OWNERS, MEMBERS, TRUSTED, EVERYONE, NONE
}
public static final GroupFlag CREATE_SHOP = new GroupFlag("create-shop");
public static final GroupFlag USE_SHOP = new GroupFlag("use-shop");
public static final GroupFlag USE_ADMIN_SHOP = new GroupFlag("use-admin-shop");
private static boolean registered = false;
public static void register(ShopChest plugin) {
if (registered) return;
Flags.registerFlag(CREATE_SHOP);
Flags.registerFlag(USE_SHOP);
Flags.registerFlag(USE_ADMIN_SHOP);
registered = true;
plugin.debug("Registered custom PlotSquared flags");
}
/**
* Check if a flag is allowed for a player on a plot from PlotSquared
* @param plot Plot from PlotSquared
* @param flag Flag to check
* @param p Player to check
* @return Whether the flag is allowed for the player
*/
public static boolean isFlagAllowedOnPlot(Plot plot, GroupFlag flag, Player p) {
if (plot != null && flag != null) {
Group group = plot.getFlag(flag, Group.NONE);
ShopChest.getInstance().debug("Flag " + flag.getName() + " is set to " + group);
switch (group) {
case OWNERS:
return plot.getOwners().contains(p.getUniqueId());
case TRUSTED:
return plot.getOwners().contains(p.getUniqueId()) || plot.getTrusted().contains(p.getUniqueId());
case MEMBERS:
return plot.getOwners().contains(p.getUniqueId()) || plot.getTrusted().contains(p.getUniqueId()) || plot.getMembers().contains(p.getUniqueId());
case EVERYONE:
return true;
case NONE:
return false;
}
}
ShopChest.getInstance().debug("Flag or plot is null, or value of flag is not a group");
return true;
}
public static class GroupFlag extends Flag<Group> {
public GroupFlag(String name) {
super(name);
}
@Override
public String valueToString(Object value) {
return String.valueOf(value);
}
@Override
public Group parseValue(String s) {
String val = s.toLowerCase(Locale.ENGLISH);
switch (val) {
case "owners":
case "owner":
return Group.OWNERS;
case "members":
case "member":
case "helpers":
case "helper":
return Group.MEMBERS;
case "trusted":
return Group.TRUSTED;
case "everyone":
case "all":
return Group.EVERYONE;
case "deny":
case "disallow":
case "false":
case "no":
case "0":
case "none":
case "noone":
return Group.NONE;
}
return null;
}
@Override
public String getValueDescription() {
return "Flag value must be a group: 'owner' , 'members', 'trusted', 'everyone' or 'none'";
}
}
}

View File

@ -1,154 +0,0 @@
package de.epiceric.shopchest.external;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import com.plotsquared.core.configuration.caption.Caption;
import com.plotsquared.core.configuration.caption.StaticCaption;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.flag.FlagParseException;
import com.plotsquared.core.plot.flag.GlobalFlagContainer;
import com.plotsquared.core.plot.flag.PlotFlag;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import de.epiceric.shopchest.ShopChest;
import net.kyori.adventure.text.minimessage.Template;
public class PlotSquaredShopFlag {
public enum Group {
OWNERS, MEMBERS, TRUSTED, EVERYONE, NONE
}
private static final String[] lowercaseValues = Arrays.asList(Group.values()).stream()
.map(value -> String.valueOf(value).toLowerCase(Locale.ENGLISH))
.toArray(String[]::new);
public static final GroupFlag<?> CREATE_SHOP = new CreateShopFlag(Group.MEMBERS);
public static final GroupFlag<?> USE_SHOP = new UseShopFlag(Group.EVERYONE);
private static boolean registered = false;
public static void register(ShopChest plugin) {
if (registered) return;
GlobalFlagContainer.getInstance().addFlag(CREATE_SHOP);
GlobalFlagContainer.getInstance().addFlag(USE_SHOP);
registered = true;
plugin.debug("Registered custom PlotSquared flags");
}
/**
* Check if a flag is allowed for a player on a plot from PlotSquared
*
* @param plot Plot from PlotSquared
* @param flag Flag to check
* @param p Player to check
* @return Whether the flag is allowed for the player
*/
public static boolean isFlagAllowedOnPlot(Plot plot, GroupFlag<?> flag, Player p) {
if (plot != null && flag != null) {
Group group = plot.getFlag(flag);
ShopChest.getInstance().debug("Flag " + flag.getName() + " is set to " + group);
switch (group) {
case OWNERS:
return plot.getOwners().contains(p.getUniqueId());
case TRUSTED:
return plot.getOwners().contains(p.getUniqueId()) || plot.getTrusted().contains(p.getUniqueId());
case MEMBERS:
return plot.getOwners().contains(p.getUniqueId()) || plot.getTrusted().contains(p.getUniqueId()) || plot.getMembers().contains(p.getUniqueId());
case EVERYONE:
return true;
case NONE:
return false;
}
}
ShopChest.getInstance().debug("Flag or plot is null, or value of flag is not a group");
return true;
}
public static class CreateShopFlag extends GroupFlag<CreateShopFlag> {
public CreateShopFlag(Group value) {
super(value, StaticCaption.of("Set to the group that is allowed to create shops."));
}
@Override
protected CreateShopFlag flagOf(@NotNull Group value) {
return new CreateShopFlag(value);
}
}
public static class UseShopFlag extends GroupFlag<UseShopFlag> {
public UseShopFlag(Group value) {
super(value, StaticCaption.of("Set to the group that is allowed to use shops."));
}
@Override
protected UseShopFlag flagOf(@NotNull Group value) {
return new UseShopFlag(value);
}
}
public abstract static class GroupFlag<F extends PlotFlag<Group, F>> extends PlotFlag<Group, F> {
protected GroupFlag(Group value, Caption description) {
super(value, TranslatableCaption.of("flags.flag_category_enum"), description);
}
@Override
public String toString() {
return String.valueOf(getValue()).toLowerCase(Locale.ENGLISH);
}
@Override
public String getExample() {
return "members";
}
@Override
public F merge(@NotNull Group newValue) {
return flagOf(newValue);
}
@Override
public F parse(@NotNull String input) throws FlagParseException {
switch (input.toLowerCase(Locale.ENGLISH)) {
case "owners":
case "owner":
return this.flagOf(Group.OWNERS);
case "members":
case "member":
case "helpers":
case "helper":
return this.flagOf(Group.MEMBERS);
case "trusted":
return this.flagOf(Group.TRUSTED);
case "everyone":
case "all":
return this.flagOf(Group.EVERYONE);
case "deny":
case "disallow":
case "false":
case "no":
case "0":
case "none":
case "noone":
return this.flagOf(Group.NONE);
}
throw new FlagParseException(this, input, TranslatableCaption.of("flags.flag_error_enum"),
Template.of("list", String.join(", ", lowercaseValues)));
}
@Override
public Collection<String> getTabCompletions() {
return Arrays.asList(lowercaseValues);
}
}
}

View File

@ -1,31 +0,0 @@
package de.epiceric.shopchest.external;
import java.util.Optional;
import org.codemc.worldguardwrapper.WorldGuardWrapper;
import org.codemc.worldguardwrapper.flag.IWrappedFlag;
import org.codemc.worldguardwrapper.flag.WrappedState;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
public class WorldGuardShopFlag {
public static void register(final ShopChest plugin) {
WorldGuardWrapper wrapper = WorldGuardWrapper.getInstance();
Optional<IWrappedFlag<WrappedState>> createFlag = wrapper.registerFlag("create-shop",
WrappedState.class, Config.wgAllowCreateShopDefault ? WrappedState.ALLOW : WrappedState.DENY);
Optional<IWrappedFlag<WrappedState>> useFlag = wrapper.registerFlag("use-shop",
WrappedState.class, Config.wgAllowUseShopDefault ? WrappedState.ALLOW : WrappedState.DENY);
Optional<IWrappedFlag<WrappedState>> useAdminFlag = wrapper.registerFlag("use-admin-shop",
WrappedState.class, Config.wgAllowUseAdminShopDefault ? WrappedState.ALLOW : WrappedState.DENY);
plugin.debug("Flag create-shop: " + String.valueOf(createFlag.isPresent()));
plugin.debug("Flag use-shop: " + String.valueOf(useFlag.isPresent()));
plugin.debug("Flag use-admin-shop: " + String.valueOf(useAdminFlag.isPresent()));
}
}

View File

@ -1,62 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Set;
import com.wasteofplastic.askyblock.ASkyBlockAPI;
import com.wasteofplastic.askyblock.Island;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.utils.Utils;
public class ASkyBlockListener implements Listener {
private final ShopChest plugin;
public ASkyBlockListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enableASkyblockIntegration)
return;
Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
for (Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enableASkyblockIntegration)
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e);
}
private boolean handleForLocation(Player player, Location loc, Cancellable e) {
Island island = ASkyBlockAPI.getInstance().getIslandAt(loc);
if (island == null)
return false;
if (!player.getUniqueId().equals(island.getOwner()) && !island.getMembers().contains(player.getUniqueId())) {
e.setCancelled(true);
plugin.debug("Cancel Reason: ASkyBlock");
return true;
}
return false;
}
}

View File

@ -1,57 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Set;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.external.BentoBoxShopFlag;
import de.epiceric.shopchest.utils.Utils;
import world.bentobox.bentobox.api.flags.FlagListener;
public class BentoBoxListener extends FlagListener {
private ShopChest plugin;
public BentoBoxListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enableBentoBoxIntegration)
return;
Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
for (Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enableBentoBoxIntegration)
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e);
}
private boolean handleForLocation(Player player, Location loc, Cancellable e) {
boolean allowed = checkIsland((Event) e, player, loc, BentoBoxShopFlag.SHOP_FLAG);
if (!allowed) {
e.setCancelled(true);
plugin.debug("Cancel Reason: BentoBox");
return true;
}
return false;
}
}

View File

@ -1,62 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Set;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.utils.Utils;
import me.ryanhamshire.GriefPrevention.Claim;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
public class GriefPreventionListener implements Listener {
private final ShopChest plugin;
private final GriefPrevention griefPrevention;
public GriefPreventionListener(ShopChest plugin) {
this.plugin = plugin;
this.griefPrevention = plugin.getGriefPrevention();
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enableGriefPreventionIntegration)
return;
Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
for (Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enableASkyblockIntegration)
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e);
}
private boolean handleForLocation(Player player, Location loc, Cancellable e) {
Claim claim = griefPrevention.dataStore.getClaimAt(loc, false, null);
if (claim == null)
return false;
if (claim.allowContainers(player) != null) {
e.setCancelled(true);
plugin.debug("Cancel Reason: GriefPrevention");
return true;
}
return false;
}
}

View File

@ -1,59 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Set;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.utils.Utils;
import pl.islandworld.api.IslandWorldApi;
public class IslandWorldListener implements Listener {
private final ShopChest plugin;
public IslandWorldListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enableIslandWorldIntegration || !IslandWorldApi.isInitialized())
return;
Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
for (Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enableIslandWorldIntegration || !IslandWorldApi.isInitialized())
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e);
}
private boolean handleForLocation(Player player, Location loc, Cancellable e) {
if (!loc.getWorld().getName().equals(IslandWorldApi.getIslandWorld().getName()))
return false;
if (!IslandWorldApi.canBuildOnLocation(player, loc, true)) {
e.setCancelled(true);
plugin.debug("Cancel Reason: IslandWorld");
return true;
}
return false;
}
}

View File

@ -1,90 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Set;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.plot.Plot;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.external.PlotSquaredOldShopFlag;
import de.epiceric.shopchest.external.PlotSquaredShopFlag;
import de.epiceric.shopchest.utils.Utils;
public class PlotSquaredListener implements Listener {
private final ShopChest plugin;
public PlotSquaredListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enablePlotsquaredIntegration)
return;
Set<org.bukkit.Location> chestLocations = Utils.getChestLocations(e.getShop());
for (org.bukkit.Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enablePlotsquaredIntegration)
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e);
}
// TODO: Outsource shop use external permission
// @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
// public void onBuySell(ShopBuySellEvent e) {
// if (!Config.enablePlotsquaredIntegration)
// return;
// Set<org.bukkit.Location> chestLocations = Utils.getChestLocations(e.getShop());
// for (org.bukkit.Location loc : chestLocations) {
// Location plotLocation = new Location(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
// Plot plot = plotLocation.getOwnedPlot();
// if (!isFlagAllowed(plot, PlotSquaredShopFlag.USE_SHOP, e.getPlayer())) {
// e.setCancelled(true);
// plugin.debug("Cancel Reason: PlotSquared");
// return;
// }
// }
// }
private boolean handleForLocation(Player player, org.bukkit.Location loc, Cancellable e) {
boolean isAllowed = false;
try {
Class.forName("com.plotsquared.core.PlotSquared");
Location plotLocation = Location.at(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
Plot plot = plotLocation.getOwnedPlot();
isAllowed = PlotSquaredShopFlag.isFlagAllowedOnPlot(plot, PlotSquaredShopFlag.CREATE_SHOP, player);
} catch (ClassNotFoundException ex) {
com.github.intellectualsites.plotsquared.plot.object.Location plotLocation = new com.github.intellectualsites.plotsquared.plot.object.Location(
loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
com.github.intellectualsites.plotsquared.plot.object.Plot plot = plotLocation.getOwnedPlot();
isAllowed = PlotSquaredOldShopFlag.isFlagAllowedOnPlot(plot, PlotSquaredOldShopFlag.CREATE_SHOP, player);
}
if (!isAllowed) {
e.setCancelled(true);
plugin.debug("Cancel Reason: PlotSquared");
return true;
}
return false;
}
}

View File

@ -1,85 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Optional;
import java.util.Set;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town;
import com.palmergames.bukkit.towny.object.TownBlock;
import com.palmergames.bukkit.towny.object.WorldCoord;
import com.palmergames.bukkit.towny.TownyUniverse;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.utils.Utils;
public class TownyListener implements Listener {
private final ShopChest plugin;
public TownyListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enableTownyIntegration)
return;
Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
for (Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enablePlotsquaredIntegration)
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e);
}
private boolean handleForLocation(Player player, Location loc, Cancellable e) {
try {
TownBlock townBlock = TownyUniverse.getInstance().getTownBlock(WorldCoord.parseWorldCoord(loc));
if (townBlock == null)
return false;
Town town = townBlock.getTown();
Optional<Resident> playerResident = town.getResidents().stream()
.filter(r -> r.getName().equals(player.getName()))
.findFirst();
if (!playerResident.isPresent()) {
e.setCancelled(true);
plugin.debug("Cancel Reason: Towny (no resident)");
return true;
}
Resident resident = playerResident.get();
String plotType = townBlock.getType().name();
boolean cancel = (resident.isMayor() && !Config.townyShopPlotsMayor.contains(plotType))
|| (resident.isKing() && !Config.townyShopPlotsKing.contains(plotType))
|| (!resident.isKing() && !resident.isMayor() && !Config.townyShopPlotsResidents.contains(plotType));
if (cancel) {
e.setCancelled(true);
plugin.debug("Cancel Reason: Towny (no permission)");
return true;
}
} catch (NotRegisteredException ignored) {
}
return false;
}
}

View File

@ -1,61 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Set;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.utils.Utils;
import us.talabrek.ultimateskyblock.api.IslandInfo;
import us.talabrek.ultimateskyblock.api.uSkyBlockAPI;
public class USkyBlockListener implements Listener {
private final ShopChest plugin;
private final uSkyBlockAPI uSkyBlockAPI;
public USkyBlockListener(ShopChest plugin) {
this.plugin = plugin;
this.uSkyBlockAPI = plugin.getUSkyBlock();
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enableUSkyblockIntegration)
return;
Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
for (Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enablePlotsquaredIntegration)
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e);
}
private boolean handleForLocation(Player player, Location loc, Cancellable e) {
IslandInfo islandInfo = uSkyBlockAPI.getIslandInfo(loc);
if (islandInfo == null)
return false;
if (!player.getName().equals(islandInfo.getLeader()) && !islandInfo.getMembers().contains(player.getName())) {
e.setCancelled(true);
plugin.debug("Cancel Reason: uSkyBlock");
return true;
}
return false;
}
}

View File

@ -1,97 +0,0 @@
package de.epiceric.shopchest.external.listeners;
import java.util.Optional;
import java.util.Set;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.codemc.worldguardwrapper.WorldGuardWrapper;
import org.codemc.worldguardwrapper.flag.IWrappedFlag;
import org.codemc.worldguardwrapper.flag.WrappedState;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopCreateEvent;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.utils.Utils;
public class WorldGuardListener implements Listener {
private final ShopChest plugin;
private final WorldGuardWrapper wgWrapper;
public WorldGuardListener(ShopChest plugin) {
this.plugin = plugin;
this.wgWrapper = WorldGuardWrapper.getInstance();
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreateShop(ShopCreateEvent e) {
if (!Config.enableWorldGuardIntegration)
return;
Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
IWrappedFlag<WrappedState> flag = getStateFlag("create-shop");
for (Location loc : chestLocations) {
if (handleForLocation(e.getPlayer(), loc, e, flag))
return;
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onExtendShop(ShopExtendEvent e) {
if (!Config.enableWorldGuardIntegration)
return;
handleForLocation(e.getPlayer(), e.getNewChestLocation(), e, getStateFlag("create-shop"));
}
// TODO: Outsource shop use external permission
// @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
// public void onBuySell(ShopBuySellEvent e) {
// if (!Config.enableWorldGuardIntegration)
// return;
// Set<Location> chestLocations = Utils.getChestLocations(e.getShop());
// String flagName = e.getShop().getShopType() == ShopType.ADMIN ? "use-admin-shop" : "use-shop";
// IWrappedFlag<WrappedState> flag = getStateFlag(flagName);
// for (Location loc : chestLocations) {
// WrappedState state = wgWrapper.queryFlag(e.getPlayer(), loc, flag).orElse(WrappedState.DENY);
// if (state == WrappedState.DENY) {
// e.setCancelled(true);
// return;
// }
// }
// }
private boolean handleForLocation(Player player, Location loc, Cancellable e, IWrappedFlag<WrappedState> flag) {
if (flag == null) {
// Flag may have not been registered successfully, so ignore them.
return false;
}
WrappedState state = wgWrapper.queryFlag(player, loc, flag).orElse(WrappedState.DENY);
if (state == WrappedState.DENY) {
e.setCancelled(true);
plugin.debug("Cancel Reason: WorldGuard");
return true;
}
return false;
}
private IWrappedFlag<WrappedState> getStateFlag(String flagName) {
Optional<IWrappedFlag<WrappedState>> flagOptional = wgWrapper.getFlag(flagName, WrappedState.class);
if (!flagOptional.isPresent()) {
plugin.getLogger().severe("Failed to get WorldGuard state flag '" + flagName + "'.");
plugin.debug("WorldGuard state flag '" + flagName + "' is not present!");
return null;
}
return flagOptional.get();
}
}

View File

@ -1,29 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.Material;
public class BannerPatternName {
private Material bannerPatternMaterial;
private String localizedName;
public BannerPatternName(Material bannerPatternMaterial, String localizedName) {
this.bannerPatternMaterial = bannerPatternMaterial;
this.localizedName = localizedName;
}
/**
* @return Localized Name of the Banner Pattern
*/
public String getLocalizedName() {
return localizedName;
}
/**
* @return Material of the Banner Pattern
*/
public Material getBannerPatternMaterial() {
return bannerPatternMaterial;
}
}

View File

@ -1,28 +0,0 @@
package de.epiceric.shopchest.language;
import de.epiceric.shopchest.nms.CustomBookMeta;
public class BookGenerationName {
private String localizedName;
private CustomBookMeta.Generation generation;
public BookGenerationName(CustomBookMeta.Generation generation, String localizedName) {
this.generation = generation;
this.localizedName = localizedName;
}
/**
* @return Generation linked to the name
*/
public CustomBookMeta.Generation getGeneration() {
return generation;
}
/**
* @return Name linked to the book generation
*/
public String getLocalizedName() {
return localizedName;
}
}

View File

@ -1,53 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.enchantments.Enchantment;
public class EnchantmentName {
private Enchantment enchantment;
private String localizedName;
public EnchantmentName(Enchantment enchantment, String localizedName) {
this.enchantment = enchantment;
this.localizedName = localizedName;
}
/**
* @return Enchantment linked to the name
*/
public Enchantment getEnchantment() {
return enchantment;
}
/**
* @return Name linked to the enchantment
*/
public String getLocalizedName() {
return localizedName;
}
public static class EnchantmentLevelName {
private int level;
private String localizedName;
public EnchantmentLevelName(int level, String localizedName) {
this.level = level;
this.localizedName = localizedName;
}
/**
* @return Level linked to the name
*/
public int getLevel() {
return level;
}
/**
* @return Name linked to the level
*/
public String getLocalizedName() {
return localizedName;
}
}
}

View File

@ -1,28 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.entity.EntityType;
public class EntityName {
private String localizedName;
private EntityType entityType;
public EntityName(EntityType entityType, String localizedName) {
this.entityType = entityType;
this.localizedName = localizedName;
}
/**
* @return EntityType linked to the name
*/
public EntityType getEntityType() {
return entityType;
}
/**
* @return Name linked to the EntityType
*/
public String getLocalizedName() {
return localizedName;
}
}

View File

@ -1,42 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.Material;
public class ItemName {
private Material material;
private int subId;
private String localizedName;
public ItemName(Material material, String localizedName) {
this(material, 0, localizedName);
}
public ItemName(Material material, int subId, String localizedName) {
this.material = material;
this.subId = subId;
this.localizedName = localizedName;
}
/**
* @return Material linked to the name
*/
public Material getMaterial() {
return material;
}
/**
* @return Sub ID linked to the name
*/
public int getSubId() {
return subId;
}
/**
* @return Name linked to the item
*/
public String getLocalizedName() {
return localizedName;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.ChatColor;
public class LocalizedMessage {
private Message message;
private String localizedString;
public LocalizedMessage(Message message, String localizedString) {
this.message = message;
this.localizedString = ChatColor.translateAlternateColorCodes('&', localizedString);
}
/**
* @return {@link Message} linked to this object
*/
public Message getMessage() {
return message;
}
/**
* @return Localized Message
*/
public String getLocalizedString() {
return localizedString;
}
}

View File

@ -1,99 +0,0 @@
package de.epiceric.shopchest.language;
public enum Message {
SHOP_CREATED,
ADMIN_SHOP_CREATED,
CHEST_ALREADY_SHOP,
CHEST_BLOCKED,
DOUBLE_CHEST_BLOCKED,
SHOP_REMOVED,
SHOP_REMOVED_REFUND,
ALL_SHOPS_REMOVED,
CHEST_NO_SHOP,
SHOP_CREATE_NOT_ENOUGH_MONEY,
SHOP_INFO_VENDOR,
SHOP_INFO_PRODUCT,
SHOP_INFO_STOCK,
SHOP_INFO_CHEST_SPACE,
SHOP_INFO_PRICE,
SHOP_INFO_DISABLED,
SHOP_INFO_NORMAL,
SHOP_INFO_ADMIN,
BUY_SELL_DISABLED,
BUY_SUCCESS,
BUY_SUCCESS_ADMIN,
SELL_SUCCESS,
SELL_SUCCESS_ADMIN,
SOMEONE_BOUGHT,
SOMEONE_SOLD,
REVENUE_WHILE_OFFLINE,
NOT_ENOUGH_INVENTORY_SPACE,
CHEST_NOT_ENOUGH_INVENTORY_SPACE,
NOT_ENOUGH_MONEY,
NOT_ENOUGH_ITEMS,
VENDOR_NOT_ENOUGH_MONEY,
OUT_OF_STOCK,
VENDOR_OUT_OF_STOCK,
ERROR_OCCURRED,
AMOUNT_PRICE_NOT_NUMBER,
AMOUNT_IS_ZERO,
PRICES_CONTAIN_DECIMALS,
NO_ITEM_IN_HAND,
CLICK_CHEST_CREATE,
CLICK_CHEST_REMOVE,
CLICK_CHEST_INFO,
CLICK_CHEST_OPEN,
CLICK_TO_CONFIRM,
OPENED_SHOP,
CANNOT_BREAK_SHOP,
CANNOT_SELL_BROKEN_ITEM,
BUY_PRICE_TOO_LOW,
SELL_PRICE_TOO_LOW,
BUY_PRICE_TOO_HIGH,
SELL_PRICE_TOO_HIGH,
BUYING_DISABLED,
SELLING_DISABLED,
RELOADED_SHOPS,
SHOP_LIMIT_REACHED,
OCCUPIED_SHOP_SLOTS,
CANNOT_SELL_ITEM,
USE_IN_CREATIVE,
SELECT_ITEM,
ITEM_SELECTED,
CREATION_CANCELLED,
UPDATE_AVAILABLE,
UPDATE_CLICK_TO_DOWNLOAD,
UPDATE_NO_UPDATE,
UPDATE_CHECKING,
UPDATE_ERROR,
NO_PERMISSION_CREATE,
NO_PERMISSION_CREATE_ADMIN,
NO_PERMISSION_CREATE_PROTECTED,
NO_PERMISSION_OPEN_OTHERS,
NO_PERMISSION_BUY,
NO_PERMISSION_SELL,
NO_PERMISSION_BUY_HERE,
NO_PERMISSION_SELL_HERE,
NO_PERMISSION_REMOVE_OTHERS,
NO_PERMISSION_REMOVE_ADMIN,
NO_PERMISSION_RELOAD,
NO_PERMISSION_UPDATE,
NO_PERMISSION_CONFIG,
NO_PERMISSION_EXTEND_OTHERS,
NO_PERMISSION_EXTEND_PROTECTED,
COMMAND_DESC_HEADER,
COMMAND_DESC_FOOTER,
COMMAND_DESC_CREATE,
COMMAND_DESC_CREATE_ADMIN,
COMMAND_DESC_REMOVE,
COMMAND_DESC_INFO,
COMMAND_DESC_REMOVEALL,
COMMAND_DESC_RELOAD,
COMMAND_DESC_UPDATE,
COMMAND_DESC_LIMITS,
COMMAND_DESC_OPEN,
COMMAND_DESC_CONFIG,
CHANGED_CONFIG_SET,
CHANGED_CONFIG_REMOVED,
CHANGED_CONFIG_ADDED
}

View File

@ -1,29 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.Material;
public class MusicDiscName {
private Material musicDiscMaterial;
private String localizedName;
public MusicDiscName(Material musicDiscMaterial, String localizedName) {
this.musicDiscMaterial = musicDiscMaterial;
this.localizedName = localizedName;
}
/**
* @return Localized Title of the Music Disc
*/
public String getLocalizedName() {
return localizedName;
}
/**
* @return Material of the Music Disc
*/
public Material getMusicDiscMaterial() {
return musicDiscMaterial;
}
}

View File

@ -1,29 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.potion.PotionEffectType;
public class PotionEffectName {
private PotionEffectType effect;
private String localizedName;
public PotionEffectName(PotionEffectType effect, String localizedName) {
this.effect = effect;
this.localizedName = localizedName;
}
/**
* @return Potion Effect linked to the name
*/
public PotionEffectType getEffect() {
return effect;
}
/**
* @return Localized Name of the potion effect
*/
public String getLocalizedName() {
return localizedName;
}
}

View File

@ -1,44 +0,0 @@
package de.epiceric.shopchest.language;
import org.bukkit.potion.PotionType;
public class PotionName {
private String localizedName;
private PotionItemType potionItemType;
private PotionType potionType;
public PotionName(PotionItemType potionItemType, PotionType potionType, String localizedName) {
this.potionItemType = potionItemType;
this.localizedName = localizedName;
this.potionType = potionType;
}
/**
* @return {@link PotionItemType} linked to the Potion name
*/
public PotionItemType getPotionItemType() {
return potionItemType;
}
/**
* @return Potion Type linked to the Potion name
*/
public PotionType getPotionType() {
return potionType;
}
/**
* @return Localized Name of the Potion
*/
public String getLocalizedName() {
return localizedName;
}
public enum PotionItemType {
POTION,
LINGERING_POTION,
SPLASH_POTION,
TIPPED_ARROW
}
}

View File

@ -1,29 +0,0 @@
package de.epiceric.shopchest.language;
import de.epiceric.shopchest.config.Placeholder;
public class Replacement {
private Placeholder placeholder;
private String replacement;
public Replacement(Placeholder placeholder, Object replacement) {
this.placeholder = placeholder;
this.replacement = String.valueOf(replacement);
}
/**
* @return String which will replace the placeholder
*/
public String getReplacement() {
return replacement;
}
/**
* @return Placeholder that will be replaced
*/
public Placeholder getPlaceholder() {
return placeholder;
}
}

View File

@ -1,69 +0,0 @@
package de.epiceric.shopchest.listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.codemc.worldguardwrapper.WorldGuardWrapper;
import org.codemc.worldguardwrapper.region.IWrappedRegion;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.shop.Shop;
import me.wiefferink.areashop.events.notify.DeletedRegionEvent;
import me.wiefferink.areashop.events.notify.ResoldRegionEvent;
import me.wiefferink.areashop.events.notify.SoldRegionEvent;
import me.wiefferink.areashop.events.notify.UnrentedRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
public class AreaShopListener implements Listener {
private ShopChest plugin;
public AreaShopListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler
public void onRegionDeleted(DeletedRegionEvent e) {
if (Config.enableAreaShopIntegration && Config.areashopRemoveShopEvents.contains("DELETE")) {
removeShopsInRegion(e.getRegion());
}
}
@EventHandler
public void onRegionUnrented(UnrentedRegionEvent e) {
if (Config.enableAreaShopIntegration && Config.areashopRemoveShopEvents.contains("UNRENT")) {
removeShopsInRegion(e.getRegion());
}
}
@EventHandler
public void onRegionResold(ResoldRegionEvent e) {
if (Config.enableAreaShopIntegration && Config.areashopRemoveShopEvents.contains("RESELL")) {
removeShopsInRegion(e.getRegion());
}
}
@EventHandler
public void onRegionSold(SoldRegionEvent e) {
if (Config.enableAreaShopIntegration && Config.areashopRemoveShopEvents.contains("SELL")) {
removeShopsInRegion(e.getRegion());
}
}
private void removeShopsInRegion(GeneralRegion generalRegion) {
if (!plugin.hasWorldGuard()) return;
for (Shop shop : plugin.getShopUtils().getShops()) {
if (!shop.getLocation().getWorld().getName().equals(generalRegion.getWorldName())) continue;
for (IWrappedRegion r : WorldGuardWrapper.getInstance().getRegions(shop.getLocation())) {
if (generalRegion.getLowerCaseName().equals(r.getId())) {
plugin.getShopUtils().removeShopById(shop.getID(), true);
break;
}
}
}
}
}

View File

@ -1,91 +0,0 @@
package de.epiceric.shopchest.listeners;
import java.util.Collection;
import java.util.UUID;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.util.BoundingBox;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.shop.Shop;
import world.bentobox.bentobox.api.events.island.IslandBanEvent;
import world.bentobox.bentobox.api.events.island.IslandDeleteChunksEvent;
import world.bentobox.bentobox.api.events.island.IslandDeletedEvent;
import world.bentobox.bentobox.api.events.island.IslandResettedEvent;
import world.bentobox.bentobox.api.events.team.TeamKickEvent;
import world.bentobox.bentobox.api.events.team.TeamLeaveEvent;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.database.objects.IslandDeletion;
public class BentoBoxListener implements Listener {
private ShopChest plugin;
public BentoBoxListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onIslandDeleted(IslandDeletedEvent e) {
deleteShops(e.getDeletedIslandInfo());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onIslandDeleteChunks(IslandDeleteChunksEvent e) {
deleteShops(e.getDeletedIslandInfo());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onIslandResetted(IslandResettedEvent e) {
deleteShops(e.getIsland(), null);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onIslandBan(IslandBanEvent e) {
deleteShops(e.getIsland(), e.getPlayerUUID());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTeamKick(TeamKickEvent e) {
deleteShops(e.getIsland(), e.getPlayerUUID());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTeamLeave(TeamLeaveEvent e) {
deleteShops(e.getIsland(), e.getPlayerUUID());
}
private void deleteShops(IslandDeletion deletedIsland) {
deleteShops(deletedIsland.getWorld(), deletedIsland.getBox(), null);
}
private void deleteShops(Island island, UUID vendorUuid) {
deleteShops(island.getWorld(), island.getBoundingBox(), vendorUuid);
}
private void deleteShops(World world, BoundingBox box, UUID vendorUuid) {
if (!Config.enableBentoBoxIntegration)
return;
Collection<Shop> shops = plugin.getShopUtils().getShops();
for (Shop shop : shops) {
if (!shop.getLocation().getWorld().getName().equals(world.getName())) {
continue;
}
if (vendorUuid != null && !shop.getVendor().getUniqueId().equals(vendorUuid)) {
continue;
}
int x = shop.getLocation().getBlockX();
int z = shop.getLocation().getBlockZ();
if (box.contains(x, 0, z)) {
plugin.getShopUtils().removeShop(shop, true);
}
}
}
}

View File

@ -1,31 +0,0 @@
package de.epiceric.shopchest.listeners;
import java.util.ArrayList;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockExplodeEvent;
import de.epiceric.shopchest.ShopChest;
public class BlockExplodeListener implements Listener {
private ShopChest plugin;
public BlockExplodeListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler
public void onBlockExplode(BlockExplodeEvent e) {
ArrayList<Block> bl = new ArrayList<>(e.blockList());
for (Block b : bl) {
if (b.getType().equals(Material.CHEST) || b.getType().equals(Material.TRAPPED_CHEST)) {
if (plugin.getShopUtils().isShop(b.getLocation())) e.blockList().remove(b);
}
}
}
}

View File

@ -1,248 +0,0 @@
package de.epiceric.shopchest.listeners;
import java.util.ArrayList;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest;
import org.bukkit.block.data.type.Chest.Type;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.InventoryHolder;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.event.ShopExtendEvent;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.ItemUtils;
import de.epiceric.shopchest.utils.Permissions;
import de.epiceric.shopchest.utils.ShopUtils;
import de.epiceric.shopchest.utils.Utils;
import net.milkbowl.vault.economy.EconomyResponse;
public class ChestProtectListener implements Listener {
private ShopChest plugin;
private ShopUtils shopUtils;
public ChestProtectListener(ShopChest plugin) {
this.plugin = plugin;
this.shopUtils = plugin.getShopUtils();
}
private void remove(final Shop shop, final Block b, final Player p) {
if (shop.getInventoryHolder() instanceof DoubleChest) {
DoubleChest dc = (DoubleChest) shop.getInventoryHolder();
final Chest l = (Chest) dc.getLeftSide();
final Chest r = (Chest) dc.getRightSide();
Location loc = (b.getLocation().equals(l.getLocation()) ? r.getLocation() : l.getLocation());
final Shop newShop = new Shop(shop.getID(), plugin, shop.getVendor(), shop.getProduct(), loc, shop.getBuyPrice(), shop.getSellPrice(), shop.getShopType());
shopUtils.removeShop(shop, true, new Callback<Void>(plugin) {
@Override
public void onResult(Void result) {
newShop.create(true);
shopUtils.addShop(newShop, true);
}
});
} else {
double creationPrice = shop.getShopType() == ShopType.ADMIN ? Config.shopCreationPriceAdmin : Config.shopCreationPriceNormal;
if (creationPrice > 0 && Config.refundShopCreation && p.getUniqueId().equals(shop.getVendor().getUniqueId())) {
EconomyResponse r = plugin.getEconomy().depositPlayer(p, shop.getLocation().getWorld().getName(), creationPrice);
if (!r.transactionSuccess()) {
plugin.debug("Economy transaction failed: " + r.errorMessage);
p.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED,
new Replacement(Placeholder.ERROR, r.errorMessage)));
p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND,
new Replacement(Placeholder.CREATION_PRICE, 0)));
} else {
p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND,
new Replacement(Placeholder.CREATION_PRICE, creationPrice)));
}
} else {
p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED));
}
shopUtils.removeShop(shop, true);
plugin.debug(String.format("%s broke %s's shop (#%d)", p.getName(), shop.getVendor().getName(), shop.getID()));
}
}
@EventHandler(ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent e) {
final Block b = e.getBlock();
if (shopUtils.isShop(b.getLocation())) {
final Shop shop = shopUtils.getShop(e.getBlock().getLocation());
Player p = e.getPlayer();
if (p.isSneaking() && Utils.hasAxeInHand(p)) {
plugin.debug(String.format("%s tries to break %s's shop (#%d)", p.getName(), shop.getVendor().getName(), shop.getID()));
if (shop.getShopType() == Shop.ShopType.ADMIN) {
if (p.hasPermission(Permissions.REMOVE_ADMIN)) {
remove(shop, b, p);
return;
}
} else {
if (shop.getVendor().getUniqueId().equals(p.getUniqueId()) || p.hasPermission(Permissions.REMOVE_OTHER)) {
remove(shop, b, p);
return;
}
}
}
if (shop.getItem() != null) {
shop.getItem().resetForPlayer(p);
}
e.setCancelled(true);
e.getPlayer().sendMessage(LanguageUtils.getMessage(Message.CANNOT_BREAK_SHOP));
}
}
@EventHandler(ignoreCancelled = true)
public void onEntityExplode(EntityExplodeEvent e) {
ArrayList<Block> bl = new ArrayList<>(e.blockList());
for (Block b : bl) {
if (b.getType().equals(Material.CHEST) || b.getType().equals(Material.TRAPPED_CHEST)) {
if (shopUtils.isShop(b.getLocation())) e.blockList().remove(b);
}
}
}
@EventHandler(ignoreCancelled = true)
public void onBlockPlace(BlockPlaceEvent e) {
final Player p = e.getPlayer();
final Block b = e.getBlockPlaced();
if (!b.getType().equals(Material.CHEST) && !b.getType().equals(Material.TRAPPED_CHEST)) {
return;
}
Chest c = (Chest) b.getState();
Block b2;
// Can't use Utils::getChestLocations since inventory holder
// has not been updated yet in this event (for 1.13+)
if (Utils.getMajorVersion() < 13) {
InventoryHolder ih = c.getInventory().getHolder();
if (!(ih instanceof DoubleChest)) {
return;
}
DoubleChest dc = (DoubleChest) ih;
Chest l = (Chest) dc.getLeftSide();
Chest r = (Chest) dc.getRightSide();
if (b.getLocation().equals(l.getLocation())) {
b2 = r.getBlock();
} else {
b2 = l.getBlock();
}
} else {
org.bukkit.block.data.type.Chest data = (org.bukkit.block.data.type.Chest) c.getBlockData();
if (data.getType() == Type.SINGLE) {
return;
}
BlockFace neighborFacing;
switch (data.getFacing()) {
case NORTH:
neighborFacing = data.getType() == Type.LEFT ? BlockFace.EAST : BlockFace.WEST;
break;
case EAST:
neighborFacing = data.getType() == Type.LEFT ? BlockFace.SOUTH : BlockFace.NORTH;
break;
case SOUTH:
neighborFacing = data.getType() == Type.LEFT ? BlockFace.WEST : BlockFace.EAST;
break;
case WEST:
neighborFacing = data.getType() == Type.LEFT ? BlockFace.NORTH : BlockFace.SOUTH;
break;
default:
neighborFacing = null;
}
b2 = b.getRelative(neighborFacing);
}
final Shop shop = shopUtils.getShop(b2.getLocation());
if (shop == null)
return;
plugin.debug(String.format("%s tries to extend %s's shop (#%d)", p.getName(), shop.getVendor().getName(), shop.getID()));
ShopExtendEvent event = new ShopExtendEvent(p, shop, b.getLocation());
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled() && !p.hasPermission(Permissions.EXTEND_PROTECTED)) {
e.setCancelled(true);
p.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_EXTEND_PROTECTED));
return;
}
if (!p.getUniqueId().equals(shop.getVendor().getUniqueId()) && !p.hasPermission(Permissions.EXTEND_OTHER)) {
e.setCancelled(true);
p.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_EXTEND_OTHERS));
return;
}
if (!ItemUtils.isAir(b.getRelative(BlockFace.UP).getType())) {
e.setCancelled(true);
p.sendMessage(LanguageUtils.getMessage(Message.CHEST_BLOCKED));
return;
}
final Shop newShop = new Shop(shop.getID(), plugin, shop.getVendor(), shop.getProduct(), shop.getLocation(), shop.getBuyPrice(), shop.getSellPrice(), shop.getShopType());
shopUtils.removeShop(shop, true, new Callback<Void>(plugin) {
@Override
public void onResult(Void result) {
newShop.create(true);
shopUtils.addShop(newShop, true);
plugin.debug(String.format("%s extended %s's shop (#%d)", p.getName(), shop.getVendor().getName(), shop.getID()));
}
});
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onItemMove(InventoryMoveItemEvent e) {
if ((e.getSource().getType().equals(InventoryType.CHEST)) && (!e.getInitiator().getType().equals(InventoryType.PLAYER))) {
if (e.getSource().getHolder() instanceof DoubleChest) {
DoubleChest dc = (DoubleChest) e.getSource().getHolder();
Chest r = (Chest) dc.getRightSide();
Chest l = (Chest) dc.getLeftSide();
if (shopUtils.isShop(r.getLocation()) || shopUtils.isShop(l.getLocation())) e.setCancelled(true);
} else if (e.getSource().getHolder() instanceof Chest) {
Chest c = (Chest) e.getSource().getHolder();
if (shopUtils.isShop(c.getLocation())) e.setCancelled(true);
}
}
}
}

View File

@ -1,179 +0,0 @@
package de.epiceric.shopchest.listeners;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockMultiPlaceEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.utils.ClickType;
import de.epiceric.shopchest.utils.ClickType.SelectClickType;
public class CreativeModeListener implements Listener {
private ShopChest plugin;
public CreativeModeListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onInventoryClick(InventoryClickEvent e) {
HumanEntity entity = e.getWhoClicked();
if (!(entity instanceof Player))
return;
Player p = (Player) entity;
ClickType clickType = ClickType.getPlayerClickType(p);
if (clickType instanceof SelectClickType) {
e.setCancelled(true);
if (e.getCursor() == null || e.getCursor().getType() == Material.AIR)
return;
ClickType.removePlayerClickType(p);
((SelectClickType) clickType).setItem(e.getCursor());
p.closeInventory();
p.sendMessage(LanguageUtils.getMessage(Message.ITEM_SELECTED,
new Replacement(Placeholder.ITEM_NAME, LanguageUtils.getItemName(e.getCursor()))));
plugin.getShopCommand().createShopAfterSelected(p, (SelectClickType) clickType);
}
}
@EventHandler
public void onPlayerCloseInventory(InventoryCloseEvent e) {
HumanEntity entity = e.getPlayer();
if (!(entity instanceof Player))
return;
Player p = (Player) entity;
ClickType clickType = ClickType.getPlayerClickType(p);
if (!(clickType instanceof SelectClickType))
return;
ClickType.removePlayerClickType(p);
p.sendMessage(LanguageUtils.getMessage(Message.CREATION_CANCELLED));
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent e) {
// Reset game mode on quit if SelectClickType is set
Player p = e.getPlayer();
ClickType.removePlayerClickType(p);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onInventoryDrag(InventoryDragEvent e) {
// Cancel any inventory drags if SelectClickType is set
HumanEntity entity = e.getWhoClicked();
if (!(entity instanceof Player))
return;
ClickType clickType = ClickType.getPlayerClickType((Player) entity);
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onInventoryMove(InventoryMoveItemEvent e) {
// Cancel any inventory movement if SelectClickType is set
if (e.getSource().getHolder() instanceof Player) {
Player p = (Player) e.getSource().getHolder();
ClickType clickType = ClickType.getPlayerClickType(p);
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerPickup(PlayerPickupItemEvent e) {
// Cancel any item pickups if SelectClickType is set
ClickType clickType = ClickType.getPlayerClickType(e.getPlayer());
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockBreak(BlockBreakEvent e) {
// Cancel any block breaks if SelectClickType is set
ClickType clickType = ClickType.getPlayerClickType(e.getPlayer());
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockPlace(BlockPlaceEvent e) {
// Cancel any block places if SelectClickType is set
ClickType clickType = ClickType.getPlayerClickType(e.getPlayer());
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockMultiPlace(BlockMultiPlaceEvent e) {
// Cancel any block places if SelectClickType is set
ClickType clickType = ClickType.getPlayerClickType(e.getPlayer());
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerInteract(PlayerInteractEvent e) {
// Cancel any interactions if SelectClickType is set
ClickType clickType = ClickType.getPlayerClickType(e.getPlayer());
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent e) {
// Cancel any entity interactions if SelectClickType is set
ClickType clickType = ClickType.getPlayerClickType(e.getPlayer());
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerDamageEntity(EntityDamageByEntityEvent e) {
// Cancel any entity damaging if SelectClickType is set
Entity entity = e.getDamager();
if (!(entity instanceof Player))
return;
ClickType clickType = ClickType.getPlayerClickType((Player) entity);
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerMove(PlayerMoveEvent e) {
// Cancel any player movement if SelectClickType is set
ClickType clickType = ClickType.getPlayerClickType(e.getPlayer());
if (clickType instanceof SelectClickType)
e.setCancelled(true);
}
}

View File

@ -1,63 +0,0 @@
package de.epiceric.shopchest.listeners;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.Permissions;
import de.epiceric.shopchest.utils.Utils;
public class NotifyPlayerOnJoinListener implements Listener {
private ShopChest plugin;
public NotifyPlayerOnJoinListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
final Player p = e.getPlayer();
if (plugin.isUpdateNeeded() && Config.enableUpdateChecker) {
if (p.hasPermission(Permissions.UPDATE_NOTIFICATION)) {
Utils.sendUpdateMessage(plugin, p);
}
}
plugin.getShopDatabase().getLastLogout(p, new Callback<Long>(plugin) {
@Override
public void onResult(Long result) {
if (result < 0) {
// No logout saved, probably first time joining.
return;
}
plugin.getShopDatabase().getRevenue(p, result, new Callback<Double>(plugin) {
@Override
public void onResult(Double result) {
if (result != 0) {
p.sendMessage(LanguageUtils.getMessage(Message.REVENUE_WHILE_OFFLINE,
new Replacement(Placeholder.REVENUE, String.valueOf(result))));
}
}
});
}
});
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent e) {
plugin.getShopDatabase().logLogout(e.getPlayer(), null);
}
}

View File

@ -1,157 +0,0 @@
package de.epiceric.shopchest.listeners;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockGrowEvent;
import org.bukkit.event.block.BlockMultiPlaceEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockSpreadEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.world.StructureGrowEvent;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.utils.ShopUtils;
public class ShopItemListener implements Listener {
private ShopUtils shopUtils;
public ShopItemListener(ShopChest plugin) {
this.shopUtils = plugin.getShopUtils();
}
@EventHandler(priority = EventPriority.HIGH)
public void onBlockPlace(BlockPlaceEvent e) {
Block b = e.getBlockPlaced();
Block below = b.getRelative(BlockFace.DOWN);
if (shopUtils.isShop(below.getLocation())) {
Shop shop = shopUtils.getShop(below.getLocation());
if (shop.getItem() != null) {
shop.getItem().resetForPlayer(e.getPlayer());
}
e.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onMultiBlockPlace(BlockMultiPlaceEvent e) {
for (BlockState blockState : e.getReplacedBlockStates()) {
Block below = blockState.getBlock().getRelative(BlockFace.DOWN);
if (shopUtils.isShop(below.getLocation())) {
Shop shop = shopUtils.getShop(below.getLocation());
if (shop.getItem() != null) {
shop.getItem().resetForPlayer(e.getPlayer());
}
e.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPistonExtend(BlockPistonExtendEvent e) {
// If the piston would only move itself
Block airAfterPiston = e.getBlock().getRelative(e.getDirection());
Block belowAir = airAfterPiston.getRelative(BlockFace.DOWN);
if (shopUtils.isShop(belowAir.getLocation())) {
e.setCancelled(true);
return;
}
for (Block b : e.getBlocks()) {
Block newBlock = b.getRelative(e.getDirection());
Block belowNewBlock = newBlock.getRelative(BlockFace.DOWN);
if (shopUtils.isShop(belowNewBlock.getLocation())) e.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPistonRetract(BlockPistonRetractEvent e) {
for (Block b : e.getBlocks()) {
Block newBlock = b.getRelative(e.getDirection());
Block belowNewBlock = newBlock.getRelative(BlockFace.DOWN);
if (shopUtils.isShop(belowNewBlock.getLocation())) {
e.setCancelled(true);
for (Player p : Bukkit.getOnlinePlayers()) {
Shop shop = shopUtils.getShop(belowNewBlock.getLocation());
if (shop.getItem() != null) {
shop.getItem().resetForPlayer(p);
}
}
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onLiquidFlow(BlockFromToEvent e) {
Block b = e.getToBlock();
Block below = b.getRelative(BlockFace.DOWN);
if (shopUtils.isShop(below.getLocation())) e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGH)
public void onBucketEmpty(PlayerBucketEmptyEvent e) {
Block clicked = e.getBlockClicked();
Block underWater = clicked.getRelative(BlockFace.DOWN).getRelative(e.getBlockFace());
if (shopUtils.isShop(clicked.getLocation())) {
if (e.getBucket() == Material.LAVA_BUCKET) {
Shop shop = shopUtils.getShop(clicked.getLocation());
if (shop.getItem() != null) {
shop.getItem().resetForPlayer(e.getPlayer());
}
}
} else if (shopUtils.isShop(underWater.getLocation())) {
if (e.getBucket() == Material.LAVA_BUCKET) {
Shop shop = shopUtils.getShop(underWater.getLocation());
if (shop.getItem() != null) {
shop.getItem().resetForPlayer(e.getPlayer());
}
}
} else {
return;
}
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGH)
public void onStructureGrow(StructureGrowEvent e) {
for (BlockState state : e.getBlocks()) {
Block newBlock = state.getBlock();
if (shopUtils.isShop(newBlock.getLocation()) || shopUtils.isShop(newBlock.getRelative(BlockFace.DOWN).getLocation())) {
e.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onBlockGrow(BlockGrowEvent e) {
Block newBlock = e.getNewState().getBlock();
if (shopUtils.isShop(newBlock.getLocation()) || shopUtils.isShop(newBlock.getRelative(BlockFace.DOWN).getLocation())) {
e.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onBlockSpread(BlockSpreadEvent e) {
Block newBlock = e.getNewState().getBlock();
if (shopUtils.isShop(newBlock.getLocation()) || shopUtils.isShop(newBlock.getRelative(BlockFace.DOWN).getLocation())) {
e.setCancelled(true);
}
}
}

View File

@ -1,153 +0,0 @@
package de.epiceric.shopchest.listeners;
import java.util.HashSet;
import java.util.Set;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.scheduler.BukkitRunnable;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.utils.Callback;
public class ShopUpdateListener implements Listener {
private final ShopChest plugin;
private final Set<Chunk> newLoadedChunks = new HashSet<>();
public ShopUpdateListener(ShopChest plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onInventoryUpdate(InventoryMoveItemEvent e) {
if (!plugin.getHologramFormat().isDynamic()) return;
Location loc = null;
if (e.getSource().getHolder() instanceof Chest) {
loc = ((Chest) e.getSource().getHolder()).getLocation();
} else if (e.getSource().getHolder() instanceof DoubleChest) {
loc = ((DoubleChest) e.getSource().getHolder()).getLocation();
} else if (e.getDestination().getHolder() instanceof Chest) {
loc = ((Chest) e.getDestination().getHolder()).getLocation();
} else if (e.getDestination().getHolder() instanceof DoubleChest) {
loc = ((DoubleChest) e.getDestination().getHolder()).getLocation();
}
if (loc != null) {
Shop shop = plugin.getShopUtils().getShop(loc);
if (shop != null) shop.updateHologramText();
}
}
@EventHandler
public void onPlayerLeave(PlayerQuitEvent e) {
// If done without delay, Bukkit#getOnlinePlayers() would still
// contain the player even though he left, so the shop updater
// would show the shop again.
new BukkitRunnable(){
@Override
public void run() {
for (Shop shop : plugin.getShopUtils().getShops()) {
if (shop.hasItem()) {
shop.getItem().resetVisible(e.getPlayer());
}
if (shop.hasHologram()) {
shop.getHologram().resetVisible(e.getPlayer());
}
}
plugin.getShopUtils().resetPlayerLocation(e.getPlayer());
}
}.runTaskLater(plugin, 1L);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent e) {
Location from = e.getFrom();
Location to = e.getTo();
final Player p = e.getPlayer();
// Wait till the chunk should have loaded on the client
if (!from.getWorld().getName().equals(to.getWorld().getName())
|| from.getChunk().getX() != to.getChunk().getX()
|| from.getChunk().getZ() != to.getChunk().getZ()) {
new BukkitRunnable() {
@Override
public void run() {
plugin.getUpdater().queue(() -> {
if (p.isOnline()) {
for (Shop shop : plugin.getShopUtils().getShops()) {
if (shop.hasItem()) {
shop.getItem().hidePlayer(p);
}
if (shop.hasHologram()) {
shop.getHologram().hidePlayer(p);
}
}
plugin.getShopUtils().resetPlayerLocation(p);
}
});
plugin.getUpdater().updateShops(p);
}
}.runTaskLater(plugin, 15L);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent e) {
plugin.getUpdater().updateShops(e.getPlayer());
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent e) {
if (!plugin.getShopDatabase().isInitialized()) {
return;
}
// Wait 10 ticks after first event is triggered, so that multiple
// chunk loads can be handled at the same time without having to
// send a database request for each chunk.
if (newLoadedChunks.isEmpty()) {
new BukkitRunnable(){
@Override
public void run() {
int chunkCount = newLoadedChunks.size();
plugin.getShopUtils().loadShops(newLoadedChunks.toArray(new Chunk[chunkCount]), new Callback<Integer>(plugin) {
@Override
public void onResult(Integer result) {
if (result == 0) {
return;
}
plugin.debug("Loaded " + result + " shops in " + chunkCount + " chunks");
}
@Override
public void onError(Throwable throwable) {
// Database connection probably failed => disable plugin to prevent more errors
plugin.getLogger().severe("Failed to load shops in newly loaded chunks");
plugin.debug("Failed to load shops in newly loaded chunks");
if (throwable != null) plugin.debug(throwable);
}
});
newLoadedChunks.clear();
}
}.runTaskLater(plugin, 10L);
}
newLoadedChunks.add(e.getChunk());
}
}

View File

@ -1,89 +0,0 @@
package de.epiceric.shopchest.listeners;
import java.util.Optional;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.codemc.worldguardwrapper.WorldGuardWrapper;
import org.codemc.worldguardwrapper.event.WrappedUseBlockEvent;
import org.codemc.worldguardwrapper.flag.IWrappedFlag;
import org.codemc.worldguardwrapper.flag.WrappedState;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.utils.ClickType;
import de.epiceric.shopchest.utils.ClickType.EnumClickType;
public class WorldGuardListener implements Listener {
private ShopChest plugin;
public WorldGuardListener(ShopChest plugin) {
this.plugin = plugin;
}
private boolean isAllowed(Player player, Location location) {
ClickType clickType = ClickType.getPlayerClickType(player);
if (clickType != null && clickType.getClickType() == EnumClickType.CREATE) {
// If the player is about to create a shop, but does not have
// access to the chest, show the 'permission denied' message
// (if not previously set to allowed by another plugin).
// If the player can open the chest, that message should be hidden.
WorldGuardWrapper wgWrapper = WorldGuardWrapper.getInstance();
Optional<IWrappedFlag<WrappedState>> flag = wgWrapper.getFlag("chest-access", WrappedState.class);
if (!flag.isPresent()) plugin.debug("WorldGuard flag 'chest-access' is not present!");
WrappedState state = flag.map(f -> wgWrapper.queryFlag(player, location, f).orElse(WrappedState.DENY)).orElse(WrappedState.DENY);
return state == WrappedState.ALLOW;
}
Shop shop = plugin.getShopUtils().getShop(location);
if (shop != null) {
// Don't show 'permission denied' messages for any kind of
// shop interaction even if block interaction is not
// allowed in the region.
return true;
}
return false;
}
@EventHandler(priority = EventPriority.LOW)
public void onUseBlock(WrappedUseBlockEvent event) {
if (Config.enableWorldGuardIntegration) {
Player player = event.getPlayer();
if (event.getOriginalEvent() instanceof PlayerInteractEvent) {
Block block = event.getBlocks().get(0);
Material type = block.getType();
if (type == Material.CHEST || type == Material.TRAPPED_CHEST) {
if (isAllowed(player, block.getLocation())) {
event.setResult(Result.ALLOW);
}
}
} else if (event.getOriginalEvent() instanceof InventoryOpenEvent) {
InventoryOpenEvent orig = (InventoryOpenEvent) event.getOriginalEvent();
if (orig.getInventory().getHolder() instanceof Chest) {
if (isAllowed(player, ((Chest) orig.getInventory().getHolder()).getLocation())) {
event.setResult(Result.ALLOW);
}
}
}
}
}
}

View File

@ -1,159 +0,0 @@
package de.epiceric.shopchest.nms;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.utils.Utils;
public class ArmorStandWrapper {
private final NMSClassResolver nmsClassResolver = new NMSClassResolver();
private final Class<?> packetDataSerializerClass = nmsClassResolver.resolveSilent("network.PacketDataSerializer");
private final Class<?> packetPlayOutEntityDestroyClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityDestroy");
private final Class<?> packetPlayOutEntityMetadataClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityMetadata");
private final Class<?> packetPlayOutEntityTeleportClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityTeleport");
private final Class<?> dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher");
private final UUID uuid = UUID.randomUUID();
private final int entityId;
private ShopChest plugin;
private Location location;
private String customName;
public ArmorStandWrapper(ShopChest plugin, Location location, String customName) {
this.plugin = plugin;
this.location = location;
this.customName = customName;
this.entityId = Utils.getFreeEntityId();
}
public void setVisible(Player player, boolean visible) {
try {
if (visible) {
Object dataWatcher = Utils.createDataWatcher(customName, null);
Utils.sendPacket(plugin, Utils.createPacketSpawnEntity(plugin, entityId, uuid, location, EntityType.ARMOR_STAND), player);
Utils.sendPacket(plugin, packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class)
.newInstance(entityId, dataWatcher, true), player);
} else if (entityId != -1) {
Utils.sendPacket(plugin, packetPlayOutEntityDestroyClass.getConstructor(int[].class).newInstance((Object) new int[]{entityId}), player);
}
} catch (ReflectiveOperationException e) {
plugin.getLogger().severe("Could not change hologram visibility");
plugin.debug("Could not change armor stand visibility");
plugin.debug(e);
}
}
public void setLocation(Location location) {
this.location = location;
double y = location.getY() + (Utils.getServerVersion().equals("v1_8_R1") ? 0 : 1.975);
Object packet;
try {
if (Utils.getMajorVersion() >= 17) {
// Empty constructor does not exist anymore in 1.17+ so create packet via serializer
Class<?> byteBufClass = Class.forName("io.netty.buffer.ByteBuf");
Class<?> unpooledClass = Class.forName("io.netty.buffer.Unpooled");
Object buffer = unpooledClass.getMethod("buffer").invoke(null);
Object serializer = packetDataSerializerClass.getConstructor(byteBufClass).newInstance(buffer);
Method d = packetDataSerializerClass.getMethod("d", int.class);
Method writeDouble = packetDataSerializerClass.getMethod("writeDouble", double.class);
Method writeByte = packetDataSerializerClass.getMethod("writeByte", int.class);
Method writeBoolean = packetDataSerializerClass.getMethod("writeBoolean", boolean.class);
d.invoke(serializer, getEntityId());
writeDouble.invoke(serializer, location.getX());
writeDouble.invoke(serializer, y);
writeDouble.invoke(serializer, location.getZ());
writeByte.invoke(serializer, 0);
writeByte.invoke(serializer, 0);
writeBoolean.invoke(serializer, false);
packet = packetPlayOutEntityTeleportClass.getConstructor(packetDataSerializerClass).newInstance(serializer);
} else {
packet = packetPlayOutEntityTeleportClass.getConstructor().newInstance();
Field[] fields = packetPlayOutEntityTeleportClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
}
boolean isPre9 = Utils.getMajorVersion() < 9;
fields[0].set(packet, entityId);
if (isPre9) {
fields[1].set(packet, (int)(location.getX() * 32));
fields[2].set(packet, (int)(y * 32));
fields[3].set(packet, (int)(location.getZ() * 32));
} else {
fields[1].set(packet, location.getX());
fields[2].set(packet, y);
fields[3].set(packet, location.getZ());
}
fields[4].set(packet, (byte) 0);
fields[5].set(packet, (byte) 0);
fields[6].set(packet, true);
}
if (packet == null) {
plugin.getLogger().severe("Could not set hologram location");
plugin.debug("Could not set armor stand location: Packet is null");
return;
}
for (Player player : location.getWorld().getPlayers()) {
Utils.sendPacket(plugin, packet, player);
}
} catch (ReflectiveOperationException e) {
plugin.getLogger().severe("Could not set hologram location");
plugin.debug("Could not set armor stand location");
plugin.debug(e);
}
}
public void setCustomName(String customName) {
this.customName = customName;
Object dataWatcher = Utils.createDataWatcher(customName, null);
try {
Object packet = packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class)
.newInstance(entityId, dataWatcher, true);
for (Player player : location.getWorld().getPlayers()) {
Utils.sendPacket(plugin, packet, player);
}
} catch (ReflectiveOperationException e) {
plugin.getLogger().severe("Could not set hologram text");
plugin.debug("Could not set armor stand custom name");
plugin.debug(e);
}
}
public void remove() {
for (Player player : location.getWorld().getPlayers()) {
setVisible(player, false);
}
}
public int getEntityId() {
return entityId;
}
public UUID getUuid() {
return uuid;
}
public Location getLocation() {
return location.clone();
}
public String getCustomName() {
return customName;
}
}

View File

@ -1,92 +0,0 @@
package de.epiceric.shopchest.nms;
import java.lang.reflect.InvocationTargetException;
import org.bukkit.inventory.ItemStack;
import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
import de.epiceric.shopchest.ShopChest;
// For versions below 1.9.4, since Bukkit's BookMeta
// didn't have generations in those versions
public class CustomBookMeta {
private static final OBCClassResolver obcClassResolver = new OBCClassResolver();
public enum Generation {
ORIGINAL,
COPY_OF_ORIGINAL,
COPY_OF_COPY,
TATTERED
}
public static Generation getGeneration(ItemStack book) {
try {
Class<?> craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack");
if (craftItemStackClass == null) {
ShopChest.getInstance().debug("Failed to get NBTGeneration: Could not find CraftItemStack class");
return null;
}
Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, book);
Object nbtTagCompound = nmsStack.getClass().getMethod("getTag").invoke(nmsStack);
if (nbtTagCompound == null) {
ShopChest.getInstance().debug("Failed to get NBTGeneration: getTag returned null");
return null;
}
Object generationObject = nbtTagCompound.getClass().getMethod("getInt", String.class).invoke(nbtTagCompound, "generation");
if (generationObject == null) {
ShopChest.getInstance().debug("Failed to get NBTGeneration: getInt returned null");
return null;
}
if (generationObject instanceof Integer) {
int generation = (Integer) generationObject;
if (generation > 3) generation = 3;
return Generation.values()[generation];
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
ShopChest.getInstance().getLogger().severe("Failed to get NBTEntityID with reflection");
ShopChest.getInstance().debug("Failed to get NBTEntityID with reflection");
ShopChest.getInstance().debug(e);
}
return null;
}
public static void setGeneration(ItemStack book, Generation generation) {
try {
Class<?> craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack");
if (craftItemStackClass == null) {
ShopChest.getInstance().debug("Failed to get NBTGeneration: Could not find CraftItemStack class");
return;
}
Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, book);
Object nbtTagCompound = nmsStack.getClass().getMethod("getTag").invoke(nmsStack);
if (nbtTagCompound == null) {
ShopChest.getInstance().debug("Failed to get NBTGeneration: getTag returned null");
return;
}
nbtTagCompound.getClass().getMethod("setInt", String.class, int.class)
.invoke(nbtTagCompound, "generation", generation.ordinal());
nmsStack.getClass().getMethod("setTag", nbtTagCompound.getClass()).invoke(nmsStack, nbtTagCompound);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
ShopChest.getInstance().getLogger().severe("Failed to get NBTEntityID with reflection");
ShopChest.getInstance().debug("Failed to get NBTEntityID with reflection");
ShopChest.getInstance().debug(e);
}
}
}

View File

@ -1,253 +0,0 @@
package de.epiceric.shopchest.nms;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
public class Hologram {
// concurrent since update task is in async thread
private final Set<UUID> viewers = ConcurrentHashMap.newKeySet();
private final List<ArmorStandWrapper> wrappers = new ArrayList<>();
private final Location location;
private final ShopChest plugin;
private boolean exists;
public Hologram(ShopChest plugin, String[] lines, Location location) {
this.plugin = plugin;
this.location = location;
for (int i = 0; i < lines.length; i++) {
addLine(i, lines[i]);
}
this.exists = true;
}
/**
* @return Location of the hologram
*/
public Location getLocation() {
return location.clone();
}
/**
* @return Whether the hologram exists and is not dead
*/
public boolean exists() {
return exists;
}
/**
* @param armorStand Armor stand to check
* @return Whether the given armor stand is part of the hologram
*/
public boolean contains(ArmorStand armorStand) {
for (ArmorStandWrapper wrapper : wrappers) {
if (armorStand.getUniqueId().equals(wrapper.getUuid())) {
return true;
}
}
return false;
}
/**
* @return A list of {@link ArmorStandWrapper}s of this hologram
*/
public List<ArmorStandWrapper> getArmorStandWrappers() {
return wrappers;
}
/**
* @param p Player to check
* @return Whether the hologram is visible to the player
*/
public boolean isVisible(Player p) {
return viewers.contains(p.getUniqueId());
}
/**
* @param p Player to which the hologram should be shown
*/
public void showPlayer(Player p) {
showPlayer(p, false);
}
/**
* @param p Player to which the hologram should be shown
* @param force Whether to force showing the hologram
*/
public void showPlayer(Player p, boolean force) {
if (viewers.add(p.getUniqueId()) || force) {
togglePlayer(p, true);
}
}
/**
* @param p Player from which the hologram should be hidden
*/
public void hidePlayer(Player p) {
hidePlayer(p, false);
}
/**
* @param p Player from which the hologram should be hidden
* @param force Whether to force hiding the hologram
*/
public void hidePlayer(Player p, boolean force) {
if (viewers.remove(p.getUniqueId()) || force) {
togglePlayer(p, false);
}
}
/**
* <p>Removes the hologram.</p>
*
* Hologram will be hidden from all players and all
* ArmorStand entities will be killed.
*/
public void remove() {
viewers.clear();
for (ArmorStandWrapper wrapper : wrappers) {
wrapper.remove();
}
wrappers.clear();
exists = false;
}
/**
* Remove the player from the list of viewers. The hologram is
* then counted as hidden, but no packets are sent to the player.
* @param p Player whose visibility status will be reset
*/
public void resetVisible(Player p) {
viewers.remove(p.getUniqueId());
}
private void togglePlayer(Player p, boolean visible) {
for (ArmorStandWrapper wrapper : wrappers) {
wrapper.setVisible(p, visible);
}
}
/**
* Get all hologram lines
*
* @return Hologram lines
*/
public String[] getLines() {
List<String> lines = new ArrayList<>();
for (ArmorStandWrapper wrapper : wrappers) {
lines.add(wrapper.getCustomName());
}
return lines.toArray(new String[lines.size()]);
}
/**
* Add a line
*
* @param line where to insert
* @param text text to display
*/
public void addLine(int line, String text) {
addLine(line, text, false);
}
private void addLine(int line, String text, boolean forceUpdateLine) {
if (text == null || text.isEmpty()) return;
if (line >= wrappers.size()) {
line = wrappers.size();
}
text = ChatColor.translateAlternateColorCodes('&', text);
if (Config.hologramFixedBottom) {
for (int i = 0; i < line; i++) {
ArmorStandWrapper wrapper = wrappers.get(i);
wrapper.setLocation(wrapper.getLocation().add(0, 0.25, 0));
}
} else {
for (int i = line; i < wrappers.size(); i++) {
ArmorStandWrapper wrapper = wrappers.get(i);
wrapper.setLocation(wrapper.getLocation().subtract(0, 0.25, 0));
}
}
Location loc = getLocation();
if (!Config.hologramFixedBottom) {
loc.subtract(0, line * 0.25, 0);
}
ArmorStandWrapper wrapper = new ArmorStandWrapper(plugin, loc, text);
wrappers.add(line, wrapper);
if (forceUpdateLine) {
for (Player player : location.getWorld().getPlayers()) {
if (viewers.contains(player.getUniqueId())) {
wrapper.setVisible(player, true);
}
}
}
}
/**
* Set a line
*
* @param line index to change
* @param text text to display
*/
public void setLine(int line, String text) {
if (text == null ||text.isEmpty()) {
removeLine(line);
return;
}
text = ChatColor.translateAlternateColorCodes('&', text);
if (line >= wrappers.size()) {
addLine(line, text, true);
return;
}
wrappers.get(line).setCustomName(text);
}
/**
* Remove a line
*
* @param line index to remove
*/
public void removeLine(int line) {
if (line < wrappers.size()) {
if (Config.hologramFixedBottom) {
for (int i = 0; i < line; i++) {
ArmorStandWrapper wrapper = wrappers.get(i);
wrapper.setLocation(wrapper.getLocation().subtract(0, 0.25, 0));
}
} else {
for (int i = line + 1; i < wrappers.size(); i++) {
ArmorStandWrapper wrapper = wrappers.get(i);
wrapper.setLocation(wrapper.getLocation().add(0, 0.25, 0));
}
}
wrappers.get(line).remove();
wrappers.remove(line);
}
}
}

View File

@ -1,252 +0,0 @@
package de.epiceric.shopchest.nms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.inventivetalent.reflection.resolver.FieldResolver;
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.utils.Utils;
public class JsonBuilder {
public static class Part {
private String value;
public Part() {
this("", true);
}
public Part(Object value) {
this(value, value instanceof CharSequence);
}
public Part(Object value, boolean appendQuotes) {
if (appendQuotes) {
this.value = "\"" + value + "\"";
} else {
this.value = String.valueOf(value);
}
}
@Override
public String toString() {
return value;
}
public PartArray toArray() {
return new PartArray(this);
}
public PartMap toMap() {
PartMap map = new PartMap();
map.setValue("text", new Part());
map.setValue("extra", toArray());
return map;
}
}
public static class PartMap extends Part {
private Map<String, Part> values = new HashMap<>();
public PartMap() {
}
public PartMap(Map<String, Part> values) {
this.values.putAll(values);
}
public void setValue(String key, Part value) {
values.put(key, value);
}
public void removeValue(String key) {
values.remove(key);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(",", "{", "}");
values.forEach((key, value) -> joiner.add("\"" + key + "\":" + value.toString()));
return joiner.toString();
}
@Override
public PartMap toMap() {
return this;
}
}
public static class PartArray extends Part {
private List<Part> parts = new ArrayList<>();
public PartArray(Part... parts) {
this.parts.addAll(Arrays.asList(parts));
}
public void addPart(Part part) {
parts.add(part);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(",", "[", "]");
parts.forEach(part -> joiner.add(part.toString()));
return joiner.toString();
}
@Override
public PartArray toArray() {
return this;
}
}
private static final Pattern PART_PATTERN = Pattern.compile("((§(?:[a-fA-Fk-oK-OrR0-9]|#[a-fA-F0-9]{6}))+)([^§]*)");
private static final Pattern HEX_PATTERN = Pattern.compile("(§[a-fA-F0-9]){6}");
private Part rootPart;
private ShopChest plugin;
private final NMSClassResolver nmsClassResolver = new NMSClassResolver();
private Class<?> iChatBaseComponentClass = nmsClassResolver.resolveSilent("network.chat.IChatBaseComponent");
private Class<?> packetPlayOutChatClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutChat");
private Class<?> chatSerializerClass = nmsClassResolver.resolveSilent("ChatSerializer", "network.chat.IChatBaseComponent$ChatSerializer");
private Class<?> chatMessageTypeClass;
public JsonBuilder(ShopChest plugin) {
this.plugin = plugin;
if (Utils.getMajorVersion() >= 16) {
chatMessageTypeClass = nmsClassResolver.resolveSilent("network.chat.ChatMessageType");
}
Class<?>[] requiredClasses = new Class<?>[] {
iChatBaseComponentClass, packetPlayOutChatClass, chatSerializerClass
};
for (Class<?> c : requiredClasses) {
if (c == null) {
plugin.debug("Failed to instantiate JsonBuilder: Could not find all required classes");
return;
}
}
}
public static Part parse(String text) {
Matcher hexMatcher = HEX_PATTERN.matcher(text);
while (hexMatcher.find()) {
String hexCode = hexMatcher.group(0).replace("§", "");
text = text.replace(hexMatcher.group(0), "§#" + hexCode);
}
Matcher matcher = PART_PATTERN.matcher(text);
if (!matcher.find()) {
return new Part(text);
}
matcher.reset();
PartArray array = new PartArray(new Part());
int lastEndIndex = 0;
while (matcher.find()) {
int startIndex = matcher.start();
int endIndex = matcher.end();
if (lastEndIndex != startIndex) {
String betweenMatches = text.substring(lastEndIndex, startIndex);
array.addPart(new Part(betweenMatches));
}
String format = matcher.group(1);
String value = matcher.group(3);
PartMap part = new PartMap();
part.setValue("text", new Part(value));
String[] formats = format.split("§");
for (String f : formats) {
switch (f.toLowerCase()) {
case "":
break;
case "k":
part.setValue("obuscated", new Part(true));
break;
case "l":
part.setValue("bold", new Part(true));
break;
case "m":
part.setValue("strikethrough", new Part(true));
break;
case "n":
part.setValue("underlined", new Part(true));
break;
case "o":
part.setValue("italic", new Part(true));
break;
case "r":
part.removeValue("obfuscated");
part.removeValue("bold");
part.removeValue("strikethrough");
part.removeValue("underlined");
part.removeValue("italic");
part.removeValue("color");
break;
default:
if (f.startsWith("#")) {
part.setValue("color", new Part(f));
} else {
part.setValue("color", new Part(ChatColor.getByChar(f).name().toLowerCase()));
}
}
}
array.addPart(part);
lastEndIndex = endIndex;
}
return array;
}
@Override
public String toString() {
return rootPart.toString();
}
public Part getRootPart() {
return rootPart;
}
public void setRootPart(Part rootPart) {
this.rootPart = rootPart;
}
public void sendJson(Player p) {
try {
Object iChatBaseComponent = chatSerializerClass.getMethod("a", String.class).invoke(null, toString());
Object packetPlayOutChat = Utils.getMajorVersion() < 16
? packetPlayOutChatClass.getConstructor(iChatBaseComponentClass).newInstance(iChatBaseComponent)
: packetPlayOutChatClass.getConstructor(iChatBaseComponentClass, chatMessageTypeClass, UUID.class)
.newInstance(iChatBaseComponent, (new FieldResolver(chatMessageTypeClass)).resolve("CHAT", "a").get(null), UUID.randomUUID());
Utils.sendPacket(plugin, packetPlayOutChat, p);
plugin.debug("Sent JSON: " + toString());
} catch (ReflectiveOperationException e) {
plugin.getLogger().severe("Failed to send JSON with reflection");
plugin.debug("Failed to send JSON with reflection: " + toString());
plugin.debug(e);
}
}
}

View File

@ -1,93 +0,0 @@
package de.epiceric.shopchest.nms;
import java.lang.reflect.InvocationTargetException;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.utils.Utils;
public class SpawnEggMeta {
private static String getNBTEntityID(ShopChest plugin, ItemStack stack) {
try {
OBCClassResolver obcClassResolver = new OBCClassResolver();
Class<?> craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack");
if (craftItemStackClass == null) {
plugin.debug("Failed to get NBTEntityID: Could not find CraftItemStack class");
return null;
}
Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, stack);
Object nbtTagCompound = nmsStack.getClass().getMethod("getTag").invoke(nmsStack);
if (nbtTagCompound == null) return null;
Object entityTagCompound = nbtTagCompound.getClass().getMethod("getCompound", String.class).invoke(nbtTagCompound, "EntityTag");
if (entityTagCompound == null) return null;
Object id = entityTagCompound.getClass().getMethod("getString", String.class).invoke(entityTagCompound, "id");
if (id instanceof String) return (String) id;
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
plugin.getLogger().severe("Failed to get NBTEntityID with reflection");
plugin.debug("Failed to get NBTEntityID with reflection");
plugin.debug(e);
}
return null;
}
/**
* @param plugin An instance of the {@link ShopChest} plugin
* @param stack {@link ItemStack} (Spawn Egg) of which the Entity should be gotten
* @return The {@link EntityType} the Spawn Egg will spawn or <b>null</b> if <i>nbtEntityID</i> is null
*/
public static EntityType getEntityTypeFromItemStack(ShopChest plugin, ItemStack stack) {
if (Utils.getMajorVersion() == 8) {
EntityType type = null;
for (EntityType entityType : EntityType.values()) {
if (entityType.getTypeId() == stack.getDurability()) {
type = entityType;
break;
}
}
return type;
}
String nbtEntityID = getNBTEntityID(plugin, stack);
if (nbtEntityID == null) return null;
if (Utils.getMajorVersion() >= 11) {
if (nbtEntityID.contains(":")) nbtEntityID = nbtEntityID.split(":")[1];
return EntityType.fromName(nbtEntityID);
}
switch (nbtEntityID) {
case "PigZombie":
return EntityType.valueOf("PIG_ZOMBIE");
case "CaveSpider":
return EntityType.CAVE_SPIDER;
case "LavaSlime":
return EntityType.MAGMA_CUBE;
case "MushroomCow":
return EntityType.MUSHROOM_COW;
case "EntityHorse":
return EntityType.HORSE;
case "PolarBear":
return EntityType.POLAR_BEAR;
case "Ozelot":
return EntityType.OCELOT;
default:
return EntityType.valueOf(nbtEntityID.toUpperCase());
}
}
}

View File

@ -1,480 +0,0 @@
package de.epiceric.shopchest.shop;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest;
import org.bukkit.block.data.Directional;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.config.HologramFormat;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.exceptions.ChestNotFoundException;
import de.epiceric.shopchest.exceptions.NotEnoughSpaceException;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.nms.Hologram;
import de.epiceric.shopchest.utils.ItemUtils;
import de.epiceric.shopchest.utils.Utils;
public class Shop {
public enum ShopType {
NORMAL,
ADMIN,
}
private static class PreCreateResult {
private final Inventory inventory;
private final Chest[] chests;
private final BlockFace face;
private PreCreateResult(Inventory inventory, Chest[] chests, BlockFace face) {
this.inventory = inventory;
this.chests = chests;
this.face = face;
}
}
private final ShopChest plugin;
private final OfflinePlayer vendor;
private final ShopProduct product;
private final Location location;
private final double buyPrice;
private final double sellPrice;
private final ShopType shopType;
private boolean created;
private int id;
private Hologram hologram;
private Location holoLocation;
private ShopItem item;
public Shop(int id, ShopChest plugin, OfflinePlayer vendor, ShopProduct product, Location location, double buyPrice, double sellPrice, ShopType shopType) {
this.id = id;
this.plugin = plugin;
this.vendor = vendor;
this.product = product;
this.location = location;
this.buyPrice = buyPrice;
this.sellPrice = sellPrice;
this.shopType = shopType;
}
public Shop(ShopChest plugin, OfflinePlayer vendor, ShopProduct product, Location location, double buyPrice, double sellPrice, ShopType shopType) {
this(-1, plugin, vendor, product, location, buyPrice, sellPrice, shopType);
}
/**
* Test if this shop is equals to another
*
* @param o Other object to test against
* @return true if we are sure they are the same, false otherwise
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Shop shop = (Shop) o;
// id = -1 means temp shop
return id != -1 && id == shop.id;
}
@Override
public int hashCode() {
return id != -1 ? id : super.hashCode();
}
/**
* Create the shop
*
* @param showConsoleMessages to log exceptions to console
* @return Whether is was created or not
*/
public boolean create(boolean showConsoleMessages) {
if (created) return false;
plugin.debug("Creating shop (#" + id + ")");
Block b = location.getBlock();
if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) {
ChestNotFoundException ex = new ChestNotFoundException(String.format("No Chest found in world '%s' at location: %d; %d; %d",
b.getWorld().getName(), b.getX(), b.getY(), b.getZ()));
plugin.getShopUtils().removeShop(this, Config.removeShopOnError);
if (showConsoleMessages) plugin.getLogger().severe(ex.getMessage());
plugin.debug("Failed to create shop (#" + id + ")");
plugin.debug(ex);
return false;
} else if ((!ItemUtils.isAir(b.getRelative(BlockFace.UP).getType()))) {
NotEnoughSpaceException ex = new NotEnoughSpaceException(String.format("No space above chest in world '%s' at location: %d; %d; %d",
b.getWorld().getName(), b.getX(), b.getY(), b.getZ()));
plugin.getShopUtils().removeShop(this, Config.removeShopOnError);
if (showConsoleMessages) plugin.getLogger().severe(ex.getMessage());
plugin.debug("Failed to create shop (#" + id + ")");
plugin.debug(ex);
return false;
}
PreCreateResult preResult = preCreateHologram();
if (preResult == null) {
return false;
}
plugin.getShopCreationThreadPool().execute(() -> {
if (hologram == null || !hologram.exists()) createHologram(preResult);
if (item == null) createItem();
// Update shops for players in the same world after creation has finished
plugin.getUpdater().queue(() -> {
for (Player player : location.getWorld().getPlayers()) {
plugin.getShopUtils().resetPlayerLocation(player);
}
});
plugin.getUpdater().updateShops(location.getWorld());
});
created = true;
return true;
}
/**
* Removes the hologram of the shop
*/
public void removeHologram() {
if (hologram != null && hologram.exists()) {
plugin.debug("Removing hologram (#" + id + ")");
hologram.remove();
}
}
/**
* Removes the floating item of the shop
*/
public void removeItem() {
if (item != null) {
plugin.debug("Removing shop item (#" + id + ")");
item.remove();
}
}
/**
* <p>Creates the floating item of the shop</p>
* <b>Call this after {@link #createHologram()}, because it depends on the hologram's location</b>
*/
private void createItem() {
plugin.debug("Creating item (#" + id + ")");
Location itemLocation;
itemLocation = new Location(location.getWorld(), holoLocation.getX(), location.getY() + 0.9, holoLocation.getZ());
item = new ShopItem(plugin, product.getItemStack(), itemLocation);
}
/**
* Runs everything that needs to be called synchronously in order
* to prepare creating the hologram.
*/
private PreCreateResult preCreateHologram() {
plugin.debug("Creating hologram (#" + id + ")");
InventoryHolder ih = getInventoryHolder();
if (ih == null) return null;
Chest[] chests = new Chest[2];
BlockFace face;
if (ih instanceof DoubleChest) {
DoubleChest dc = (DoubleChest) ih;
Chest r = (Chest) dc.getRightSide();
Chest l = (Chest) dc.getLeftSide();
chests[0] = r;
chests[1] = l;
} else {
chests[0] = (Chest) ih;
}
if (Utils.getMajorVersion() < 13) {
face = ((org.bukkit.material.Directional) chests[0].getData()).getFacing();
} else {
face = ((Directional) chests[0].getBlockData()).getFacing();
}
return new PreCreateResult(ih.getInventory(), chests, face);
}
/**
* Acuatlly creates the hologram (async)
*/
private void createHologram(PreCreateResult preResult) {
String[] holoText = getHologramText(preResult.inventory);
holoLocation = getHologramLocation(preResult.chests, preResult.face);
new BukkitRunnable(){
@Override
public void run() {
hologram = new Hologram(plugin, holoText, holoLocation);
}
}.runTask(plugin);
}
/**
* Keep hologram text up to date.
* <p><b>Has to be called synchronously!</b></p>
*/
public void updateHologramText() {
String[] lines = getHologramText(getInventoryHolder().getInventory());
String[] currentLines = hologram.getLines();
int max = Math.max(lines.length, currentLines.length);
for (int i = 0; i < max; i++) {
if (i < lines.length) {
hologram.setLine(i, lines[i]);
} else {
hologram.removeLine(i);
}
}
}
private String[] getHologramText(Inventory inventory) {
List<String> lines = new ArrayList<>();
ItemStack itemStack = getProduct().getItemStack();
Map<HologramFormat.Requirement, Object> requirements = new EnumMap<>(HologramFormat.Requirement.class);
requirements.put(HologramFormat.Requirement.VENDOR, getVendor().getName());
requirements.put(HologramFormat.Requirement.AMOUNT, getProduct().getAmount());
requirements.put(HologramFormat.Requirement.ITEM_TYPE, itemStack.getType() + (itemStack.getDurability() > 0 ? ":" + itemStack.getDurability() : ""));
requirements.put(HologramFormat.Requirement.ITEM_NAME, itemStack.hasItemMeta() ? itemStack.getItemMeta().getDisplayName() : null);
requirements.put(HologramFormat.Requirement.HAS_ENCHANTMENT, !LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(itemStack)).isEmpty());
requirements.put(HologramFormat.Requirement.BUY_PRICE, getBuyPrice());
requirements.put(HologramFormat.Requirement.SELL_PRICE, getSellPrice());
requirements.put(HologramFormat.Requirement.HAS_POTION_EFFECT, ItemUtils.getPotionEffect(itemStack) != null);
requirements.put(HologramFormat.Requirement.IS_MUSIC_DISC, itemStack.getType().isRecord());
requirements.put(HologramFormat.Requirement.IS_POTION_EXTENDED, ItemUtils.isExtendedPotion(itemStack));
requirements.put(HologramFormat.Requirement.IS_WRITTEN_BOOK, itemStack.getType() == Material.WRITTEN_BOOK);
requirements.put(HologramFormat.Requirement.IS_BANNER_PATTERN, ItemUtils.isBannerPattern(itemStack));
requirements.put(HologramFormat.Requirement.ADMIN_SHOP, getShopType() == ShopType.ADMIN);
requirements.put(HologramFormat.Requirement.NORMAL_SHOP, getShopType() == ShopType.NORMAL);
requirements.put(HologramFormat.Requirement.IN_STOCK, Utils.getAmount(inventory, itemStack));
requirements.put(HologramFormat.Requirement.MAX_STACK, itemStack.getMaxStackSize());
requirements.put(HologramFormat.Requirement.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, itemStack));
requirements.put(HologramFormat.Requirement.DURABILITY, itemStack.getDurability());
Map<Placeholder, Object> placeholders = new EnumMap<>(Placeholder.class);
placeholders.put(Placeholder.VENDOR, getVendor().getName());
placeholders.put(Placeholder.AMOUNT, getProduct().getAmount());
placeholders.put(Placeholder.ITEM_NAME, getProduct().getLocalizedName());
placeholders.put(Placeholder.ENCHANTMENT, LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(itemStack)));
placeholders.put(Placeholder.BUY_PRICE, getBuyPrice());
placeholders.put(Placeholder.SELL_PRICE, getSellPrice());
placeholders.put(Placeholder.POTION_EFFECT, LanguageUtils.getPotionEffectName(itemStack));
placeholders.put(Placeholder.MUSIC_TITLE, LanguageUtils.getMusicDiscName(itemStack.getType()));
placeholders.put(Placeholder.BANNER_PATTERN_NAME, LanguageUtils.getBannerPatternName(itemStack.getType()));
placeholders.put(Placeholder.GENERATION, LanguageUtils.getBookGenerationName(itemStack));
placeholders.put(Placeholder.STOCK, Utils.getAmount(inventory, itemStack));
placeholders.put(Placeholder.MAX_STACK, itemStack.getMaxStackSize());
placeholders.put(Placeholder.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, itemStack));
placeholders.put(Placeholder.DURABILITY, itemStack.getDurability());
int lineCount = plugin.getHologramFormat().getLineCount();
for (int i = 0; i < lineCount; i++) {
String format = plugin.getHologramFormat().getFormat(i, requirements, placeholders);
for (Placeholder placeholder : placeholders.keySet()) {
String replace;
switch (placeholder) {
case BUY_PRICE:
replace = plugin.getEconomy().format(getBuyPrice());
break;
case SELL_PRICE:
replace = plugin.getEconomy().format(getSellPrice());
break;
default:
replace = String.valueOf(placeholders.get(placeholder));
}
format = format.replace(placeholder.toString(), replace);
}
if (!format.isEmpty()) {
lines.add(format);
}
}
return lines.toArray(new String[0]);
}
private Location getHologramLocation(Chest[] chests, BlockFace face) {
World w = location.getWorld();
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
Location holoLocation = new Location(w, x, y, z);
double deltaY = -0.6;
if (Config.hologramFixedBottom) deltaY = -0.85;
if (chests[1] != null) {
Chest c1 = Utils.getMajorVersion() >= 13 && (face == BlockFace.NORTH || face == BlockFace.EAST) ? chests[1] : chests[0];
Chest c2 = Utils.getMajorVersion() >= 13 && (face == BlockFace.NORTH || face == BlockFace.EAST) ? chests[0] : chests[1];
if (holoLocation.equals(c1.getLocation())) {
if (c1.getX() != c2.getX()) {
holoLocation.add(0, deltaY, 0.5);
} else if (c1.getZ() != c2.getZ()) {
holoLocation.add(0.5, deltaY, 0);
} else {
holoLocation.add(0.5, deltaY, 0.5);
}
} else {
if (c1.getX() != c2.getX()) {
holoLocation.add(1, deltaY, 0.5);
} else if (c1.getZ() != c2.getZ()) {
holoLocation.add(0.5, deltaY, 1);
} else {
holoLocation.add(0.5, deltaY, 0.5);
}
}
} else {
holoLocation.add(0.5, deltaY, 0.5);
}
holoLocation.add(0, Config.hologramLift, 0);
return holoLocation;
}
/**
* @return Whether an ID has been assigned to the shop
*/
public boolean hasId() {
return id != -1;
}
/**
* <p>Assign an ID to the shop.</p>
* Only works for the first time!
* @param id ID to set for this shop
*/
public void setId(int id) {
if (this.id == -1) {
this.id = id;
}
}
/**
* @return Whether the shop has already been created
*/
public boolean isCreated() {
return created;
}
/**
* @return The ID of the shop
*/
public int getID() {
return id;
}
/**
* @return Vendor of the shop; probably the creator of it
*/
public OfflinePlayer getVendor() {
return vendor;
}
/**
* @return Product the shop sells (or buys)
*/
public ShopProduct getProduct() {
return product;
}
/**
* @return Location of (one of) the shop's chest
*/
public Location getLocation() {
return location;
}
/**
* @return Buy price of the shop
*/
public double getBuyPrice() {
return buyPrice;
}
/**
* @return Sell price of the shop
*/
public double getSellPrice() {
return sellPrice;
}
/**
* @return Type of the shop
*/
public ShopType getShopType() {
return shopType;
}
/**
* @return Hologram of the shop
*/
public Hologram getHologram() {
return hologram;
}
/**
* @return Floating {@link ShopItem} of the shop
*/
public ShopItem getItem() {
return item;
}
public boolean hasHologram() {
return hologram != null;
}
public boolean hasItem() {
return item != null;
}
/**
* @return {@link InventoryHolder} of the shop or <b>null</b> if the shop has no chest.
*/
public InventoryHolder getInventoryHolder() {
Block b = getLocation().getBlock();
if (b.getType() == Material.CHEST || b.getType() == Material.TRAPPED_CHEST) {
Chest chest = (Chest) b.getState();
return chest.getInventory().getHolder();
}
return null;
}
}

View File

@ -1,164 +0,0 @@
package de.epiceric.shopchest.shop;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.utils.Utils;
public class ShopItem {
private final ShopChest plugin;
// concurrent since update task is in async thread
private final Set<UUID> viewers = ConcurrentHashMap.newKeySet();
private final ItemStack itemStack;
private final Location location;
private final UUID uuid = UUID.randomUUID();
private final int entityId;
private final NMSClassResolver nmsClassResolver = new NMSClassResolver();
private final OBCClassResolver obcClassResolver = new OBCClassResolver();
private final Class<?> packetPlayOutEntityDestroyClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityDestroy");
private final Class<?> packetPlayOutEntityVelocityClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityVelocity");
private final Class<?> packetPlayOutEntityMetadataClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityMetadata");
private final Class<?> dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher");
private final Class<?> vec3dClass = nmsClassResolver.resolveSilent("world.phys.Vec3D");
private final Class<?> craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack");
private final Class<?> nmsItemStackClass = nmsClassResolver.resolveSilent("world.item.ItemStack");
public ShopItem(ShopChest plugin, ItemStack itemStack, Location location) {
this.plugin = plugin;
this.itemStack = itemStack;
this.location = location;
this.entityId = Utils.getFreeEntityId();
Class<?>[] requiredClasses = new Class<?>[] {
nmsItemStackClass, craftItemStackClass, packetPlayOutEntityMetadataClass, dataWatcherClass,
packetPlayOutEntityDestroyClass, packetPlayOutEntityVelocityClass,
};
for (Class<?> c : requiredClasses) {
if (c == null) {
plugin.debug("Failed to create shop item: Could not find all required classes");
return;
}
}
}
/**
* @return Clone of the location, where the shop item should be (it could have been moved by something, even though it shouldn't)
*/
public Location getLocation() {
return location.clone();
}
/**
* @return A clone of this Item's {@link ItemStack}
*/
public ItemStack getItemStack() {
return itemStack.clone();
}
/**
* @param p Player to check
* @return Whether the item is visible to the player
*/
public boolean isVisible(Player p) {
return viewers.contains(p.getUniqueId());
}
/**
* @param p Player to which the item should be shown
*/
public void showPlayer(Player p) {
showPlayer(p, false);
}
/**
* @param p Player to which the item should be shown
* @param force whether to force or not
*/
public void showPlayer(Player p, boolean force) {
if (viewers.add(p.getUniqueId()) || force) {
try {
Object nmsItemStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, itemStack);
Object dataWatcher = Utils.createDataWatcher(null, nmsItemStack);
Utils.sendPacket(plugin, Utils.createPacketSpawnEntity(plugin, entityId, uuid, location, EntityType.DROPPED_ITEM), p);
Utils.sendPacket(plugin, packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class).newInstance(entityId, dataWatcher, true), p);
if (Utils.getMajorVersion() < 14) {
Utils.sendPacket(plugin, packetPlayOutEntityVelocityClass.getConstructor(int.class, double.class, double.class, double.class).newInstance(entityId, 0D, 0D, 0D), p);
} else {
Object vec3d = vec3dClass.getConstructor(double.class, double.class, double.class).newInstance(0D, 0D, 0D);
Utils.sendPacket(plugin, packetPlayOutEntityVelocityClass.getConstructor(int.class, vec3dClass).newInstance(entityId, vec3d), p);
}
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException | InstantiationException e) {
plugin.getLogger().severe("Failed to create item!");
plugin.debug("Failed to create item!");
plugin.debug(e);
}
}
}
/**
* @param p Player from which the item should be hidden
*/
public void hidePlayer(Player p) {
hidePlayer(p, false);
}
/**
* @param p Player from which the item should be hidden
* @param force whether to force or not
*/
public void hidePlayer(Player p, boolean force) {
if (viewers.remove(p.getUniqueId()) || force) {
try {
if (p.isOnline()) {
Object packetPlayOutEntityDestroy = packetPlayOutEntityDestroyClass.getConstructor(int[].class).newInstance((Object) new int[]{entityId});
Utils.sendPacket(plugin, packetPlayOutEntityDestroy, p);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
plugin.getLogger().severe("Failed to destroy shop item");
plugin.debug("Failed to destroy shop item with reflection");
plugin.debug(e);
}
}
}
public void resetVisible(Player p) {
viewers.remove(p.getUniqueId());
}
/**
* Removes the item. <br>
* Item will be hidden from all players
*/
public void remove() {
// Avoid ConcurrentModificationException
for (UUID uuid : new ArrayList<>(viewers)) {
Player p = Bukkit.getPlayer(uuid);
if (p != null) hidePlayer(p);
}
}
/**
* Respawns the item at the set location for a player
* @param p Player, for which the item should be reset
*/
public void resetForPlayer(Player p) {
hidePlayer(p);
showPlayer(p);
}
}

View File

@ -1,43 +0,0 @@
package de.epiceric.shopchest.shop;
import org.bukkit.inventory.ItemStack;
import de.epiceric.shopchest.language.LanguageUtils;
public class ShopProduct {
private final ItemStack itemStack;
private final int amount;
public ShopProduct(ItemStack itemStack, int amount) {
this.itemStack = new ItemStack(itemStack);
this.itemStack.setAmount(1);
this.amount = amount;
}
public ShopProduct(ItemStack itemStack) {
this(itemStack, itemStack.getAmount());
}
/**
* @return The localized name of the product's {@link ItemStack} in the selected language file.
*/
public String getLocalizedName() {
return LanguageUtils.getItemName(getItemStack());
}
/**
* @return The {@link ItemStack} with an amount of {@code 1}.
*/
public ItemStack getItemStack() {
return itemStack;
}
/**
* @return The amount
*/
public int getAmount() {
return amount;
}
}

View File

@ -1,877 +0,0 @@
package de.epiceric.shopchest.sql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopBuySellEvent;
import de.epiceric.shopchest.event.ShopBuySellEvent.Type;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.shop.ShopProduct;
import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.Utils;
public abstract class Database {
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private boolean initialized;
String tableShops;
String tableLogs;
String tableLogouts;
String tableFields;
ShopChest plugin;
HikariDataSource dataSource;
protected Database(ShopChest plugin) {
this.plugin = plugin;
}
abstract HikariDataSource getDataSource();
abstract String getQueryCreateTableShops();
abstract String getQueryCreateTableLog();
abstract String getQueryCreateTableLogout();
abstract String getQueryCreateTableFields();
abstract String getQueryGetTable();
private int getDatabaseVersion() throws SQLException {
try (Connection con = dataSource.getConnection()) {
try (Statement s = con.createStatement()) {
ResultSet rs = s.executeQuery("SELECT value FROM " + tableFields + " WHERE field='version'");
if (rs.next()) {
return rs.getInt("value");
}
}
}
return 0;
}
private void setDatabaseVersion(int version) throws SQLException {
String queryUpdateVersion = "REPLACE INTO " + tableFields + " VALUES ('version', ?)";
try (Connection con = dataSource.getConnection()) {
try (PreparedStatement ps = con.prepareStatement(queryUpdateVersion)) {
ps.setInt(1, version);
ps.executeUpdate();
}
}
}
private boolean update() throws SQLException {
String queryGetTable = getQueryGetTable();
try (Connection con = dataSource.getConnection()) {
boolean needsUpdate1 = false; // update "shop_log" to "economy_logs" and update "shops" with prefixes
boolean needsUpdate2 = false; // create field table and set database version
try (PreparedStatement ps = con.prepareStatement(queryGetTable)) {
ps.setString(1, "shop_log");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
needsUpdate1 = true;
}
}
try (PreparedStatement ps = con.prepareStatement(queryGetTable)) {
ps.setString(1, tableFields);
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
needsUpdate2 = true;
}
}
if (needsUpdate1) {
String queryRenameTableLogouts = "ALTER TABLE player_logout RENAME TO " + tableLogouts;
String queryRenameTableLogs = "ALTER TABLE shop_log RENAME TO backup_shop_log"; // for backup
String queryRenameTableShops = "ALTER TABLE shops RENAME TO backup_shops"; // for backup
plugin.getLogger().info("Updating database... (#1)");
// Rename logout table
try (Statement s = con.createStatement()) {
s.executeUpdate(queryRenameTableLogouts);
}
// Backup shops table
try (Statement s = con.createStatement()) {
s.executeUpdate(queryRenameTableShops);
}
// Backup log table
try (Statement s = con.createStatement()) {
s.executeUpdate(queryRenameTableLogs);
}
// Create new shops table
try (Statement s = con.createStatement()) {
s.executeUpdate(getQueryCreateTableShops());
}
// Create new log table
try (Statement s = con.createStatement()) {
s.executeUpdate(getQueryCreateTableLog());
}
// Convert shop table
try (Statement s = con.createStatement()) {
ResultSet rs = s.executeQuery("SELECT id,product FROM backup_shops");
while (rs.next()) {
ItemStack is = Utils.decode(rs.getString("product"));
int amount = is.getAmount();
is.setAmount(1);
String product = Utils.encode(is);
String insertQuery = "INSERT INTO " + tableShops + " SELECT id,vendor,?,?,world,x,y,z,buyprice,sellprice,shoptype FROM backup_shops WHERE id = ?";
try (PreparedStatement ps = con.prepareStatement(insertQuery)) {
ps.setString(1, product);
ps.setInt(2, amount);
ps.setInt(3, rs.getInt("id"));
ps.executeUpdate();
}
}
}
// Convert log table
try (Statement s = con.createStatement()) {
ResultSet rs = s.executeQuery("SELECT id,timestamp,executor,product,vendor FROM backup_shop_log");
while (rs.next()) {
String timestamp = rs.getString("timestamp");
long time = 0L;
try {
time = dateFormat.parse(timestamp).getTime();
} catch (ParseException e) {
plugin.debug("Failed to parse timestamp '" + timestamp + "': Time is set to 0");
plugin.debug(e);
}
String player = rs.getString("executor");
String playerUuid = player.substring(0, 36);
String playerName = player.substring(38, player.length() - 1);
String oldProduct = rs.getString("product");
String product = oldProduct.split(" x ")[1];
int amount = Integer.valueOf(oldProduct.split(" x ")[0]);
String vendor = rs.getString("vendor");
String vendorUuid = vendor.substring(0, 36);
String vendorName = vendor.substring(38).replaceAll("\\)( \\(ADMIN\\))?", "");
boolean admin = vendor.endsWith("(ADMIN)");
String insertQuery = "INSERT INTO " + tableLogs + " SELECT id,-1,timestamp,?,?,?,?,'Unknown',?,?,?,?,world,x,y,z,price,type FROM backup_shop_log WHERE id = ?";
try (PreparedStatement ps = con.prepareStatement(insertQuery)) {
ps.setLong(1, time);
ps.setString(2, playerName);
ps.setString(3, playerUuid);
ps.setString(4, product);
ps.setInt(5, amount);
ps.setString(6, vendorName);
ps.setString(7, vendorUuid);
ps.setBoolean(8, admin);
ps.setInt(9, rs.getInt("id"));
ps.executeUpdate();
}
}
}
}
if (needsUpdate2) {
plugin.getLogger().info("Updating database... (#2)");
// Create fields table
try (Statement s = con.createStatement()) {
s.executeUpdate(getQueryCreateTableFields());
}
setDatabaseVersion(2);
}
int databaseVersion = getDatabaseVersion();
if (databaseVersion < 3) {
// plugin.getLogger().info("Updating database... (#3)");
// Update database structure...
// setDatabaseVersion(3);
}
int newDatabaseVersion = getDatabaseVersion();
return needsUpdate1 || needsUpdate2 || newDatabaseVersion > databaseVersion;
}
}
/**
* <p>(Re-)Connects to the the database and initializes it.</p>
*
* All tables are created if necessary and if the database
* structure has to be updated, that is done as well.
*
* @param callback Callback that - if succeeded - returns the amount of shops
* that were found (as {@code int})
*/
public void connect(final Callback<Integer> callback) {
if (!Config.databaseTablePrefix.matches("^([a-zA-Z0-9\\-\\_]+)?$")) {
// Only letters, numbers dashes and underscores are allowed
plugin.getLogger().severe("Database table prefix contains illegal letters, using 'shopchest_' prefix.");
Config.databaseTablePrefix = "shopchest_";
}
this.tableShops = Config.databaseTablePrefix + "shops";
this.tableLogs = Config.databaseTablePrefix + "economy_logs";
this.tableLogouts = Config.databaseTablePrefix + "player_logouts";
this.tableFields = Config.databaseTablePrefix + "fields";
new BukkitRunnable() {
@Override
public void run() {
disconnect();
try {
dataSource = getDataSource();
} catch (Exception e) {
callback.onError(e);
plugin.debug(e);
return;
}
if (dataSource == null) {
Exception e = new IllegalStateException("Data source is null");
callback.onError(e);
plugin.debug(e);
return;
}
try (Connection con = dataSource.getConnection()) {
// Update database structure if necessary
if (update()) {
plugin.getLogger().info("Updating database finished");
}
// Create shop table
try (Statement s = con.createStatement()) {
s.executeUpdate(getQueryCreateTableShops());
}
// Create log table
try (Statement s = con.createStatement()) {
s.executeUpdate(getQueryCreateTableLog());
}
// Create logout table
try (Statement s = con.createStatement()) {
s.executeUpdate(getQueryCreateTableLogout());
}
// Create fields table
try (Statement s = con.createStatement()) {
s.executeUpdate(getQueryCreateTableFields());
}
// Clean up economy log
if (Config.cleanupEconomyLogDays > 0) {
cleanUpEconomy(false);
}
// Count shops entries in database
try (Statement s = con.createStatement()) {
ResultSet rs = s.executeQuery("SELECT COUNT(id) FROM " + tableShops);
if (rs.next()) {
int count = rs.getInt(1);
initialized = true;
plugin.debug("Initialized database with " + count + " entries");
if (callback != null) {
callback.callSyncResult(count);
}
} else {
throw new SQLException("Count result set has no entries");
}
}
} catch (SQLException e) {
if (callback != null) {
callback.callSyncError(e);
}
plugin.getLogger().severe("Failed to initialize or connect to database");
plugin.debug("Failed to initialize or connect to database");
plugin.debug(e);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Remove a shop from the database
*
* @param shop Shop to remove
* @param callback Callback that - if succeeded - returns {@code null}
*/
public void removeShop(final Shop shop, final Callback<Void> callback) {
new BukkitRunnable() {
@Override
public void run() {
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM " + tableShops + " WHERE id = ?")) {
ps.setInt(1, shop.getID());
ps.executeUpdate();
plugin.debug("Removing shop from database (#" + shop.getID() + ")");
if (callback != null) {
callback.callSyncResult(null);
}
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to remove shop from database");
plugin.debug("Failed to remove shop from database (#" + shop.getID() + ")");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Get shop amounts for each player
*
* @param callback Callback that returns a map of each player's shop amount
*/
public void getShopAmounts(final Callback<Map<UUID, Integer>> callback) {
new BukkitRunnable(){
@Override
public void run() {
try (Connection con = dataSource.getConnection();
Statement s = con.createStatement()) {
ResultSet rs = s.executeQuery("SELECT vendor, COUNT(*) AS count FROM " + tableShops + " WHERE shoptype = 'NORMAL' GROUP BY vendor");
plugin.debug("Getting shop amounts from database");
Map<UUID, Integer> result = new HashMap<>();
while (rs.next()) {
UUID uuid = UUID.fromString(rs.getString("vendor"));
result.put(uuid, rs.getInt("count"));
}
if (callback != null) {
callback.callSyncResult(result);
}
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to get shop amounts from database");
plugin.debug("Failed to get shop amounts from database");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Get all shops of a player, including admin shops
*
* @param callback Callback that returns a set of shops of the given player
*/
public void getShops(UUID playerUuid, final Callback<Collection<Shop>> callback) {
new BukkitRunnable(){
@Override
public void run() {
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableShops + " WHERE vendor = ?")) {
ps.setString(1, playerUuid.toString());
ResultSet rs = ps.executeQuery();
plugin.debug("Getting a player's shops from database");
Set<Shop> result = new HashSet<>();
while (rs.next()) {
int id = rs.getInt("id");
plugin.debug("Getting Shop... (#" + id + ")");
int x = rs.getInt("x");
int y = rs.getInt("y");
int z = rs.getInt("z");
World world = plugin.getServer().getWorld(rs.getString("world"));
Location location = new Location(world, x, y, z);
OfflinePlayer vendor = Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("vendor")));
ItemStack itemStack = Utils.decode(rs.getString("product"));
int amount = rs.getInt("amount");
ShopProduct product = new ShopProduct(itemStack, amount);
double buyPrice = rs.getDouble("buyprice");
double sellPrice = rs.getDouble("sellprice");
ShopType shopType = ShopType.valueOf(rs.getString("shoptype"));
plugin.debug("Initializing new shop... (#" + id + ")");
result.add(new Shop(id, plugin, vendor, product, location, buyPrice, sellPrice, shopType));
}
if (callback != null) {
callback.callSyncResult(result);
}
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to get player's shops from database");
plugin.debug("Failed to get player's shops from database");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Get all shops from the database that are located in the given chunks
*
* @param chunks Shops in these chunks are retrieved
* @param callback Callback that returns an immutable collection of shops if succeeded
*/
public void getShopsInChunks(final Chunk[] chunks, final Callback<Collection<Shop>> callback) {
// Split chunks into packages containing each {splitSize} chunks at max
int splitSize = 80;
int parts = (int) Math.ceil(chunks.length / (double) splitSize);
Chunk[][] splitChunks = new Chunk[parts][];
for (int i = 0; i < parts; i++) {
int size = i < parts - 1 ? splitSize : chunks.length % splitSize;
Chunk[] tmp = new Chunk[size];
System.arraycopy(chunks, i * splitSize, tmp, 0, size);
splitChunks[i] = tmp;
}
new BukkitRunnable(){
@Override
public void run() {
List<Shop> shops = new ArrayList<>();
// Send a request for each chunk package
for (Chunk[] newChunks : splitChunks) {
// Map chunks by world
Map<String, Set<Chunk>> chunksByWorld = new HashMap<>();
for (Chunk chunk : newChunks) {
String world = chunk.getWorld().getName();
Set<Chunk> chunksForWorld = chunksByWorld.getOrDefault(world, new HashSet<>());
chunksForWorld.add(chunk);
chunksByWorld.put(world, chunksForWorld);
}
// Create query dynamically
String query = "SELECT * FROM " + tableShops + " WHERE ";
for (String world : chunksByWorld.keySet()) {
query += "(world = ? AND (";
int chunkNum = chunksByWorld.get(world).size();
for (int i = 0; i < chunkNum; i++) {
query += "((x BETWEEN ? AND ?) AND (z BETWEEN ? AND ?)) OR ";
}
query += "1=0)) OR ";
}
query += "1=0";
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(query)) {
int index = 0;
for (String world : chunksByWorld.keySet()) {
ps.setString(++index, world);
for (Chunk chunk : chunksByWorld.get(world)) {
int minX = chunk.getX() * 16;
int minZ = chunk.getZ() * 16;
ps.setInt(++index, minX);
ps.setInt(++index, minX + 15);
ps.setInt(++index, minZ);
ps.setInt(++index, minZ + 15);
}
}
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
plugin.debug("Getting Shop... (#" + id + ")");
int x = rs.getInt("x");
int y = rs.getInt("y");
int z = rs.getInt("z");
World world = plugin.getServer().getWorld(rs.getString("world"));
Location location = new Location(world, x, y, z);
OfflinePlayer vendor = Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("vendor")));
ItemStack itemStack = Utils.decode(rs.getString("product"));
int amount = rs.getInt("amount");
ShopProduct product = new ShopProduct(itemStack, amount);
double buyPrice = rs.getDouble("buyprice");
double sellPrice = rs.getDouble("sellprice");
ShopType shopType = ShopType.valueOf(rs.getString("shoptype"));
plugin.debug("Initializing new shop... (#" + id + ")");
shops.add(new Shop(id, plugin, vendor, product, location, buyPrice, sellPrice, shopType));
}
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to get shops from database");
plugin.debug("Failed to get shops");
plugin.debug(ex);
return;
}
}
if (callback != null) {
callback.callSyncResult(Collections.unmodifiableCollection(shops));
}
};
}.runTaskAsynchronously(plugin);
}
/**
* Adds a shop to the database
*
* @param shop Shop to add
* @param callback Callback that - if succeeded - returns the ID the shop was
* given (as {@code int})
*/
public void addShop(final Shop shop, final Callback<Integer> callback) {
final String queryNoId = "REPLACE INTO " + tableShops + " (vendor,product,amount,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?)";
final String queryWithId = "REPLACE INTO " + tableShops + " (id,vendor,product,amount,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?,?)";
new BukkitRunnable() {
@Override
public void run() {
String query = shop.hasId() ? queryWithId : queryNoId;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
int i = 0;
if (shop.hasId()) {
i = 1;
ps.setInt(1, shop.getID());
}
ps.setString(i+1, shop.getVendor().getUniqueId().toString());
ps.setString(i+2, Utils.encode(shop.getProduct().getItemStack()));
ps.setInt(i+3, shop.getProduct().getAmount());
ps.setString(i+4, shop.getLocation().getWorld().getName());
ps.setInt(i+5, shop.getLocation().getBlockX());
ps.setInt(i+6, shop.getLocation().getBlockY());
ps.setInt(i+7, shop.getLocation().getBlockZ());
ps.setDouble(i+8, shop.getBuyPrice());
ps.setDouble(i+9, shop.getSellPrice());
ps.setString(i+10, shop.getShopType().toString());
ps.executeUpdate();
if (!shop.hasId()) {
int shopId = -1;
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
shopId = rs.getInt(1);
}
shop.setId(shopId);
}
if (callback != null) {
callback.callSyncResult(shop.getID());
}
plugin.debug("Adding shop to database (#" + shop.getID() + ")");
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to add shop to database");
plugin.debug("Failed to add shop to database (#" + shop.getID() + ")");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Log an economy transaction to the database
*
* @param executor Player who bought/sold something
* @param shop The {@link Shop} the player bought from or sold to
* @param product The {@link ItemStack} that was bought/sold
* @param price The price the product was bought or sold for
* @param type Whether the executor bought or sold
* @param callback Callback that - if succeeded - returns {@code null}
*/
public void logEconomy(final Player executor, Shop shop, ShopProduct product, double price, Type type, final Callback<Void> callback) {
final String query = "INSERT INTO " + tableLogs + " (shop_id,timestamp,time,player_name,player_uuid,product_name,product,amount,"
+ "vendor_name,vendor_uuid,admin,world,x,y,z,price,type) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
if (Config.enableEconomyLog) {
new BukkitRunnable() {
@Override
public void run() {
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(query)) {
long millis = System.currentTimeMillis();
ps.setInt(1, shop.getID());
ps.setString(2, dateFormat.format(millis));
ps.setLong(3, millis);
ps.setString(4, executor.getName());
ps.setString(5, executor.getUniqueId().toString());
ps.setString(6, product.getLocalizedName());
ps.setString(7, Utils.encode(product.getItemStack()));
ps.setInt(8, product.getAmount());
ps.setString(9, shop.getVendor().getName());
ps.setString(10, shop.getVendor().getUniqueId().toString());
ps.setBoolean(11, shop.getShopType() == ShopType.ADMIN);
ps.setString(12, shop.getLocation().getWorld().getName());
ps.setInt(13, shop.getLocation().getBlockX());
ps.setInt(14, shop.getLocation().getBlockY());
ps.setInt(15, shop.getLocation().getBlockZ());
ps.setDouble(16, price);
ps.setString(17, type.toString());
ps.executeUpdate();
if (callback != null) {
callback.callSyncResult(null);
}
plugin.debug("Logged economy transaction to database");
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to log economy transaction to database");
plugin.debug("Failed to log economy transaction to database");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
} else {
if (callback != null) {
callback.callSyncResult(null);
}
}
}
/**
* Cleans up the economy log to reduce file size
*
* @param async Whether the call should be executed asynchronously
*/
public void cleanUpEconomy(boolean async) {
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public void run() {
long time = System.currentTimeMillis() - Config.cleanupEconomyLogDays * 86400000L;
String queryCleanUpLog = "DELETE FROM " + tableLogs + " WHERE time < " + time;
String queryCleanUpPlayers = "DELETE FROM " + tableLogouts + " WHERE time < " + time;
try (Connection con = dataSource.getConnection();
Statement s = con.createStatement();
Statement s2 = con.createStatement()) {
s.executeUpdate(queryCleanUpLog);
s2.executeUpdate(queryCleanUpPlayers);
plugin.getLogger().info("Cleaned up economy log");
plugin.debug("Cleaned up economy log");
} catch (SQLException ex) {
plugin.getLogger().severe("Failed to clean up economy log");
plugin.debug("Failed to clean up economy log");
plugin.debug(ex);
}
}
};
if (async) {
runnable.runTaskAsynchronously(plugin);
} else {
runnable.run();
}
}
/**
* Get the revenue a player got while he was offline
*
* @param player Player whose revenue to get
* @param logoutTime Time in milliseconds when he logged out the last time
* @param callback Callback that - if succeeded - returns the revenue the
* player made while offline (as {@code double})
*/
public void getRevenue(final Player player, final long logoutTime, final Callback<Double> callback) {
new BukkitRunnable() {
@Override
public void run() {
double revenue = 0;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableLogs + " WHERE vendor_uuid = ?")) {
ps.setString(1, player.getUniqueId().toString());
ResultSet rs = ps.executeQuery();
while (rs.next()) {
long timestamp = rs.getLong("time");
double singleRevenue = rs.getDouble("price");
ShopBuySellEvent.Type type = ShopBuySellEvent.Type.valueOf(rs.getString("type"));
if (type == ShopBuySellEvent.Type.SELL) {
singleRevenue = -singleRevenue;
}
if (timestamp > logoutTime) {
revenue += singleRevenue;
}
}
if (callback != null) {
callback.callSyncResult(revenue);
}
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to get revenue from database");
plugin.debug("Failed to get revenue from player \"" + player.getUniqueId().toString() + "\"");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Log a logout to the database
*
* @param player Player who logged out
* @param callback Callback that - if succeeded - returns {@code null}
*/
public void logLogout(final Player player, final Callback<Void> callback) {
new BukkitRunnable() {
@Override
public void run() {
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("REPLACE INTO " + tableLogouts + " (player,time) VALUES(?,?)")) {
ps.setString(1, player.getUniqueId().toString());
ps.setLong(2, System.currentTimeMillis());
ps.executeUpdate();
if (callback != null) {
callback.callSyncResult(null);
}
plugin.debug("Logged logout to database");
} catch (final SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to log last logout to database");
plugin.debug("Failed to log logout to database");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Get the last logout of a player
*
* @param player Player who logged out
* @param callback Callback that - if succeeded - returns the time in
* milliseconds the player logged out (as {@code long})
* or {@code -1} if the player has not logged out yet.
*/
public void getLastLogout(final Player player, final Callback<Long> callback) {
new BukkitRunnable() {
@Override
public void run() {
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableLogouts + " WHERE player = ?")) {
ps.setString(1, player.getUniqueId().toString());
ResultSet rs = ps.executeQuery();
if (rs.next()) {
if (callback != null) {
callback.callSyncResult(rs.getLong("time"));
}
}
if (callback != null) {
callback.callSyncResult(-1L);
}
} catch (SQLException ex) {
if (callback != null) {
callback.callSyncError(ex);
}
plugin.getLogger().severe("Failed to get last logout from database");
plugin.debug("Failed to get last logout from player \"" + player.getName() + "\"");
plugin.debug(ex);
}
}
}.runTaskAsynchronously(plugin);
}
/**
* Closes the data source
*/
public void disconnect() {
if (dataSource != null) {
dataSource.close();
dataSource = null;
}
}
/**
* Returns whether a connection to the database has been established
*/
public boolean isInitialized() {
return initialized;
}
public enum DatabaseType {
SQLite, MySQL
}
}

View File

@ -1,110 +0,0 @@
package de.epiceric.shopchest.sql;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.scheduler.BukkitRunnable;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
public class MySQL extends Database {
public MySQL(ShopChest plugin) {
super(plugin);
}
@Override
HikariDataSource getDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(String.format("jdbc:mysql://%s:%d/%s?autoReconnect=true&useSSL=false&serverTimezone=UTC",
Config.databaseMySqlHost, Config.databaseMySqlPort, Config.databaseMySqlDatabase));
config.setUsername(Config.databaseMySqlUsername);
config.setPassword(Config.databaseMySqlPassword);
config.setConnectionTestQuery("SELECT 1");
return new HikariDataSource(config);
}
/**
* Sends an asynchronous ping to the database
*/
public void ping() {
new BukkitRunnable() {
@Override
public void run() {
try (Connection con = dataSource.getConnection();
Statement s = con.createStatement()) {
plugin.debug("Pinging to MySQL server...");
s.execute("/* ping */ SELECT 1");
} catch (SQLException ex) {
plugin.getLogger().severe("Failed to ping to MySQL server. Trying to reconnect...");
plugin.debug("Failed to ping to MySQL server. Trying to reconnect...");
connect(null);
}
}
}.runTaskAsynchronously(plugin);
}
@Override
String getQueryCreateTableShops() {
return "CREATE TABLE IF NOT EXISTS " + tableShops + " ("
+ "id INTEGER PRIMARY KEY AUTO_INCREMENT,"
+ "vendor TINYTEXT NOT NULL,"
+ "product TEXT NOT NULL,"
+ "amount INTEGER NOT NULL,"
+ "world TINYTEXT NOT NULL,"
+ "x INTEGER NOT NULL,"
+ "y INTEGER NOT NULL,"
+ "z INTEGER NOT NULL,"
+ "buyprice FLOAT NOT NULL,"
+ "sellprice FLOAT NOT NULL,"
+ "shoptype TINYTEXT NOT NULL)";
}
@Override
String getQueryCreateTableLog() {
return "CREATE TABLE IF NOT EXISTS " + tableLogs + " ("
+ "id INTEGER PRIMARY KEY AUTO_INCREMENT,"
+ "shop_id INTEGER NOT NULL,"
+ "timestamp TINYTEXT NOT NULL,"
+ "time LONG NOT NULL,"
+ "player_name TINYTEXT NOT NULL,"
+ "player_uuid TINYTEXT NOT NULL,"
+ "product_name TINYTEXT NOT NULL,"
+ "product TEXT NOT NULL,"
+ "amount INTEGER NOT NULL,"
+ "vendor_name TINYTEXT NOT NULL,"
+ "vendor_uuid TINYTEXT NOT NULL,"
+ "admin BIT NOT NULL,"
+ "world TINYTEXT NOT NULL,"
+ "x INTEGER NOT NULL,"
+ "y INTEGER NOT NULL,"
+ "z INTEGER NOT NULL,"
+ "price FLOAT NOT NULL,"
+ "type TINYTEXT NOT NULL)";
}
@Override
String getQueryCreateTableLogout() {
return "CREATE TABLE IF NOT EXISTS " + tableLogouts + " ("
+ "player VARCHAR(36) PRIMARY KEY NOT NULL,"
+ "time LONG NOT NULL)";
}
@Override
String getQueryCreateTableFields() {
return "CREATE TABLE IF NOT EXISTS " + tableFields + " ("
+ "field VARCHAR(32) PRIMARY KEY NOT NULL,"
+ "value INTEGER NOT NULL)";
}
@Override
String getQueryGetTable() {
return "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?";
}
}

View File

@ -1,126 +0,0 @@
package de.epiceric.shopchest.sql;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import de.epiceric.shopchest.ShopChest;
public class SQLite extends Database {
public SQLite(ShopChest plugin) {
super(plugin);
}
@Override
HikariDataSource getDataSource() {
try {
// Initialize driver class so HikariCP can find it
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
plugin.getLogger().severe("Failed to initialize SQLite driver");
plugin.debug("Failed to initialize SQLite driver");
plugin.debug(e);
return null;
}
File folder = plugin.getDataFolder();
File dbFile = new File(folder, "shops.db");
if (!dbFile.exists()) {
try {
dbFile.createNewFile();
} catch (IOException ex) {
plugin.getLogger().severe("Failed to create database file");
plugin.debug("Failed to create database file");
plugin.debug(ex);
return null;
}
}
HikariConfig config = new HikariConfig();
config.setJdbcUrl(String.format("jdbc:sqlite:" + dbFile));
config.setConnectionTestQuery("SELECT 1");
return new HikariDataSource(config);
}
/**
* Vacuums the database synchronously to reduce file size
*/
public void vacuum() {
try (Connection con = dataSource.getConnection();
Statement s = con.createStatement()) {
s.executeUpdate("VACUUM");
plugin.debug("Vacuumed SQLite database");
} catch (final SQLException ex) {
plugin.getLogger().warning("Failed to vacuum database");
plugin.debug("Failed to vacuum database");
plugin.debug(ex);
}
}
@Override
String getQueryCreateTableShops() {
return "CREATE TABLE IF NOT EXISTS " + tableShops + " ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "vendor TINYTEXT NOT NULL,"
+ "product TEXT NOT NULL,"
+ "amount INTEGER NOT NULL,"
+ "world TINYTEXT NOT NULL,"
+ "x INTEGER NOT NULL,"
+ "y INTEGER NOT NULL,"
+ "z INTEGER NOT NULL,"
+ "buyprice FLOAT NOT NULL,"
+ "sellprice FLOAT NOT NULL,"
+ "shoptype TINYTEXT NOT NULL)";
}
@Override
String getQueryCreateTableLog() {
return "CREATE TABLE IF NOT EXISTS " + tableLogs + " ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "shop_id INTEGER NOT NULL,"
+ "timestamp TINYTEXT NOT NULL,"
+ "time LONG NOT NULL,"
+ "player_name TINYTEXT NOT NULL,"
+ "player_uuid TINYTEXT NOT NULL,"
+ "product_name TINYTEXT NOT NULL,"
+ "product TEXT NOT NULL,"
+ "amount INTEGER NOT NULL,"
+ "vendor_name TINYTEXT NOT NULL,"
+ "vendor_uuid TINYTEXT NOT NULL,"
+ "admin BIT NOT NULL,"
+ "world TINYTEXT NOT NULL,"
+ "x INTEGER NOT NULL,"
+ "y INTEGER NOT NULL,"
+ "z INTEGER NOT NULL,"
+ "price FLOAT NOT NULL,"
+ "type TINYTEXT NOT NULL)";
}
@Override
String getQueryCreateTableLogout() {
return "CREATE TABLE IF NOT EXISTS " + tableLogouts + " ("
+ "player VARCHAR(36) PRIMARY KEY NOT NULL,"
+ "time LONG NOT NULL)";
}
@Override
String getQueryCreateTableFields() {
return "CREATE TABLE IF NOT EXISTS " + tableFields + " ("
+ "field VARCHAR(32) PRIMARY KEY NOT NULL,"
+ "value INTEGER NOT NULL)";
}
@Override
String getQueryGetTable() {
return "SELECT name FROM sqlite_master WHERE type='table' AND name=?";
}
}

View File

@ -1,35 +0,0 @@
package de.epiceric.shopchest.utils;
import org.bukkit.scheduler.BukkitRunnable;
import de.epiceric.shopchest.ShopChest;
public abstract class Callback<T> {
private ShopChest plugin;
public Callback(ShopChest plugin) {
this.plugin = plugin;
}
public void onResult(T result) {}
public void onError(Throwable throwable) {}
public final void callSyncResult(final T result) {
new BukkitRunnable() {
@Override
public void run() {
onResult(result);
}
}.runTask(plugin);
}
public final void callSyncError(final Throwable throwable) {
new BukkitRunnable() {
@Override
public void run() {
onError(throwable);
}
}.runTask(plugin);
}
}

View File

@ -1,222 +0,0 @@
package de.epiceric.shopchest.utils;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.shop.ShopProduct;
public class ClickType {
private static Map<UUID, ClickType> playerClickType = new HashMap<>();
private static Map<UUID, BukkitTask> playerTimers = new HashMap<>();
private EnumClickType enumClickType;
public ClickType(EnumClickType enumClickType) {
this.enumClickType = enumClickType;
}
/**
* Clear click types, cancel timers, and reset game modes
*/
public static void clear() {
playerClickType.forEach((uuid, ct) -> {
if (ct instanceof SelectClickType) {
Player p = Bukkit.getPlayer(uuid);
if (p != null)
p.setGameMode(((SelectClickType) ct).getGameMode());
}
});
playerTimers.forEach((uuid, timer) -> timer.cancel());
}
/**
* Gets the click type of a player
*
* @param player Player whose click type should be gotten
* @return The Player's click type or <b>null</b> if he doesn't have one
*/
public static ClickType getPlayerClickType(OfflinePlayer player) {
return playerClickType.get(player.getUniqueId());
}
/**
* Removes the click type from a player and cancels the 15 second timer
*
* @param player Player to remove the click type from
*/
public static void removePlayerClickType(OfflinePlayer player) {
UUID uuid = player.getUniqueId();
if (playerClickType.get(uuid) instanceof SelectClickType && player instanceof Player) {
// Reset gamemode player has select click type
((Player) player).setGameMode(((SelectClickType) playerClickType.get(uuid)).gameMode);
}
playerClickType.remove(uuid);
// If a timer is still running, cancel it
Optional.ofNullable(playerTimers.get(uuid)).ifPresent(task -> task.cancel());
playerTimers.remove(uuid);
}
/**
* Sets the click type of a player and removes it after 15 seconds
*
* @param player Player whose click type should be set
* @param clickType Click type to set
*/
public static void setPlayerClickType(OfflinePlayer player, ClickType clickType) {
UUID uuid = player.getUniqueId();
if (playerClickType.get(uuid) instanceof SelectClickType && player instanceof Player) {
// Reset gamemode player has select click type
((Player) player).setGameMode(((SelectClickType) playerClickType.get(uuid)).gameMode);
}
playerClickType.put(uuid, clickType);
// If a timer is already running, cancel it
Optional.ofNullable(playerTimers.get(uuid)).ifPresent(task -> task.cancel());
if (clickType.getClickType() != EnumClickType.SELECT_ITEM) {
// Remove ClickType after 15 seconds if player has not clicked a chest
playerTimers.put(uuid, new BukkitRunnable() {
@Override
public void run() {
playerClickType.remove(uuid);
}
}.runTaskLater(ShopChest.getInstance(), 300));
}
}
/**
* @return Type of the click type
*/
public EnumClickType getClickType() {
return enumClickType;
}
public enum EnumClickType {
CREATE, REMOVE, INFO, OPEN, SELECT_ITEM
}
public static class CreateClickType extends ClickType {
private ShopProduct product;
private double buyPrice;
private double sellPrice;
private ShopType shopType;
public CreateClickType(ShopProduct product, double buyPrice, double sellPrice, ShopType shopType) {
super(EnumClickType.CREATE);
this.product = product;
this.sellPrice = sellPrice;
this.buyPrice = buyPrice;
this.shopType = shopType;
}
/**
* Returns the item, the player has hold in his hands
*/
public ShopProduct getProduct() {
return product;
}
/**
* Returns the buy price, the player has entered
*/
public double getBuyPrice() {
return buyPrice;
}
/**
* Returns the sell price, the player has entered
*/
public double getSellPrice() {
return sellPrice;
}
/**
* Returns the shop type, the player has entered
*/
public ShopType getShopType() {
return shopType;
}
}
public static class SelectClickType extends ClickType {
private ItemStack itemStack;
private GameMode gameMode;
private int amount;
private double buyPrice;
private double sellPrice;
private ShopType shopType;
public SelectClickType(GameMode gameMode, int amount, double buyPrice, double sellPrice, ShopType shopType) {
super(EnumClickType.SELECT_ITEM);
this.gameMode = gameMode;
this.amount = amount;
this.sellPrice = sellPrice;
this.buyPrice = buyPrice;
this.shopType = shopType;
}
/**
* Returns the selected item (or {@code null} if no item has been selected)
*/
public ItemStack getItem() {
return itemStack;
}
/**
* Sets the selected item
* @param itemStack The item to set as selected
*/
public void setItem(ItemStack itemStack) {
this.itemStack = itemStack;
}
/**
* Returns the gamemode, the player was in before entering creative mode
*/
public GameMode getGameMode() {
return gameMode;
}
/**
* Returns the amount, the player has entered
*/
public int getAmount() {
return amount;
}
/**
* Returns the buy price, the player has entered
*/
public double getBuyPrice() {
return buyPrice;
}
/**
* Returns the sell price, the player has entered
*/
public double getSellPrice() {
return sellPrice;
}
/**
* Returns the shop type, the player has entered
*/
public ShopType getShopType() {
return shopType;
}
}
}

View File

@ -1,55 +0,0 @@
package de.epiceric.shopchest.utils;
/**
* Represents a counter for integers greather than or equal to zero.
*/
public final class Counter {
private int value;
/**
* Creates a counter with a starting value of zero
*/
public Counter() {
this(0);
}
/**
* Creates a counter with the given starting value
* @param value the starting value of this counter
*/
public Counter(int value) {
set(value);
}
/**
* Increments the counter by one and returns itself
*/
public final Counter increment() {
this.value++;
return this;
}
/**
* Decrements the counter by one if its value is greater than zero and returns itself
*/
public final Counter decrement() {
this.value = Math.max(0, this.value - 1);
return this;
}
/**
* Sets the counter's value to the given value or zero if the given value is negative
* @param value the value to set the counter to
*/
public final Counter set(int value) {
this.value = Math.max(0, value);
return this;
}
/**
* Returns the current value
*/
public final int get() {
return value;
}
}

View File

@ -1,42 +0,0 @@
package de.epiceric.shopchest.utils;
public class FastMath {
/**
* Fast sqrt, 1.57% precision
*
* @param n value to calculate square root from
* @return the square root of n
*/
public static double sqrt(double n) {
return n * Double.longBitsToDouble(6910470738111508698L - (Double.doubleToRawLongBits(n) >> 1));
}
/**
* Fast acos, 2.9% precision
*
* @param n value to calculate arc cosine from
* @return the arc cosine of n
*/
public static double acos(double n) {
int v = (int) (n * MULTIPLIER + OFFSET);
while (v > PRECISION) v -= PRECISION;
while (v < 0) v += PRECISION;
return acos[v];
}
// Below is lookup table generation
// It is only executed once at initialization
private static final int PRECISION = 512;
private static final double MULTIPLIER = PRECISION / 2D;
private static final double OFFSET = MULTIPLIER + 0.5D; // + 0.5 as cast truncate and don't round
private static final double[] acos = new double[PRECISION + 1];
static {
for (int i = 0; i <= PRECISION; i++) {
acos[i] = Math.acos(i * (2D / PRECISION) - 1);
}
}
}

View File

@ -1,76 +0,0 @@
package de.epiceric.shopchest.utils;
import java.util.Arrays;
import java.util.Map;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionType;
public class ItemUtils {
public static Map<Enchantment, Integer> getEnchantments(ItemStack itemStack) {
if (itemStack.getItemMeta() instanceof EnchantmentStorageMeta) {
EnchantmentStorageMeta esm = (EnchantmentStorageMeta) itemStack.getItemMeta();
return esm.getStoredEnchants();
} else {
return itemStack.getEnchantments();
}
}
public static PotionType getPotionEffect(ItemStack itemStack) {
if (itemStack.getItemMeta() instanceof PotionMeta) {
if (Utils.getMajorVersion() < 9) {
return Potion.fromItemStack(itemStack).getType();
} else {
return ((PotionMeta) itemStack.getItemMeta()).getBasePotionData().getType();
}
}
return null;
}
public static boolean isExtendedPotion(ItemStack itemStack) {
if (itemStack.getItemMeta() instanceof PotionMeta) {
if (Utils.getMajorVersion() < 9) {
return Potion.fromItemStack(itemStack).hasExtendedDuration();
} else {
return ((PotionMeta) itemStack.getItemMeta()).getBasePotionData().isExtended();
}
}
return false;
}
public static boolean isBannerPattern(ItemStack itemStack) {
return itemStack.getType().name().endsWith("BANNER_PATTERN");
}
public static boolean isAir(Material type) {
return Arrays.asList("AIR", "CAVE_AIR", "VOID_AIR").contains(type.name());
}
/**
* Get the {@link ItemStack} from a String
* @param item Serialized ItemStack e.g. {@code "STONE"} or {@code "STONE:1"}
* @return The de-serialized ItemStack or {@code null} if the serialized item is invalid
*/
public static ItemStack getItemStack(String item) {
if (item.trim().isEmpty()) return null;
if (item.contains(":")) {
Material mat = Material.getMaterial(item.split(":")[0]);
if (mat == null) return null;
return new ItemStack(mat, 1, Short.parseShort(item.split(":")[1]));
} else {
Material mat = Material.getMaterial(item);
if (mat == null) return null;
return new ItemStack(mat, 1);
}
}
}

View File

@ -1,75 +0,0 @@
package de.epiceric.shopchest.utils;
public enum Operator {
EQUAL("==") {
@Override
public boolean compare(double a, double b) {
return Double.compare(a, b) == 0;
}
@Override
public boolean compare(String a, String b) {
return a.equals(b);
}
},
NOT_EQUAL("!=") {
@Override
public boolean compare(double a, double b) {
return Double.compare(a, b) != 0;
}
@Override
public boolean compare(String a, String b) {
return !a.equals(b);
}
},
GREATER_THAN(">") {
@Override
public boolean compare(double a, double b) {
return a > b;
}
},
GREATER_THAN_OR_EQUAL(">=") {
@Override
public boolean compare(double a, double b) {
return a >= b;
}
},
LESS_THAN("<") {
@Override
public boolean compare(double a, double b) {
return a < b;
}
},
LESS_THAN_OR_EQUAL("<=") {
@Override
public boolean compare(double a, double b) {
return a <= b;
}
};
private final String symbol;
Operator(String symbol) {
this.symbol = symbol;
}
public static Operator from(String symbol) {
for (Operator operator : values()) {
if (operator.symbol.equals(symbol)) {
return operator;
}
}
throw new IllegalArgumentException();
}
public abstract boolean compare(double a, double b);
public boolean compare(String a, String b) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,24 +0,0 @@
package de.epiceric.shopchest.utils;
public class Permissions {
public static final String CREATE = "shopchest.create";
public static final String CREATE_BUY = "shopchest.create.buy";
public static final String CREATE_SELL = "shopchest.create.sell";
public static final String CREATE_ADMIN = "shopchest.create.admin";
public static final String CREATE_PROTECTED = "shopchest.create.protected";
public static final String REMOVE_OTHER = "shopchest.remove.other";
public static final String REMOVE_ADMIN = "shopchest.remove.admin";
public static final String BUY = "shopchest.buy";
public static final String SELL = "shopchest.sell";
public static final String OPEN_OTHER = "shopchest.openOther";
public static final String UPDATE_NOTIFICATION = "shopchest.notification.update";
public static final String RELOAD = "shopchest.reload";
public static final String UPDATE = "shopchest.update";
public static final String NO_LIMIT = "shopchest.limit.*";
public static final String CONFIG = "shopchest.config";
public static final String EXTEND_OTHER = "shopchest.extend.other";
public static final String EXTEND_PROTECTED = "shopchest.extend.protected";
public static final String BYPASS_EXTERNAL_PLUGIN = "shopchest.external.bypass";
}

View File

@ -1,107 +0,0 @@
package de.epiceric.shopchest.utils;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import de.epiceric.shopchest.ShopChest;
public class ShopUpdater {
private final ShopChest plugin;
private final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
private volatile Thread thread;
public ShopUpdater(ShopChest plugin) {
this.plugin = plugin;
}
/**
* Start task, except if it is already
*/
public void start() {
if (!isRunning()) {
thread = new Thread(() -> {
while (!Thread.interrupted()) {
try {
queue.take().run();
} catch (InterruptedException e) {
break;
}
}
}, "Shop Updater");
thread.start();
}
}
/**
* Stop any running task then start it again
*/
public void restart() {
stop();
start();
}
/**
* Stop task properly
*/
public void stop() {
if (thread != null) {
thread.interrupt();
thread = null;
}
}
/**
* @return whether task is running or not
*/
public boolean isRunning() {
return thread != null;
}
/**
* Queue a task to update shops for the given player
*
* @param player Player to show updates
*/
public void updateShops(Player player) {
queue(() -> plugin.getShopUtils().updateShops(player));
}
/**
* Queue a task to update shops for players in the given world
*
* @param world World in whose players to show updates
*/
public void updateShops(World world) {
queue(() -> {
for (Player player : world.getPlayers()) {
plugin.getShopUtils().updateShops(player);
}
});
}
/**
* Queue a task to update shops for all players
*/
public void updateShops() {
queue(() -> {
for (Player player : Bukkit.getOnlinePlayers()) {
plugin.getShopUtils().updateShops(player);
}
});
}
/**
* Register a task to run before next loop
*
* @param runnable task to run
*/
public void queue(Runnable runnable) {
queue.add(runnable);
}
}

View File

@ -1,524 +0,0 @@
package de.epiceric.shopchest.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.util.Vector;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.event.ShopsLoadedEvent;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.Shop.ShopType;
public class ShopUtils {
private final Map<UUID, Counter> playerShopAmount = new HashMap<>();
// concurrent since it is updated in async task
private final Map<UUID, Location> playerLocation = new ConcurrentHashMap<>();
private final Map<Location, Shop> shopLocation = new ConcurrentHashMap<>();
private final Collection<Shop> shopLocationValues = Collections.unmodifiableCollection(shopLocation.values());
private final ShopChest plugin;
public ShopUtils(ShopChest plugin) {
this.plugin = plugin;
}
/**
* Get the shop at a given location
*
* @param location Location of the shop
* @return Shop at the given location or <b>null</b> if no shop is found there
*/
public Shop getShop(Location location) {
Location newLocation = new Location(location.getWorld(), location.getBlockX(),
location.getBlockY(), location.getBlockZ());
return shopLocation.get(newLocation);
}
/**
* Checks whether there is a shop at a given location
* @param location Location to check
* @return Whether there is a shop at the given location
*/
public boolean isShop(Location location) {
return getShop(location) != null;
}
/**
* Get a collection of all loaded shops
* <p>
* This collection is safe to use for looping over and removing shops.
*
* @return Read-only collection of all shops, may contain duplicates for double chests
*/
public Collection<Shop> getShops() {
return Collections.unmodifiableCollection(new ArrayList<>(shopLocationValues));
}
/**
* Get all shops
*
* @see #getShops()
* @return Copy of collection of all shops, may contain duplicates
* @deprecated Use {@link #getShops()} instead
*/
@Deprecated
public Collection<Shop> getShopsCopy() {
return new ArrayList<>(getShops());
}
/**
* Add a shop
* @param shop Shop to add
* @param addToDatabase Whether the shop should also be added to the database
* @param callback Callback that - if succeeded - returns the ID the shop had or was given (as {@code int})
*/
public void addShop(Shop shop, boolean addToDatabase, Callback<Integer> callback) {
InventoryHolder ih = shop.getInventoryHolder();
plugin.debug("Adding shop... (#" + shop.getID() + ")");
if (ih instanceof DoubleChest) {
DoubleChest dc = (DoubleChest) ih;
Chest r = (Chest) dc.getRightSide();
Chest l = (Chest) dc.getLeftSide();
plugin.debug("Added shop as double chest. (#" + shop.getID() + ")");
shopLocation.put(r.getLocation(), shop);
shopLocation.put(l.getLocation(), shop);
} else {
plugin.debug("Added shop as single chest. (#" + shop.getID() + ")");
shopLocation.put(shop.getLocation(), shop);
}
if (addToDatabase) {
if (shop.getShopType() != ShopType.ADMIN) {
playerShopAmount.compute(shop.getVendor().getUniqueId(), (uuid, amount) -> amount == null ? new Counter(1) : amount.increment());
}
plugin.getShopDatabase().addShop(shop, callback);
} else {
if (callback != null) callback.callSyncResult(shop.getID());
}
}
/**
* Add a shop
* @param shop Shop to add
* @param addToDatabase Whether the shop should also be added to the database
*/
public void addShop(Shop shop, boolean addToDatabase) {
addShop(shop, addToDatabase, null);
}
/**
* Removes (i.e. unloads) all currently loaded shops
*/
public void removeShops() {
shopLocation.forEach((location, shop) -> {
if (!shop.isCreated()) return;
plugin.debug("Removing shop " + shop.getID());
shop.removeItem();
shop.removeHologram();
});
shopLocation.clear();
}
/** Remove a shop. May not work properly if double chest doesn't exist!
* @param shop Shop to remove
* @param removeFromDatabase Whether the shop should also be removed from the database
* @param callback Callback that - if succeeded - returns null
* @see ShopUtils#removeShopById(int, boolean, Callback)
*/
public void removeShop(Shop shop, boolean removeFromDatabase, Callback<Void> callback) {
plugin.debug("Removing shop (#" + shop.getID() + ")");
if (shop.isCreated()) {
InventoryHolder ih = shop.getInventoryHolder();
if (ih instanceof DoubleChest) {
DoubleChest dc = (DoubleChest) ih;
Chest r = (Chest) dc.getRightSide();
Chest l = (Chest) dc.getLeftSide();
shopLocation.remove(r.getLocation());
shopLocation.remove(l.getLocation());
} else {
shopLocation.remove(shop.getLocation());
}
shop.removeItem();
shop.removeHologram();
}
if (removeFromDatabase) {
if (shop.getShopType() != ShopType.ADMIN) {
playerShopAmount.compute(shop.getVendor().getUniqueId(), (uuid, amount) -> amount == null ? new Counter() : amount.decrement());
}
plugin.getShopDatabase().removeShop(shop, callback);
} else {
if (callback != null) callback.callSyncResult(null);
}
}
/**
* Remove a shop. May not work properly if double chest doesn't exist!
* @param shop Shop to remove
* @param removeFromDatabase Whether the shop should also be removed from the database
* @see ShopUtils#removeShopById(int, boolean)
*/
public void removeShop(Shop shop, boolean removeFromDatabase) {
removeShop(shop, removeFromDatabase, null);
}
/**
* Remove a shop by its ID
* @param shopId ID of the shop to remove
* @param removeFromDatabase Whether the shop should also be removed from the database
* @param callback Callback that - if succeeded - returns null
*/
public void removeShopById(int shopId, boolean removeFromDatabase, Callback<Void> callback) {
Map<Location, Shop> toRemove = shopLocation.entrySet().stream()
.filter(e -> e.getValue().getID() == shopId)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
plugin.debug(String.format("Removing %d shop(s) with ID %d", toRemove.size(), shopId));
if (toRemove.isEmpty()) {
if (callback != null) callback.callSyncResult(null);
return;
}
toRemove.forEach((loc, shop) -> {
shopLocation.remove(loc);
shop.removeItem();
shop.removeHologram();
});
Shop first = toRemove.values().iterator().next();
boolean isAdmin = first.getShopType() == ShopType.ADMIN;
UUID vendorUuid = first.getVendor().getUniqueId();
// Database#removeShop removes shop by ID so this only needs to be called once
if (removeFromDatabase) {
if (!isAdmin) {
playerShopAmount.compute(vendorUuid, (uuid, amount) -> amount == null ? new Counter() : amount.decrement());
}
plugin.getShopDatabase().removeShop(toRemove.values().iterator().next(), callback);
} else {
if (callback != null) callback.callSyncResult(null);
}
}
/**
* Remove a shop by its ID
* @param shopId ID of the shop to remove
* @param removeFromDatabase Whether the shop should also be removed from the database
*/
public void removeShopById(int shopId, boolean removeFromDatabase) {
removeShopById(shopId, removeFromDatabase, null);
}
/**
* Get the shop limits of a player
* @param p Player, whose shop limits should be returned
* @return The shop limits of the given player
*/
public int getShopLimit(Player p) {
int limit = 0;
boolean useDefault = true;
for (PermissionAttachmentInfo permInfo : p.getEffectivePermissions()) {
if (permInfo.getPermission().startsWith("shopchest.limit.") && p.hasPermission(permInfo.getPermission())) {
if (permInfo.getPermission().equalsIgnoreCase(Permissions.NO_LIMIT)) {
limit = -1;
useDefault = false;
break;
} else {
String[] spl = permInfo.getPermission().split("shopchest.limit.");
if (spl.length > 1) {
try {
int newLimit = Integer.valueOf(spl[1]);
if (newLimit < 0) {
limit = -1;
break;
}
limit = Math.max(limit, newLimit);
useDefault = false;
} catch (NumberFormatException ignored) {
/* Ignore and continue */
}
}
}
}
}
if (limit < -1) limit = -1;
return (useDefault ?Config.defaultLimit : limit);
}
/**
* Get the amount of shops of a player
* @param p Player, whose shops should be counted
* @return The amount of a shops a player has (if {@link Config#excludeAdminShops} is true, admin shops won't be counted)
*/
public int getShopAmount(OfflinePlayer p) {
return playerShopAmount.getOrDefault(p.getUniqueId(), new Counter()).get();
}
/**
* Get all shops of a player from the database without loading them
* @param p Player, whose shops should be get
* @param callback Callback that returns a collection of the given player's shops
*/
public void getShops(OfflinePlayer p, Callback<Collection<Shop>> callback) {
plugin.getShopDatabase().getShops(p.getUniqueId(), new Callback<Collection<Shop>>(plugin) {
@Override
public void onResult(Collection<Shop> result) {
Set<Shop> shops = new HashSet<>();
for (Shop playerShop : result) {
Shop loadedShop = getShop(playerShop.getLocation());
if (loadedShop != null && loadedShop.equals(playerShop)) {
shops.add(loadedShop);
} else {
shops.add(playerShop);
}
}
if (callback != null) callback.onResult(shops);
}
@Override
public void onError(Throwable throwable) {
if (callback != null) callback.onError(throwable);
}
});
}
/**
* Loads the amount of shops for each player
* @param callback Callback that returns the amount of shops for each player
*/
public void loadShopAmounts(final Callback<Map<UUID, Integer>> callback) {
plugin.getShopDatabase().getShopAmounts(new Callback<Map<UUID,Integer>>(plugin) {
@Override
public void onResult(Map<UUID, Integer> result) {
playerShopAmount.clear();
result.forEach((uuid, amount) -> playerShopAmount.put(uuid, new Counter(amount)));
if (callback != null) callback.onResult(result);
}
@Override
public void onError(Throwable throwable) {
if (callback != null) callback.onError(throwable);
}
});
}
/**
* Gets all shops in the given chunk from the database and adds them to the server
* @param chunk The chunk to load shops from
* @param callback Callback that returns the amount of shops added if succeeded
* @see ShopUtils#loadShops(Chunk[], Callback)
*/
public void loadShops(final Chunk chunk, final Callback<Integer> callback) {
loadShops(new Chunk[] {chunk}, callback);
}
/**
* Gets all shops in the given chunks from the database and adds them to the server
* @param chunk The chunks to load shops from
* @param callback Callback that returns the amount of shops added if succeeded
* @see ShopUtils#loadShops(Chunk Callback)
*/
public void loadShops(final Chunk[] chunks, final Callback<Integer> callback) {
plugin.getShopDatabase().getShopsInChunks(chunks, new Callback<Collection<Shop>>(plugin) {
@Override
public void onResult(Collection<Shop> result) {
Collection<Shop> loadedShops = new HashSet<>();
for (Shop shop : result) {
Location loc = shop.getLocation();
// Don't add shop if shop is already loaded
if (shopLocation.containsKey(loc)) {
continue;
}
int x = loc.getBlockX() / 16;
int z = loc.getBlockZ() / 16;
// Don't add shop if chunk is no longer loaded
if (!loc.getWorld().isChunkLoaded(x, z)) {
continue;
}
if (shop.create(true)) {
addShop(shop, false);
loadedShops.add(shop);
}
}
if (callback != null) callback.onResult(loadedShops.size());
Bukkit.getPluginManager().callEvent(new ShopsLoadedEvent(Collections.unmodifiableCollection(loadedShops)));
}
@Override
public void onError(Throwable throwable) {
if (callback != null) callback.onError(throwable);
}
});
}
/**
* Update hologram and item of all shops for a player
* @param player Player to show the updates
*/
public void updateShops(Player player) {
updateShops(player, false);
}
/**
* Update hologram and item of all shops for a player
* @param player Player to show the updates
* @param force Whether update should be forced even if player has not moved
*/
public void updateShops(Player player, boolean force) {
if (!force && player.getLocation().equals(playerLocation.get(player.getUniqueId()))) {
// Player has not moved, so don't calculate shops again.
return;
}
if (Config.onlyShowShopsInSight) {
updateVisibleShops(player);
} else {
updateNearestShops(player);
}
playerLocation.put(player.getUniqueId(), player.getLocation());
}
/**
* Remove a saved location of a player to force a recalculation
* of whether the hologram should be visible.
* This should only be called when really needed
* @param player Player whose saved location will be reset
*/
public void resetPlayerLocation(Player player) {
playerLocation.remove(player.getUniqueId());
}
private void updateVisibleShops(Player player) {
double itemDistSquared = Math.pow(Config.maximalItemDistance, 2);
double maxDist = Config.maximalDistance;
double nearestDistSquared = Double.MAX_VALUE;
Shop nearestShop = null;
Location pLoc = player.getEyeLocation();
Vector pDir = pLoc.getDirection();
// Display holograms based on sight
for (double i = 0; i <= maxDist; i++) {
Location loc = pLoc.clone();
Vector dir = pDir.clone();
double factor = Math.min(i, maxDist);
loc.add(dir.multiply(factor));
Location locBelow = loc.clone().subtract(0, 1, 0);
// Check block below as player may look at hologram
Shop shop = getShop(loc);
if (shop == null) {
shop = getShop(locBelow);
}
if (shop != null && shop.hasHologram()) {
double distSquared = pLoc.distanceSquared(loc);
if (distSquared < nearestDistSquared) {
nearestDistSquared = distSquared;
nearestShop = shop;
}
}
}
for (Shop shop : getShops()) {
if (!shop.equals(nearestShop) && shop.hasHologram()) {
shop.getHologram().hidePlayer(player);
}
// Display item based on distance
Location shopLocation = shop.getLocation();
if (shopLocation.getWorld().getName().equals(player.getWorld().getName())) {
double distSquared = shop.getLocation().distanceSquared(player.getLocation());
if (shop.hasItem()) {
if (distSquared <= itemDistSquared) {
shop.getItem().showPlayer(player);
} else {
shop.getItem().hidePlayer(player);
}
}
}
}
if (nearestShop != null) {
nearestShop.getHologram().showPlayer(player);
}
}
private void updateNearestShops(Player p) {
double holoDistSqr = Math.pow(Config.maximalDistance, 2);
double itemDistSqr = Math.pow(Config.maximalItemDistance, 2);
Location playerLocation = p.getLocation();
for (Shop shop : getShops()) {
if (playerLocation.getWorld().getName().equals(shop.getLocation().getWorld().getName())) {
double distSqr = shop.getLocation().distanceSquared(playerLocation);
if (shop.hasHologram()) {
if (distSqr <= holoDistSqr) {
shop.getHologram().showPlayer(p);
} else {
shop.getHologram().hidePlayer(p);
}
}
if (shop.hasItem()) {
if (distSqr <= itemDistSqr) {
shop.getItem().showPlayer(p);
} else {
shop.getItem().hidePlayer(p);
}
}
}
}
}
}

View File

@ -1,88 +0,0 @@
package de.epiceric.shopchest.utils;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import de.epiceric.shopchest.ShopChest;
public class UpdateChecker {
private ShopChest plugin;
private String version;
private String link;
public UpdateChecker(ShopChest plugin) {
this.plugin = plugin;
}
/**
* Check if an update is needed
*
* @return {@link UpdateCheckerResult#TRUE} if an update is available,
* {@link UpdateCheckerResult#FALSE} if no update is needed or
* {@link UpdateCheckerResult#ERROR} if an error occurred
*/
public UpdateCheckerResult check() {
try {
plugin.debug("Checking for updates...");
URL url = new URL("https://api.spiget.org/v2/resources/11431/versions?size=1&page=1&sort=-releaseDate");
URLConnection conn = url.openConnection();
conn.setRequestProperty("User-Agent", "ShopChest/UpdateChecker");
InputStreamReader reader = new InputStreamReader(conn.getInputStream());
JsonElement element = new JsonParser().parse(reader);
if (element.isJsonArray()) {
JsonObject result = element.getAsJsonArray().get(0).getAsJsonObject();
String id = result.get("id").getAsString();
version = result.get("name").getAsString();
link = "https://www.spigotmc.org/resources/shopchest.11431/download?version=" + id;
} else {
plugin.debug("Failed to check for updates");
plugin.debug("Result: " + element.toString());
return UpdateCheckerResult.ERROR;
}
if (plugin.getDescription().getVersion().equals(version)) {
plugin.debug("No update found");
return UpdateCheckerResult.FALSE;
} else {
plugin.debug("Update found: " + version);
return UpdateCheckerResult.TRUE;
}
} catch (Exception e) {
plugin.debug("Failed to check for updates");
plugin.debug(e);
return UpdateCheckerResult.ERROR;
}
}
/**
* @return Latest Version or <b>null</b> if no update is available
*/
public String getVersion() {
return version;
}
/**
* @return Download Link of the latest version of <b>null</b> if no update is available
*/
public String getLink() {
return link;
}
public enum UpdateCheckerResult {
TRUE,
FALSE,
ERROR
}
}

View File

@ -1,649 +0,0 @@
package de.epiceric.shopchest.utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.Vector;
import org.inventivetalent.reflection.resolver.FieldResolver;
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.nms.CustomBookMeta;
import de.epiceric.shopchest.nms.JsonBuilder;
import de.epiceric.shopchest.shop.Shop;
public class Utils {
static NMSClassResolver nmsClassResolver = new NMSClassResolver();
static Class<?> entityClass = nmsClassResolver.resolveSilent("world.entity.Entity");
static Class<?> entityArmorStandClass = nmsClassResolver.resolveSilent("world.entity.decoration.EntityArmorStand");
static Class<?> entityItemClass = nmsClassResolver.resolveSilent("world.entity.item.EntityItem");
static Class<?> dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher");
static Class<?> dataWatcherObjectClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcherObject");
static Class<?> chatSerializerClass = nmsClassResolver.resolveSilent("ChatSerializer", "network.chat.IChatBaseComponent$ChatSerializer");
private Utils() {}
/**
* Check if two items are similar to each other
* @param itemStack1 The first item
* @param itemStack2 The second item
* @return {@code true} if the given items are similar or {@code false} if not
*/
public static boolean isItemSimilar(ItemStack itemStack1, ItemStack itemStack2) {
if (itemStack1 == null || itemStack2 == null) {
return false;
}
ItemMeta itemMeta1 = itemStack1.getItemMeta();
ItemMeta itemMeta2 = itemStack2.getItemMeta();
if (itemMeta1 instanceof BookMeta && itemMeta2 instanceof BookMeta) {
BookMeta bookMeta1 = (BookMeta) itemStack1.getItemMeta();
BookMeta bookMeta2 = (BookMeta) itemStack2.getItemMeta();
if ((getMajorVersion() == 9 && getRevision() == 1) || getMajorVersion() == 8) {
CustomBookMeta.Generation generation1 = CustomBookMeta.getGeneration(itemStack1);
CustomBookMeta.Generation generation2 = CustomBookMeta.getGeneration(itemStack2);
if (generation1 == null) CustomBookMeta.setGeneration(itemStack1, CustomBookMeta.Generation.ORIGINAL);
if (generation2 == null) CustomBookMeta.setGeneration(itemStack2, CustomBookMeta.Generation.ORIGINAL);
} else {
if (bookMeta1.getGeneration() == null) bookMeta1.setGeneration(BookMeta.Generation.ORIGINAL);
if (bookMeta2.getGeneration() == null) bookMeta2.setGeneration(BookMeta.Generation.ORIGINAL);
}
itemStack1.setItemMeta(bookMeta1);
itemStack2.setItemMeta(bookMeta2);
itemStack1 = decode(encode(itemStack1));
itemStack2 = decode(encode(itemStack2));
}
return itemStack1.isSimilar(itemStack2);
}
/**
* Gets the amount of items in an inventory
*
* @param inventory The inventory, in which the items are counted
* @param itemStack The items to count
* @return Amount of given items in the given inventory
*/
public static int getAmount(Inventory inventory, ItemStack itemStack) {
int amount = 0;
ArrayList<ItemStack> inventoryItems = new ArrayList<>();
if (inventory instanceof PlayerInventory) {
for (int i = 0; i < 37; i++) {
if (i == 36) {
if (getMajorVersion() < 9) {
break;
}
i = 40;
}
inventoryItems.add(inventory.getItem(i));
}
} else {
for (int i = 0; i < inventory.getSize(); i++) {
inventoryItems.add(inventory.getItem(i));
}
}
for (ItemStack item : inventoryItems) {
if (isItemSimilar(item, itemStack)) {
amount += item.getAmount();
}
}
return amount;
}
/**
* Get the amount of the given item, that fits in the given inventory
*
* @param inventory Inventory, where to search for free space
* @param itemStack Item, of which the amount that fits in the inventory should be returned
* @return Amount of the given item, that fits in the given inventory
*/
public static int getFreeSpaceForItem(Inventory inventory, ItemStack itemStack) {
HashMap<Integer, Integer> slotFree = new HashMap<>();
if (inventory instanceof PlayerInventory) {
for (int i = 0; i < 37; i++) {
if (i == 36) {
if (getMajorVersion() < 9) {
break;
}
i = 40;
}
ItemStack item = inventory.getItem(i);
if (item == null || item.getType() == Material.AIR) {
slotFree.put(i, itemStack.getMaxStackSize());
} else {
if (isItemSimilar(item, itemStack)) {
int amountInSlot = item.getAmount();
int amountToFullStack = itemStack.getMaxStackSize() - amountInSlot;
slotFree.put(i, amountToFullStack);
}
}
}
} else {
for (int i = 0; i < inventory.getSize(); i++) {
ItemStack item = inventory.getItem(i);
if (item == null || item.getType() == Material.AIR) {
slotFree.put(i, itemStack.getMaxStackSize());
} else {
if (isItemSimilar(item, itemStack)) {
int amountInSlot = item.getAmount();
int amountToFullStack = itemStack.getMaxStackSize() - amountInSlot;
slotFree.put(i, amountToFullStack);
}
}
}
}
int freeAmount = 0;
for (int value : slotFree.values()) {
freeAmount += value;
}
return freeAmount;
}
/**
* @param p Player whose item in his main hand should be returned
* @return {@link ItemStack} in his main hand, or {@code null} if he doesn't hold one
*/
public static ItemStack getItemInMainHand(Player p) {
if (getMajorVersion() < 9) {
if (p.getItemInHand().getType() == Material.AIR)
return null;
else
return p.getItemInHand();
}
if (p.getInventory().getItemInMainHand().getType() == Material.AIR)
return null;
else
return p.getInventory().getItemInMainHand();
}
/**
* @param p Player whose item in his off hand should be returned
* @return {@link ItemStack} in his off hand, or {@code null} if he doesn't hold one or the server version is below 1.9
*/
public static ItemStack getItemInOffHand(Player p) {
if (getMajorVersion() < 9)
return null;
else if (p.getInventory().getItemInOffHand().getType() == Material.AIR)
return null;
else
return p.getInventory().getItemInOffHand();
}
/**
* @param p Player whose item in his hand should be returned
* @return Item in his main hand, or the item in his off if he doesn't have one in this main hand, or {@code null}
* if he doesn't have one in both hands
*/
public static ItemStack getPreferredItemInHand(Player p) {
if (getMajorVersion() < 9)
return getItemInMainHand(p);
else if (getItemInMainHand(p) != null)
return getItemInMainHand(p);
else
return getItemInOffHand(p);
}
/**
* @param p Player to check if he has an axe in one of his hands
* @return Whether a player has an axe in one of his hands
*/
public static boolean hasAxeInHand(Player p) {
List<String> axes;
if (Utils.getMajorVersion() < 13)
axes = Arrays.asList("WOOD_AXE", "STONE_AXE", "IRON_AXE", "GOLD_AXE", "DIAMOND_AXE");
else
axes = Arrays.asList("WOODEN_AXE", "STONE_AXE", "IRON_AXE", "GOLDEN_AXE", "DIAMOND_AXE");
ItemStack item = getItemInMainHand(p);
if (item == null || !axes.contains(item.getType().toString())) {
item = getItemInOffHand(p);
}
return item != null && axes.contains(item.getType().toString());
}
/**
* <p>Check if a player is allowed to create a shop that sells or buys the given item.</p>
* @param player Player to check
* @param item Item to be sold or bought
* @param buy Whether buying should be enabled
* @param sell Whether selling should be enabled
* @return Whether the player is allowed
*/
public static boolean hasPermissionToCreateShop(Player player, ItemStack item, boolean buy, boolean sell) {
if (hasPermissionToCreateShop(player, item, Permissions.CREATE)) {
return true;
} else if (!sell && buy && hasPermissionToCreateShop(player, item,Permissions.CREATE_BUY)) {
return true;
} else if (!buy && sell && hasPermissionToCreateShop(player, item, Permissions.CREATE_SELL)) {
return true;
} else if (buy && sell && hasPermissionToCreateShop(player, item, Permissions.CREATE_BUY, Permissions.CREATE_SELL)) {
return true;
}
return false;
}
private static boolean hasPermissionToCreateShop(Player player, ItemStack item, String... permissions) {
for (String permission : permissions) {
boolean b1 = false;
boolean b2 = false;
boolean b3 = false;
if (player.hasPermission(permission)) {
b1 = true;
}
if (item != null) {
if (item.getDurability() == 0) {
String perm1 = permission + "." + item.getType().toString();
String perm2 = permission + "." + item.getType().toString() + ".0";
if (player.hasPermission(perm1) || player.hasPermission(perm2)) {
b2 = true;
}
}
if (player.hasPermission(permission + "." + item.getType().toString() + "." + item.getDurability())) {
b3 = true;
}
}
if (!(b1 || b2 || b3)) {
return false;
}
}
return true;
}
/**
* Get a set for the location(s) of the shop's chest(s)
* @param shop The shop
* @return A set of 1 or 2 locations
*/
public static Set<Location> getChestLocations(Shop shop) {
Set<Location> chestLocations = new HashSet<>();
InventoryHolder ih = shop.getInventoryHolder();
if (ih instanceof DoubleChest) {
DoubleChest dc = (DoubleChest) ih;
chestLocations.add(((Chest) dc.getLeftSide()).getLocation());
chestLocations.add(((Chest) dc.getRightSide()).getLocation());
} else {
chestLocations.add(shop.getLocation());
}
return chestLocations;
}
/**
* Send a clickable update notification to the given player.
* @param plugin An instance of the {@link ShopChest} plugin
* @param p The player to receive the notification
*/
public static void sendUpdateMessage(ShopChest plugin, Player p) {
JsonBuilder jb = new JsonBuilder(plugin);
Map<String, JsonBuilder.Part> hoverEvent = new HashMap<>();
hoverEvent.put("action", new JsonBuilder.Part("show_text"));
hoverEvent.put("value", new JsonBuilder.Part(LanguageUtils.getMessage(Message.UPDATE_CLICK_TO_DOWNLOAD)));
Map<String, JsonBuilder.Part> clickEvent = new HashMap<>();
clickEvent.put("action", new JsonBuilder.Part("open_url"));
clickEvent.put("value", new JsonBuilder.Part(plugin.getDownloadLink()));
JsonBuilder.PartMap rootPart = JsonBuilder.parse(LanguageUtils.getMessage(Message.UPDATE_AVAILABLE,
new Replacement(Placeholder.VERSION, plugin.getLatestVersion()))).toMap();
rootPart.setValue("hoverEvent", new JsonBuilder.PartMap(hoverEvent));
rootPart.setValue("clickEvent", new JsonBuilder.PartMap(clickEvent));
jb.setRootPart(rootPart);
jb.sendJson(p);
}
/**
* Create a NMS data watcher object to send via a {@code PacketPlayOutEntityMetadata} packet.
* Gravity will be disabled and the custom name will be displayed if available.
* @param customName Custom Name of the entity or {@code null}
* @param nmsItemStack NMS ItemStack or {@code null} if armor stand
*/
public static Object createDataWatcher(String customName, Object nmsItemStack) {
String version = getServerVersion();
int majorVersion = getMajorVersion();
try {
byte entityFlags = nmsItemStack == null ? (byte) 0b100000 : 0; // invisible if armor stand
byte armorStandFlags = nmsItemStack == null ? (byte) 0b10000 : 0; // marker (since 1.8_R2)
Object dataWatcher = dataWatcherClass.getConstructor(entityClass).newInstance((Object) null);
if (majorVersion < 9) {
if (getRevision() == 1) armorStandFlags = 0; // Marker not supported on 1.8_R1
Method a = dataWatcherClass.getMethod("a", int.class, Object.class);
a.invoke(dataWatcher, 0, entityFlags); // flags
a.invoke(dataWatcher, 1, (short) 300); // air ticks (?)
a.invoke(dataWatcher, 3, (byte) (customName != null ? 1 : 0)); // custom name visible
a.invoke(dataWatcher, 2, customName != null ? customName : ""); // custom name
a.invoke(dataWatcher, 4, (byte) 1); // silent
a.invoke(dataWatcher, 10, nmsItemStack == null ? armorStandFlags : nmsItemStack); // item / armor stand flags
} else {
Method register = dataWatcherClass.getMethod("register", dataWatcherObjectClass, Object.class);
String[] dataWatcherObjectFieldNames;
if ("v1_9_R1".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"ax", "ay", "aA", "az", "aB", null, "c", "a"};
} else if ("v1_9_R2".equals(version)){
dataWatcherObjectFieldNames = new String[] {"ay", "az", "aB", "aA", "aC", null, "c", "a"};
} else if ("v1_10_R1".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"aa", "az", "aB", "aA", "aC", "aD", "c", "a"};
} else if ("v1_11_R1".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"Z", "az", "aB", "aA", "aC", "aD", "c", "a"};
} else if ("v1_12_R1".equals(version) || "v1_12_R2".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"Z", "aA", "aC", "aB", "aD", "aE", "c", "a"};
} else if ("v1_13_R1".equals(version) || "v1_13_R2".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"ac", "aD", "aF", "aE", "aG", "aH", "b", "a"};
} else if ("v1_14_R1".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"W", "AIR_TICKS", "aA", "az", "aB", "aC", "ITEM", "b"};
} else if ("v1_15_R1".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"T", "AIR_TICKS", "aA", "az", "aB", "aC", "ITEM", "b"};
} else if ("v1_16_R1".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"T", "AIR_TICKS", "ay", "ax", "az", "aA", "ITEM", "b"};
} else if ("v1_16_R2".equals(version) || "v1_16_R3".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"S", "AIR_TICKS", "ar", "aq", "as", "at", "ITEM", "b"};
} else if ("v1_17_R1".equals(version)) {
dataWatcherObjectFieldNames = new String[] {"Z", "aI", "aK", "aJ", "aL", "aM", "c", "bG"};
} else {
return null;
}
Field fEntityFlags = entityClass.getDeclaredField(dataWatcherObjectFieldNames[0]);
Field fAirTicks = entityClass.getDeclaredField(dataWatcherObjectFieldNames[1]);
Field fNameVisible = entityClass.getDeclaredField(dataWatcherObjectFieldNames[2]);
Field fCustomName = entityClass.getDeclaredField(dataWatcherObjectFieldNames[3]);
Field fSilent = entityClass.getDeclaredField(dataWatcherObjectFieldNames[4]);
Field fNoGravity = majorVersion >= 10 ? entityClass.getDeclaredField(dataWatcherObjectFieldNames[5]) : null;
Field fItem = entityItemClass.getDeclaredField(dataWatcherObjectFieldNames[6]);
Field fArmorStandFlags = entityArmorStandClass.getDeclaredField(dataWatcherObjectFieldNames[7]);
fEntityFlags.setAccessible(true);
fAirTicks.setAccessible(true);
fNameVisible.setAccessible(true);
fCustomName.setAccessible(true);
fSilent.setAccessible(true);
if (majorVersion >= 10) fNoGravity.setAccessible(true);
fItem.setAccessible(true);
fArmorStandFlags.setAccessible(true);
register.invoke(dataWatcher, fEntityFlags.get(null), entityFlags);
register.invoke(dataWatcher, fAirTicks.get(null), 300);
register.invoke(dataWatcher, fNameVisible.get(null), customName != null);
register.invoke(dataWatcher, fSilent.get(null), true);
if (majorVersion < 13) register.invoke(dataWatcher, fCustomName.get(null), customName != null ? customName : "");
if (nmsItemStack != null) {
register.invoke(dataWatcher, fItem.get(null), majorVersion < 11 ? com.google.common.base.Optional.of(nmsItemStack) : nmsItemStack);
} else {
register.invoke(dataWatcher, fArmorStandFlags.get(null), armorStandFlags);
}
if (majorVersion >= 10) {
register.invoke(dataWatcher, fNoGravity.get(null), true);
if (majorVersion >= 13) {
if (customName != null) {
Object iChatBaseComponent = chatSerializerClass.getMethod("a", String.class).invoke(null, JsonBuilder.parse(customName).toString());
register.invoke(dataWatcher, fCustomName.get(null), Optional.of(iChatBaseComponent));
} else {
register.invoke(dataWatcher, fCustomName.get(null), Optional.empty());
}
}
}
}
return dataWatcher;
} catch (InstantiationException | InvocationTargetException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
ShopChest.getInstance().getLogger().severe("Failed to create data watcher!");
ShopChest.getInstance().debug("Failed to create data watcher");
ShopChest.getInstance().debug(e);
}
return null;
}
/**
* Get a free entity ID for use in {@link #createPacketSpawnEntity(ShopChest, int, UUID, Location, Vector, EntityType)}
*
* @return The id or {@code -1} if a free entity ID could not be retrieved.
*/
public static int getFreeEntityId() {
try {
Field entityCountField = new FieldResolver(entityClass).resolve("entityCount", "b");
entityCountField.setAccessible(true);
if (entityCountField.getType() == int.class) {
int id = entityCountField.getInt(null);
entityCountField.setInt(null, id+1);
return id;
} else if (entityCountField.getType() == AtomicInteger.class) {
return ((AtomicInteger) entityCountField.get(null)).incrementAndGet();
}
return -1;
} catch (Exception e) {
return -1;
}
}
/**
* Create a {@code PacketPlayOutSpawnEntity} object.
* Only {@link EntityType#ARMOR_STAND} and {@link EntityType#DROPPED_ITEM} are supported!
*/
public static Object createPacketSpawnEntity(ShopChest plugin, int id, UUID uuid, Location loc, EntityType type) {
try {
Class<?> packetPlayOutSpawnEntityClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutSpawnEntity");
Class<?> entityTypesClass = nmsClassResolver.resolveSilent("world.entity.EntityTypes");
Class<?> vec3dClass = nmsClassResolver.resolveSilent("world.phys.Vec3D");
boolean isPre9 = getMajorVersion() < 9;
boolean isPre14 = getMajorVersion() < 14;
double y = loc.getY();
if (type == EntityType.ARMOR_STAND && !getServerVersion().equals("v1_8_R1")) {
// Marker armor stand => lift by normal armor stand height
y += 1.975;
}
if (getMajorVersion() >= 17) {
// Empty packet constructor does not exist anymore in 1.17+
Constructor<?> c = packetPlayOutSpawnEntityClass.getConstructor(int.class, UUID.class, double.class, double.class, double.class,
float.class, float.class, entityTypesClass, int.class, vec3dClass);
Object vec3d = vec3dClass.getField("a").get(null);
Object entityType = entityTypesClass.getField(type == EntityType.ARMOR_STAND ? "c" : "Q").get(null);
return c.newInstance(id, uuid, loc.getX(), y, loc.getZ(), 0f, 0f, entityType, 0, vec3d);
}
Object packet = packetPlayOutSpawnEntityClass.getConstructor().newInstance();
Field[] fields = new Field[12];
fields[0] = packetPlayOutSpawnEntityClass.getDeclaredField("a"); // ID
fields[1] = packetPlayOutSpawnEntityClass.getDeclaredField("b"); // UUID (Only 1.9+)
fields[2] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "b" : "c"); // Loc X
fields[3] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "c" : "d"); // Loc Y
fields[4] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "d" : "e"); // Loc Z
fields[5] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "e" : "f"); // Mot X
fields[6] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "f" : "g"); // Mot Y
fields[7] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "g" : "h"); // Mot Z
fields[8] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "h" : "i"); // Pitch
fields[9] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "i" : "j"); // Yaw
fields[10] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "j" : "k"); // Type
fields[11] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "k" : "l"); // Data
for (Field field : fields) {
field.setAccessible(true);
}
Object entityType = null;
if (!isPre14) {
entityType = entityTypesClass.getField(type == EntityType.ARMOR_STAND ? "ARMOR_STAND" : "ITEM").get(null);
}
fields[0].set(packet, id);
if (!isPre9) fields[1].set(packet, uuid);
if (isPre9) {
fields[2].set(packet, (int)(loc.getX() * 32));
fields[3].set(packet, (int)(y * 32));
fields[4].set(packet, (int)(loc.getZ() * 32));
} else {
fields[2].set(packet, loc.getX());
fields[3].set(packet, y);
fields[4].set(packet, loc.getZ());
}
fields[5].set(packet, 0);
fields[6].set(packet, 0);
fields[7].set(packet, 0);
fields[8].set(packet, 0);
fields[9].set(packet, 0);
if (isPre14) fields[10].set(packet, type == EntityType.ARMOR_STAND ? 78 : 2);
else fields[10].set(packet, entityType);
fields[11].set(packet, 0);
return packet;
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
plugin.getLogger().severe("Failed to create packet to spawn entity!");
plugin.debug("Failed to create packet to spawn entity!");
plugin.debug(e);
return null;
}
}
/**
* Send a packet to a player
* @param plugin An instance of the {@link ShopChest} plugin
* @param packet Packet to send
* @param player Player to which the packet should be sent
* @return {@code true} if the packet was sent, or {@code false} if an exception was thrown
*/
public static boolean sendPacket(ShopChest plugin, Object packet, Player player) {
try {
if (packet == null) {
plugin.debug("Failed to send packet: Packet is null");
return false;
}
Class<?> packetClass = nmsClassResolver.resolveSilent("network.protocol.Packet");
if (packetClass == null) {
plugin.debug("Failed to send packet: Could not find Packet class");
return false;
}
Object nmsPlayer = player.getClass().getMethod("getHandle").invoke(player);
Field fConnection = (new FieldResolver(nmsPlayer.getClass())).resolve("playerConnection", "b");
Object playerConnection = fConnection.get(nmsPlayer);
playerConnection.getClass().getMethod("sendPacket", packetClass).invoke(playerConnection, packet);
return true;
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException e) {
plugin.getLogger().severe("Failed to send packet " + packet.getClass().getName());
plugin.debug("Failed to send packet " + packet.getClass().getName());
plugin.debug(e);
return false;
}
}
/**
* @return The current server version with revision number (e.g. v1_9_R2, v1_10_R1)
*/
public static String getServerVersion() {
String packageName = Bukkit.getServer().getClass().getPackage().getName();
return packageName.substring(packageName.lastIndexOf('.') + 1);
}
/**
* @return The revision of the current server version (e.g. <i>2</i> for v1_9_R2, <i>1</i> for v1_10_R1)
*/
public static int getRevision() {
return Integer.parseInt(getServerVersion().substring(getServerVersion().length() - 1));
}
/**
* @return The major version of the server (e.g. <i>9</i> for 1.9.2, <i>10</i> for 1.10)
*/
public static int getMajorVersion() {
return Integer.parseInt(getServerVersion().split("_")[1]);
}
/**
* Encodes an {@link ItemStack} in a Base64 String
* @param itemStack {@link ItemStack} to encode
* @return Base64 encoded String
*/
public static String encode(ItemStack itemStack) {
YamlConfiguration config = new YamlConfiguration();
config.set("i", itemStack);
return Base64.getEncoder().encodeToString(config.saveToString().getBytes(StandardCharsets.UTF_8));
}
/**
* Decodes an {@link ItemStack} from a Base64 String
* @param string Base64 encoded String to decode
* @return Decoded {@link ItemStack}
*/
public static ItemStack decode(String string) {
YamlConfiguration config = new YamlConfiguration();
try {
config.loadFromString(new String(Base64.getDecoder().decode(string), StandardCharsets.UTF_8));
} catch (IllegalArgumentException | InvalidConfigurationException e) {
e.printStackTrace();
return null;
}
return config.getItemStack("i", null);
}
}

View File

@ -1,224 +0,0 @@
# ===============================================
# ====== Configuration File of 'ShopChest' ======
# ===============================================
#
# Lines starting with '#' are comments and are ignored by the server.
#
# You can find item names in the 'item_names.txt' file.
# Set the main command you have to enter to manage the shops.
# (default: "/shop ...")
main-command-name: "shop"
# Set the language file for all translatable messages or names.
# The value must equal to the name of one of a file in the 'lang' folder
# (without the '.lang' extension).
language-file: "en_US"
# Set the item with which a player can click a shop to retrieve information.
# You can set this to an empty string ("") to disable this feature.
shop-info-item: "STICK"
# Set whether buys or sells need to be confirmed by the player
# in order to prevent accidents.
confirm-shopping: false
# Set whether players should be able to select the shop item from the
# creative inventory if they don't hold an item in their hand.
creative-select-item: true
# Set whether the (current) shop creation price should be refunded
# when the shop is removed by its creator.
refund-shop-creation: false
# Set whether the plugin will check for updates on server start
# and notify permitted players on join.
# The command is not affected by this setting and will continue to
# check for updates.
enable-update-checker: true
# Set whether buys and sells should be logged in the database.
enable-economy-log: false
# Set the maximum age for economy log entries in days.
# All log entries older than this will be deleted on server start.
# Set this to 0 to disable this feature.
cleanup-economy-log-days: 30
# Set whether a debug log file should be created.
# The file may get large! Please enable this setting when reporting issues.
enable-debug-log: false
# Set whether various protection plugins should be hooked into (if installed)
# in order to allow or deny shop creation in certain locations.
enable-worldguard-integration: true
enable-towny-integration: true
enable-authme-integration: true
enable-plotsquared-integration: true
enable-uskyblock-integration: true
enable-askyblock-integration: true
enable-bentobox-integration: true
enable-islandworld-integration: true
enable-griefprevention-integration: true
enable-areashop-integration: true
# Set whether the vendor of a shop should get messages when players buy
# or sell something from/to his shop or when his shop is out of stock.
enable-vendor-messages: true
# Set whether the vendor of a shop should get messages on all servers when players
# buy or sell something from/to his shop or when his shop is out of stock.
enable-vendor-bungee-messages: false
# Set whether only the shop a player is pointing at should be shown.
# If set to false, every shop near the player (with the specified
# distance) will be shown to him.
only-show-shops-in-sight: true
# Set whether the hologram's location should be fixed at the bottom,
# so when it gets more lines, it won't interfere with the item or chest,
# but goes higher.
hologram-fixed-bottom: true
# Set the amount (may be negative) a hologram should be lifted in the y-axis.
# A value of '1' equals to one block, and a value of '0.25' is equal to the
# height of one line.
hologram-lift: 0
# Set whether players should be allowed to buy or sell less items
# than the vendor has specified, in case the player does not have enough
# money or items, or if the chest does not have enough items or space,
# or if the vendor does not have enough money.
# The price will be calculated correspondingly.
auto-calculate-item-amount: false
# Set whether prices may contain decimals (prices of existing shops will stay).
allow-decimals-in-price: true
# Set whether players should be allowed to sell/buy broken items.
allow-broken-items: false
# Set whether the level of a potion or tipped arrow (if upgraded) should be
# appended to the item name. If set to true, the level ("II") will be
# displayed after the item name, but only if the item does not have a
# custom name.
append-potion-level-to-item-name: false
# Set whether shops should automatically be removed from the database if
# an error occurred while loading.
# (e.g. no chest, no space above chest, or unknown world)
remove-shop-on-error: false
# Set whether the mouse buttons should be inverted.
# Default:
# Right-Click -> Buy
# Left-Click -> Sell
invert-mouse-buttons: false
# Set the maximal distance (in blocks) to the shop where the
# player can see the hologram.
maximal-distance: 2
# Set the maximal distance (in blocks) to the shop where the
# player can see the floating shop item.
maximal-item-distance: 40
# Set whether the buy price must be greater than or equal to the sell price.
buy-greater-or-equal-sell: true
# Set the minimum and maximum prices for each individual item.
minimum-prices:
# "DIAMOND": 0.5
maximum-prices:
# "STONE": 2
# Set the items of which a player can't create a shop.
blacklist:
# - "DIORITE"
# Set the price a player has to pay in order to create...
# You can set this to 0 to disable costs.
shop-creation-price:
# ...a normal shop
normal: 5
# ...an admin shop
admin: 0
# Shop limits are handled with permissions.
# A player with permission "shopchest.limit.X" has a limit of X shops,
# a player with permission "shopchest.limit.*" does not have a shop limit.
# Admin shops are excluded from the shop limit.
shop-limits:
# Set the amount of shops that anyone who doesn't have a
# specific permission may have.
# If you don't want the players to have a limit by default
# set the value to -1.
default: 5
# Set the events of AreaShop when shops on that region should be removed.
# Valid values are: DELETE, UNRENT, RESELL, SELL
areashop-remove-shops:
- "DELETE"
- "UNRENT"
- "RESELL"
- "SELL"
# Set whether the custom WorldGuard flags should be allowed by default.
worldguard-default-flag-values:
create-shop: false
use-shop: false
use-admin-shop: false
# Set the types of Towny plots where shop creation should be allowed.
# Valid values are:
# RESIDENTIAL, COMMERCIAL, ARENA, EMBASSY, WILDS, SPLEEF, INN, JAIL, FARM
towny-shop-plots:
residents:
- "COMMERCIAL"
mayor:
- "COMMERCIAL"
king:
- "COMMERCIAL"
# Configuration of the database, where everything is stored.
# Shops are found in the table 'shopchest_shops', and logged economy
# transactions are found in the table 'shopchest_economy_logs'
database:
# Select the type of database which should be used
# Either use 'SQLite' or 'MySQL'. Otherwise you will break the plugin!
type: "SQLite"
# Set the prefix of all table names related to this plugin.
table-prefix: "shopchest_"
# If the specified type is 'MySQL', here you configure the...
mysql:
# ...interval in seconds, when the database should be pinged,
# to keep the connection alive
# You can set this to '0' to disable the ping interval
ping-interval: 3600
# ...hostname where the database is accessible
hostname: ""
# ...port where the database is accessible (default: 3306)
port: 3306
# ...database you want to use
database: ""
# ...username you are going to login with
username: ""
# ...password you are going to login with
password: ""

View File

@ -1,78 +0,0 @@
# ===============================================
# === ShopChest's hologram configuration file ===
# ===============================================
#
# Valid requirements are:
# VENDOR, AMOUNT, ITEM_TYPE, ITEM_NAME, HAS_ENCHANTMENT, BUY_PRICE,
# SELL_PRICE, HAS_POTION_EFFECT, IS_MUSIC_DISC, IS_POTION_EXTENDED,
# IS_WRITTEN_BOOK, IS_BANNER_PATTERN, ADMIN_SHOP, NORMAL_SHOP,
# IN_STOCK, MAX_STACK, CHEST_SPACE, DURABILITY
#
# You can also use the requirements for conditions.
# ITEM_TYPE will return the type of the item (-> item_names.txt),
# ITEM_NAME can be compared against a custom named item's name (may be null).
#
# Examples:
# - IN_STOCK > 0
# - VENDOR == "EpicEric"
# - BUY_PRICE <= SELL_PRICE
# - ITEM_TYPE == "STONE:2"
# - ITEM_TYPE != "IRON_INGOT"
# - ITEM_NAME == "The Mighty Sword"
# - (AMOUNT > 10) && (AMOUNT <= 20)
# - (IN_STOCK > 0) || ADMIN_SHOP
#
# Valid placeholders are:
# %VENDOR%, %AMOUNT%, %ITEMNAME%, %ENCHANTMENT%, %BUY-PRICE%,
# %SELL-PRICE%, %POTION-EFFECT%, %MUSIC-TITLE%, %BANNER-PATTERN-NAME%,
# %GENERATION%, %STOCK%, %MAX-STACK%, %CHEST-SPACE%, %DURABILITY%
#
# In the format, placeholders can also be used for scripts.
# Examples:
# - "In Stock: {%STOCK% / 64} Stk."
# - "In Stock: {(%STOCK% - (%STOCK% % 64)) / 64} Stk. {%STOCK% % 64}"
#
# Other information:
# - Options can be called however you want.
# - Color codes can be used in the format.
# - Options are checked from top to bottom; the first to
# fulfill the requirements will be taken.
# - All scripts have to be in JavaScript syntax.
# - Lines start with 0.
lines:
0:
options:
normal-shop:
format: "%VENDOR%"
requirements:
- NORMAL_SHOP
admin-shop:
format: "&cAdmin Shop"
requirements:
- ADMIN_SHOP
1:
options:
default:
format: "%AMOUNT% x %ITEMNAME%"
requirements:
2:
options:
buy-and-sell:
format: "Buy %BUY-PRICE% | %SELL-PRICE% Sell"
requirements:
- BUY_PRICE > 0
- SELL_PRICE > 0
only-buy:
format: "Buy %BUY-PRICE%"
requirements:
- BUY_PRICE > 0
only-sell:
format: "Sell %SELL-PRICE%"
requirements:
- SELL_PRICE > 0

File diff suppressed because it is too large Load Diff

View File

@ -1,919 +0,0 @@
message.shop-created=&6Dir wurden &c%CREATION-PRICE% &6abgenommen, um diesen Shop zu erstellen.
message.admin-shop-created=&6Dir wurden &c%CREATION-PRICE% &6abgenommen, um diesen Admin Shop zu erstellen.
message.chest-already-shop=&cTruhe ist bereits ein Shop.
message.chest-blocked=&cÜber der Truhe ist kein Platz.
message.double-chest-blocked=&cÜber der Truhe ist kein Platz.
message.shop-removed=&6Shop entfernt.
message.shop-removed-refund=&6Shop entfernt. Dir wurden &c%CREATION-PRICE%&6 erstattet.
message.all-shops-removed=&6Alle (&c%AMOUNT%&6) Shops von &c%VENDOR%&6 wurden entfernt.
message.chest-no-shop=&cTruhe ist kein Shop.
message.shop-create-not-enough-money=&cNicht genug Geld. Du brauchst &6%CREATION-PRICE% &cum einen Shop zu erstellen.
message.shopInfo.vendor=&6Verkäufer: &e%VENDOR%
message.shopInfo.product=&6Produkt: &e%AMOUNT% x %ITEMNAME%
message.shopInfo.stock=&6Auf Lager: &e%STOCK%
message.shopInfo.chest-space=&6Platz in Truhe: &e%CHEST-SPACE%
message.shopInfo.price=&6Preis: Kauf: &e%BUY-PRICE%&6 Verkauf: &e%SELL-PRICE%
message.shopInfo.disabled=&7Deaktiviert
message.shopInfo.is-normal=&6Typ: &eNormal
message.shopInfo.is-admin=&6Typ: &eAdmin
message.buy-and-sell-disabled=&cDu kannst keinen Shop ohne Kauf- und Verkaufspreis erstellen.
message.buy-success=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE%&a von &6%VENDOR% &agekauft.
message.buy-success-admin=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE% &agekauft.
message.sell-success=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%SELL-PRICE%&a an &6%VENDOR% &averkauft.
message.sell-success-admin=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%SELL-PRICE% &averkauft.
message.someone-bought=&6%PLAYER% &ahat &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE%&a von deinem Shop gekauft.
message.someone-sold=&6%PLAYER% &ahat &6%AMOUNT% x %ITEMNAME%&a für &6%SELL-PRICE%&a an deinen Shop verkauft.
message.revenue-while-offline=&6Während du offline warst, haben deine Shops einen Umsatz von &c%REVENUE%&6 gemacht.
message.not-enough-inventory-space=&cNicht genug Platz im Inventar.
message.chest-not-enough-inventory-space=&cShop ist voll.
message.not-enough-money=&cNicht genug Geld.
message.not-enough-items=&cNicht genug Items.
message.vendor-not-enough-money=&cVerkäufer hat nicht genug Geld.
message.out-of-stock=&cShop ausverkauft.
message.vendor-out-of-stock=&cDein Shop, der &6%AMOUNT% x %ITEMNAME% &cverkauft, ist ausverkauft.
message.error-occurred=&cEin Fehler ist aufgetreten: %ERROR%
message.amount-and-price-not-number=&cAnzahl und Preise müssen Zahlen sein.
message.amount-is-zero=&cAnzahl muss größer als 0 sein.
message.prices-contain-decimals=&cPreise dürfen keine Dezimalen enthalten.
message.no-item-in-hand=&cKein Item in der Hand.
message.click-chest-to-create-shop=&aKlicke innerhalb von 15 Sekunden auf eine Truhe, um einen Shop zu erstellen.
message.click-chest-to-remove-shop=&aKlicke innerhalb von 15 Sekunden auf einen Shop, um ihn zu entfernen.
message.click-chest-for-info=&aKlicke innerhalb von 15 Sekunden auf einen Shop, um Informationen über ihn zu bekommen.
message.click-chest-to-open-shop=&Klicke innerhalb von 15 Sekunden auf einen Shop, um ihn zu öffnen.
message.click-to-confirm=&aKlicke noch einmal zum Bestätigen.
message.opened-shop=&aDu hast &6%VENDOR%&as Shop geöffnet.
message.cannot-break-shop=&cDu kannst einen Shop nicht zerstören.
message.cannot-sell-broken-item=&cDu kannst kein kaputtes Artikel verkaufen.
message.buy-price-too-low=&cDer Kaufpreis muss höher sein als %MIN-PRICE%.
message.sell-price-too-low=&cDer Verkaufspreis muss höher sein als %MIN-PRICE%.
message.buy-price-too-high=&cDer Kaufpreis muss geringer sein als %MAX-PRICE%.
message.sell-price-too-high=&cDer Verkaufspreis muss geringer sein als %MAX-PRICE%.
message.buying-disabled=&cKaufen ist an diesem Shop nicht möglich.
message.selling-disabled=&cVerkaufen ist an diesem Shop nicht möglich.
message.reloaded-shops=&a%AMOUNT% Shop/s wurden erfolgreich neu geladen.
message.shop-limit-reached=&cDu hast dein Limit von &6%LIMIT% &cShop/s erreicht.
message.occupied-shop-slots=&6Du hast &c%AMOUNT%/%LIMIT% &6Shop Slot/s benutzt.
message.cannot-sell-item=&cDu kannst für diesen Artikel keinen Shop erstellen.
message.use-in-creative=&cDu kannst im Kreativ-Modus keine Shops benutzen.
message.select-item=&aÖffne dein Inventar und lass ein Item fallen, um es auszuwählen.
message.item-selected=&aItem wurde ausgewählt: &6%ITEMNAME%
message.creation-cancelled=&cShoperstellung wurde abgebrochen.
message.update.update-available=&6&lVersion &c&l%VERSION% &6&lvon &c&lShopChest &6&list verfügbar.
message.update.click-to-download=Klicke hier zum Herunterladen
message.update.no-update=&6&lKeine neue Aktualisierung verfügbar.
message.update.checking=&6&lSuche nach Aktualisierungen...
message.update.error=&c&lFehler beim Suchen nach Aktualisierungen.
message.noPermission.create=&cDu hast keine Berechtigung einen Shop zu erstellen.
message.noPermission.create-admin=&cDu hast keine Berechtigung einen Admin-Shop zu erstellen.
message.noPermission.create-protected=&cDu hast keine Berechtigung hier einen Shop zu erstellen.
message.noPermission.open-others=&cDu hast keine Berechtigung diesen Shop zu öffnen.
message.noPermission.buy=&cDu hast keine Berechtigung etwas zu kaufen.
message.noPermission.sell=&cDu hast keine Berechtigung etwas zu verkaufen.
message.noPermission.buy-here=&cDu hast keine Berechtigung hier etwas zu kaufen.
message.noPermission.sell-here=&cDu hast keine Berechtigung hier etwas zu verkaufen.
message.noPermission.remove-others=&cDu hast keine Berechtigung diesen Shop zu entfernen.
message.noPermission.remove-admin=&cDu hast keine Berechtigung einen Admin Shop zu entfernen.
message.noPermission.reload=&cDu hast keine Berechtigung die Shops neu zu laden.
message.noPermission.update=&cDu hast keine Berechtigung nach Aktualisierungen zu suchen.
message.noPermission.config=&cDu hast keine Berechtigung Konfigurationswerte zu verändern.
message.noPermission.extend-others=&cDu hast keine Berechtigung diesen Shop zu erweitern.
message.noPermission.extend-protected=&cDu hast keine Berechtigung diesen Shop nach hier zu erweitern.
message.commandDescription.header=&6==== &c/%COMMAND% &6Hilfe
message.commandDescription.footer=&6==== Ende
message.commandDescription.create=&a/%COMMAND% create <amount> <buy-price> <sell-price> - Erstelle einen Shop.
message.commandDescription.create-admin=&a/%COMMAND% create <amount> <buy-price> <sell-price> [admin] - Erstelle einen Shop.
message.commandDescription.remove=&a/%COMMAND% remove - Entferne einen Shop.
message.commandDescription.info=&a/%COMMAND% info - Rufe Informationen über den Shop ab.
message.commandDescription.removeall=&a/%COMMAND% removeall - Entferne alle Shops eines Spielers.
message.commandDescription.reload=&a/%COMMAND% reload - Lade die Shops neu.
message.commandDescription.update=&a/%COMMAND% update - Suche nach Aktualisierungen.
message.commandDescription.limits=&a/%COMMAND% limits - Betrachte die Shop Limits.
message.commandDescription.open=&a/%COMMAND% open - Öffne einen Shop.
message.commandDescription.config=&a/%COMMAND% config <set|add|remove> <property> <value> - Verändere Konfigurationswerte.
message.config.set=&6Eigenschaft &a%PROPERTY% &6wurde auf &a%VALUE% &6gesetzt.
message.config.added=&6Wert &a%VALUE% &6wurde zu &a%PROPERTY% &6hinzugefügt.
message.config.removed=&6Wert &a%VALUE% &6wurde aus &a%PROPERTY% &6entfernt.
book.generation.0=Original
book.generation.1=Kopie des Originals
book.generation.2=Kopie einer Kopie
book.generation.3=Zerrissen
effect.damageBoost=Stärke
effect.fireResistance=Feuerschutz
effect.harm=Direktschaden
effect.heal=Direktheilung
effect.invisibility=Unsichtbarkeit
effect.jump=Sprungkraft
effect.luck=Glück
effect.moveSlowdown=Langsamkeit
effect.moveSpeed=Schnelligkeit
effect.nightVision=Nachtsicht
effect.poison=Vergiftung
effect.regeneration=Regeneration
effect.waterBreathing=Unterwasseratem
effect.weakness=Schwäche
enchantment.arrowDamage=Stärke
enchantment.arrowFire=Flamme
enchantment.arrowInfinite=Unendlich
enchantment.arrowKnockback=Schlag
enchantment.binding_curse=Fluch der Bindung
enchantment.damage.all=Schärfe
enchantment.damage.arthropods=Nemesis der Gliederfüßer
enchantment.damage.undead=Bann
enchantment.digging=Effizienz
enchantment.durability=Haltbarkeit
enchantment.fire=Verbrennung
enchantment.fishingSpeed=Köder
enchantment.frostWalker=Eisläufer
enchantment.knockback=Rückstoß
enchantment.level.1=I
enchantment.level.10=X
enchantment.level.2=II
enchantment.level.3=III
enchantment.level.4=IV
enchantment.level.5=V
enchantment.level.6=VI
enchantment.level.7=VII
enchantment.level.8=VIII
enchantment.level.9=IX
enchantment.lootBonus=Plünderung
enchantment.lootBonusDigger=Glück
enchantment.lootBonusFishing=Glück des Meeres
enchantment.mending=Reparatur
enchantment.oxygen=Atmung
enchantment.protect.all=Schutz
enchantment.protect.explosion=Explosionsschutz
enchantment.protect.fall=Federfall
enchantment.protect.fire=Feuerschutz
enchantment.protect.projectile=Schusssicher
enchantment.sweeping=Schwungkraft
enchantment.thorns=Dornen
enchantment.untouching=Behutsamkeit
enchantment.vanishing_curse=Fluch des Verschwindens
enchantment.waterWalker=Wasserläufer
enchantment.waterWorker=Wasseraffinität
entity.Bat.name=Fledermaus
entity.Blaze.name=Lohe
entity.CaveSpider.name=Höhlenspinne
entity.Chicken.name=Huhn
entity.Cow.name=Kuh
entity.Creeper.name=Creeper
entity.Donkey.name=Esel
entity.ElderGuardian.name=Großer Wächter
entity.Enderman.name=Enderman
entity.Endermite.name=Endermite
entity.EntityHorse.name=Pferd
entity.EvocationIllager.name=Magier
entity.Ghast.name=Ghast
entity.Guardian.name=Wächter
entity.Horse.name=Pferd
entity.Husk.name=Wüstenzombie
entity.IllusionIllager.name=Illusionist
entity.LavaSlime.name=Magmawürfel
entity.Llama.name=Lama
entity.Mule.name=Maultier
entity.MushroomCow.name=Mooshroom
entity.Ozelot.name=Ozelot
entity.Parrot.name=Papagei
entity.Pig.name=Schwein
entity.PigZombie.name=Schweinezombie
entity.PolarBear.name=Eisbär
entity.Rabbit.name=Kaninchen
entity.Sheep.name=Schaf
entity.Shulker.name=Shulker
entity.Silverfish.name=Silberfischchen
entity.Skeleton.name=Skelett
entity.SkeletonHorse.name=Skelettpferd
entity.Slime.name=Schleim
entity.Spider.name=Spinne
entity.Squid.name=Tintenfisch
entity.Stray.name=Eiswanderer
entity.Vex.name=Plagegeist
entity.Villager.name=Dorfbewohner
entity.VindicationIllager.name=Diener
entity.Witch.name=Hexe
entity.WitherSkeleton.name=Witherskelett
entity.Wolf.name=Wolf
entity.Zombie.name=Zombie
entity.ZombieHorse.name=Zombiepferd
entity.ZombieVillager.name=Dorfbewohnerzombie
item.apple.name=Apfel
item.appleGold.name=Goldener Apfel
item.armorStand.name=Rüstungsständer
item.arrow.name=Pfeil
item.banner.black.name=Schwarzes Banner
item.banner.blue.name=Blaues Banner
item.banner.brown.name=Braunes Banner
item.banner.cyan.name=Türkises Banner
item.banner.gray.name=Graues Banner
item.banner.green.name=Grünes Banner
item.banner.lightBlue.name=Hellblaues Banner
item.banner.lime.name=Hellgrünes Banner
item.banner.magenta.name=Magenta Banner
item.banner.orange.name=Oranges Banner
item.banner.pink.name=Rosa Banner
item.banner.purple.name=Violettes Banner
item.banner.red.name=Rotes Banner
item.banner.silver.name=Hellgraues Banner
item.banner.white.name=Weißes Banner
item.banner.yellow.name=Gelbes Banner
item.bed.black.name=Schwarzes Bett
item.bed.blue.name=Blaues Bett
item.bed.brown.name=Braunes Bett
item.bed.cyan.name=Türkises Bett
item.bed.gray.name=Graues Bett
item.bed.green.name=Grünes Bett
item.bed.lightBlue.name=Hellblaues Bett
item.bed.lime.name=Hellgrünes Bett
item.bed.magenta.name=Magenta Bett
item.bed.name=Bett
item.bed.orange.name=Oranges Bett
item.bed.pink.name=Rosa Bett
item.bed.purple.name=Violettes Bett
item.bed.red.name=Rotes Bett
item.bed.silver.name=Hellgraues Bett
item.bed.white.name=Weißes Bett
item.bed.yellow.name=Gelbes Bett
item.beefCooked.name=Steak
item.beefRaw.name=Rohes Rindfleisch
item.beetroot.name=Rote Bete
item.beetroot_seeds.name=Rote-Bete-Samen
item.beetroot_soup.name=Borschtsch
item.blazePowder.name=Lohenstaub
item.blazeRod.name=Lohenrute
item.boat.acacia.name=Akazienholzboot
item.boat.birch.name=Birkenholzboot
item.boat.dark_oak.name=Schwarzeichenholzboot
item.boat.jungle.name=Tropenholzboot
item.boat.oak.name=Eichenholzboot
item.boat.spruce.name=Fichtenholzboot
item.bone.name=Knochen
item.book.name=Buch
item.bootsChain.name=Kettenstiefel
item.bootsCloth.name=Lederstiefel
item.bootsDiamond.name=Diamantstiefel
item.bootsGold.name=Goldstiefel
item.bootsIron.name=Eisenstiefel
item.bow.name=Bogen
item.bowl.name=Schüssel
item.bread.name=Brot
item.brewingStand.name=Braustand
item.brick.name=Ziegel
item.bucket.name=Eimer
item.bucketLava.name=Lavaeimer
item.bucketWater.name=Wassereimer
item.cake.name=Kuchen
item.carrotGolden.name=Goldene Karotte
item.carrotOnAStick.name=Karottenrute
item.carrots.name=Karotte
item.cauldron.name=Kessel
item.charcoal.name=Holzkohle
item.chestplateChain.name=Kettenhemd
item.chestplateCloth.name=Lederjacke
item.chestplateDiamond.name=Diamantharnisch
item.chestplateGold.name=Goldharnisch
item.chestplateIron.name=Eisenharnisch
item.chickenCooked.name=Gebratenes Hühnchen
item.chickenRaw.name=Rohes Hühnchen
item.chorusFruit.name=Chorusfrucht
item.chorusFruitPopped.name=Geplatzte Chorusfrucht
item.clay.name=Tonklumpen
item.clock.name=Uhr
item.coal.name=Kohle
item.comparator.name=Redstone-Komparator
item.compass.name=Kompass
item.cookie.name=Keks
item.diamond.name=Diamant
item.diode.name=Redstone-Verstärker
item.doorAcacia.name=Akazienholztür
item.doorBirch.name=Birkenholztür
item.doorDarkOak.name=Schwarzeichenholztür
item.doorIron.name=Eisentür
item.doorJungle.name=Tropenholztür
item.doorOak.name=Eichenholztür
item.doorSpruce.name=Fichtenholztür
item.dragon_breath.name=Drachenatem
item.dyePowder.black.name=Tintenbeutel
item.dyePowder.blue.name=Lapislazuli
item.dyePowder.brown.name=Kakaobohnen
item.dyePowder.cyan.name=Türkiser Farbstoff
item.dyePowder.gray.name=Grauer Farbstoff
item.dyePowder.green.name=Kaktusgrün
item.dyePowder.lightBlue.name=Hellblauer Farbstoff
item.dyePowder.lime.name=Hellgrüner Farbstoff
item.dyePowder.magenta.name=Magenta Farbstoff
item.dyePowder.orange.name=Oranger Farbstoff
item.dyePowder.pink.name=Rosa Farbstoff
item.dyePowder.purple.name=Violetter Farbstoff
item.dyePowder.red.name=Roter Farbstoff
item.dyePowder.silver.name=Hellgrauer Farbstoff
item.dyePowder.white.name=Knochenmehl
item.dyePowder.yellow.name=Gelber Farbstoff
item.egg.name=Ei
item.elytra.name=Elytren
item.emerald.name=Smaragd
item.emptyMap.name=Leere Karte
item.enchantedBook.name=Verzaubertes Buch
item.end_crystal.name=Enderkristall
item.enderPearl.name=Enderperle
item.expBottle.name=Erfahrungsfläschchen
item.eyeOfEnder.name=Enderauge
item.feather.name=Feder
item.fermentedSpiderEye.name=Fermentiertes Spinnenauge
item.fireball.name=Feuerkugel
item.fireworks.name=Feuerwerksrakete
item.fireworksCharge.name=Feuerwerksstern
item.fish.clownfish.raw.name=Clownfisch
item.fish.cod.cooked.name=Gebratener Kabeljau
item.fish.cod.raw.name=Roher Kabeljau
item.fish.pufferfish.raw.name=Kugelfisch
item.fish.salmon.cooked.name=Gebratener Lachs
item.fish.salmon.raw.name=Roher Lachs
item.fishingRod.name=Angel
item.flint.name=Feuerstein
item.flintAndSteel.name=Feuerzeug
item.flowerPot.name=Blumentopf
item.frame.name=Rahmen
item.ghastTear.name=Ghastträne
item.glassBottle.name=Glasflasche
item.goldNugget.name=Goldklumpen
item.hatchetDiamond.name=Diamantaxt
item.hatchetGold.name=Goldaxt
item.hatchetIron.name=Eisenaxt
item.hatchetStone.name=Steinaxt
item.hatchetWood.name=Holzaxt
item.helmetChain.name=Kettenhaube
item.helmetCloth.name=Lederkappe
item.helmetDiamond.name=Diamanthelm
item.helmetGold.name=Goldhelm
item.helmetIron.name=Eisenhelm
item.hoeDiamond.name=Diamanthacke
item.hoeGold.name=Goldhacke
item.hoeIron.name=Eisenhacke
item.hoeStone.name=Steinhacke
item.hoeWood.name=Holzhacke
item.horsearmordiamond.name=Diamantene Pferderüstung
item.horsearmorgold.name=Goldene Pferderüstung
item.horsearmormetal.name=Eiserne Pferderüstung
item.ingotGold.name=Goldbarren
item.ingotIron.name=Eisenbarren
item.ironNugget.name=Eisenklumpen
item.knowledgeBook.name=Buch des Wissens
item.leash.name=Leine
item.leather.name=Leder
item.leggingsChain.name=Kettenhose
item.leggingsCloth.name=Lederhose
item.leggingsDiamond.name=Diamantbeinschutz
item.leggingsGold.name=Goldbeinschutz
item.leggingsIron.name=Eisenbeinschutz
item.magmaCream.name=Magmacreme
item.map.name=Karte
item.melon.name=Melonenscheibe
item.milk.name=Milch
item.minecart.name=Lore
item.minecartChest.name=Güterlore
item.minecartCommandBlock.name=Befehlsblocklore
item.minecartFurnace.name=Antriebslore
item.minecartHopper.name=Trichterlore
item.minecartTnt.name=TNT-Lore
item.monsterPlacer.name=Erschaffe
item.mushroomStew.name=Pilzsuppe
item.muttonCooked.name=Gebratenes Hammelfleisch
item.muttonRaw.name=Rohes Hammelfleisch
item.nameTag.name=Namensschild
item.netherStalkSeeds.name=Netherwarze
item.netherStar.name=Netherstern
item.netherbrick.name=Netherziegel
item.netherquartz.name=Netherquarz
item.painting.name=Gemälde
item.paper.name=Papier
item.pickaxeDiamond.name=Diamantspitzhacke
item.pickaxeGold.name=Goldspitzhacke
item.pickaxeIron.name=Eisenspitzhacke
item.pickaxeStone.name=Steinspitzhacke
item.pickaxeWood.name=Holzspitzhacke
item.porkchopCooked.name=Gebratenes Schweinefleisch
item.porkchopRaw.name=Rohes Schweinefleisch
item.potato.name=Kartoffel
item.potatoBaked.name=Ofenkartoffel
item.potatoPoisonous.name=Giftige Kartoffel
item.potion.name=Trank
item.prismarineCrystals.name=Prismarinkristalle
item.prismarineShard.name=Prismarinscherbe
item.pumpkinPie.name=Kürbiskuchen
item.rabbitCooked.name=Gebratenes Kaninchen
item.rabbitFoot.name=Hasenpfote
item.rabbitHide.name=Kaninchenfell
item.rabbitRaw.name=Rohes Kaninchen
item.rabbitStew.name=Kaninchenragout
item.record.11.desc=C418 - 11
item.record.13.desc=C418 - 13
item.record.blocks.desc=C418 - Blocks
item.record.cat.desc=C418 - Cat
item.record.chirp.desc=C418 - Chirp
item.record.far.desc=C418 - Far
item.record.mall.desc=C418 - Mall
item.record.mellohi.desc=C418 - Mellohi
item.record.name=Schallplatte
item.record.stal.desc=C418 - Stal
item.record.strad.desc=C418 - Strad
item.record.wait.desc=C418 - Wait
item.record.ward.desc=C418 - Ward
item.redstone.name=Redstone
item.reeds.name=Zuckerrohr
item.rottenFlesh.name=Verrottetes Fleisch
item.saddle.name=Sattel
item.seeds.name=Weizenkörner
item.seeds_melon.name=Melonenkerne
item.seeds_pumpkin.name=Kürbiskerne
item.shears.name=Schere
item.shield.name=Schild
item.shovelDiamond.name=Diamantschaufel
item.shovelGold.name=Goldschaufel
item.shovelIron.name=Eisenschaufel
item.shovelStone.name=Steinschaufel
item.shovelWood.name=Holzschaufel
item.shulkerShell.name=Shulkerschale
item.sign.name=Schild
item.skull.char.name=Kopf
item.skull.creeper.name=Creeperkopf
item.skull.dragon.name=Drachenkopf
item.skull.skeleton.name=Skelettschädel
item.skull.wither.name=Witherskelettschädel
item.skull.zombie.name=Zombiekopf
item.slimeball.name=Schleimball
item.snowball.name=Schneeball
item.speckledMelon.name=Glitzernde Melonenscheibe
item.spectral_arrow.name=Spektralpfeil
item.spiderEye.name=Spinnenauge
item.stick.name=Stock
item.string.name=Faden
item.sugar.name=Zucker
item.sulphur.name=Schwarzpulver
item.swordDiamond.name=Diamantschwert
item.swordGold.name=Goldschwert
item.swordIron.name=Eisenschwert
item.swordStone.name=Steinschwert
item.swordWood.name=Holzschwert
item.tipped_arrow.name=Getränkter Pfeil
item.totem.name=Totem der Unsterblichkeit
item.wheat.name=Weizen
item.writingBook.name=Buch und Feder
item.writtenBook.name=Beschriebenes Buch
item.yellowDust.name=Glowstonestaub
lingering_potion.effect.awkward=Seltsamer Verweiltrank
lingering_potion.effect.empty=Nicht braubarer Verweiltrank
lingering_potion.effect.fire_resistance=Verweiltrank der Feuerresistenz
lingering_potion.effect.harming=Verweiltrank des Schadens
lingering_potion.effect.healing=Verweiltrank der Heilung
lingering_potion.effect.invisibility=Verweiltrank der Unsichtbarkeit
lingering_potion.effect.leaping=Verweiltrank der Sprungkraft
lingering_potion.effect.luck=Verweiltrank des Glücks
lingering_potion.effect.mundane=Gewöhnlicher Verweiltrank
lingering_potion.effect.night_vision=Verweiltrank der Nachtsicht
lingering_potion.effect.poison=Verweiltrank der Vergiftung
lingering_potion.effect.regeneration=Verweiltrank der Regeneration
lingering_potion.effect.slowness=Verweiltrank der Langsamkeit
lingering_potion.effect.strength=Verweiltrank der Stärke
lingering_potion.effect.swiftness=Verweiltrank der Schnelligkeit
lingering_potion.effect.thick=Dickflüssiger Verweiltrank
lingering_potion.effect.water=Verweilende Wasserflasche
lingering_potion.effect.water_breathing=Verweiltrank der Unterwasseratmung
lingering_potion.effect.weakness=Verweiltrank der Schwäche
potion.effect.awkward=Seltsamer Trank
potion.effect.empty=Nicht braubarer Trank
potion.effect.fire_resistance=Trank der Feuerresistenz
potion.effect.harming=Trank des Schadens
potion.effect.healing=Trank der Heilung
potion.effect.invisibility=Trank der Unsichtbarkeit
potion.effect.leaping=Trank der Sprungkraft
potion.effect.luck=Trank des Glücks
potion.effect.mundane=Gewöhnlicher Trank
potion.effect.night_vision=Trank der Nachtsicht
potion.effect.poison=Trank der Vergiftung
potion.effect.regeneration=Trank der Regeneration
potion.effect.slowness=Trank der Langsamkeit
potion.effect.strength=Trank der Stärke
potion.effect.swiftness=Trank der Schnelligkeit
potion.effect.thick=Dickflüssiger Trank
potion.effect.water=Wasserflasche
potion.effect.water_breathing=Trank der Unterwasseratmung
potion.effect.weakness=Trank der Schwäche
splash_potion.effect.awkward=Seltsamer Wurftrank
splash_potion.effect.empty=Nicht braubarer Wurftrank
splash_potion.effect.fire_resistance=Wurftrank der Feuerresistenz
splash_potion.effect.harming=Wurftrank des Schadens
splash_potion.effect.healing=Wurftrank der Heilung
splash_potion.effect.invisibility=Wurftrank der Unsichtbarkeit
splash_potion.effect.leaping=Wurftrank der Sprungkraft
splash_potion.effect.luck=Wurftrank des Glücks
splash_potion.effect.mundane=Gewöhnlicher Wurftrank
splash_potion.effect.night_vision=Wurftrank der Nachtsicht
splash_potion.effect.poison=Wurftrank der Vergiftung
splash_potion.effect.regeneration=Wurftrank der Regeneration
splash_potion.effect.slowness=Wurftrank der Langsamkeit
splash_potion.effect.strength=Wurftrank der Stärke
splash_potion.effect.swiftness=Wurftrank der Schnelligkeit
splash_potion.effect.thick=Dickflüssiger Wurftrank
splash_potion.effect.water=Werfbare Wasserflasche
splash_potion.effect.water_breathing=Wurftrank der Unterwasseratmung
splash_potion.effect.weakness=Wurftrank der Schwäche
tile.acaciaFence.name=Akazienholzzaun
tile.acaciaFenceGate.name=Akazienholzzauntor
tile.activatorRail.name=Aktivierungsschiene
tile.anvil.intact.name=Amboss
tile.anvil.slightlyDamaged.name=Leicht beschädigter Amboss
tile.anvil.veryDamaged.name=Stark beschädigter Amboss
tile.barrier.name=Barriere
tile.beacon.name=Leuchtfeuer
tile.bedrock.name=Grundgestein
tile.birchFence.name=Birkenholzzaun
tile.birchFenceGate.name=Birkenholzzauntor
tile.blockCoal.name=Kohleblock
tile.blockDiamond.name=Diamantblock
tile.blockEmerald.name=Smaragdblock
tile.blockGold.name=Goldblock
tile.blockIron.name=Eisenblock
tile.blockLapis.name=Lapislazuliblock
tile.blockRedstone.name=Redstone-Block
tile.boneBlock.name=Knochenblock
tile.bookshelf.name=Bücherregal
tile.brick.name=Ziegelsteine
tile.button.name=Knopf
tile.cactus.name=Kaktus
tile.chainCommandBlock.name=Ketten-Befehlsblock
tile.chest.name=Truhe
tile.chestTrap.name=Redstone-Truhe
tile.chorusFlower.name=Chorusblüte
tile.chorusPlant.name=Choruspflanze
tile.clay.name=Ton
tile.clayHardened.name=Keramik
tile.clayHardenedStained.black.name=Schwarze Keramik
tile.clayHardenedStained.blue.name=Blaue Keramik
tile.clayHardenedStained.brown.name=Braune Keramik
tile.clayHardenedStained.cyan.name=Türkise Keramik
tile.clayHardenedStained.gray.name=Graue Keramik
tile.clayHardenedStained.green.name=Grüne Keramik
tile.clayHardenedStained.lightBlue.name=Hellblaue Keramik
tile.clayHardenedStained.lime.name=Hellgrüne Keramik
tile.clayHardenedStained.magenta.name=Magenta Keramik
tile.clayHardenedStained.orange.name=Orange Keramik
tile.clayHardenedStained.pink.name=Rosa Keramik
tile.clayHardenedStained.purple.name=Violette Keramik
tile.clayHardenedStained.red.name=Rote Keramik
tile.clayHardenedStained.silver.name=Hellgraue Keramik
tile.clayHardenedStained.white.name=Weiße Keramik
tile.clayHardenedStained.yellow.name=Gelbe Keramik
tile.cloth.black.name=Schwarze Wolle
tile.cloth.blue.name=Blaue Wolle
tile.cloth.brown.name=Braune Wolle
tile.cloth.cyan.name=Türkise Wolle
tile.cloth.gray.name=Graue Wolle
tile.cloth.green.name=Grüne Wolle
tile.cloth.lightBlue.name=Hellblaue Wolle
tile.cloth.lime.name=Hellgrüne Wolle
tile.cloth.magenta.name=Magenta Wolle
tile.cloth.orange.name=Orange Wolle
tile.cloth.pink.name=Rosa Wolle
tile.cloth.purple.name=Violette Wolle
tile.cloth.red.name=Rote Wolle
tile.cloth.silver.name=Hellgraue Wolle
tile.cloth.white.name=Weiße Wolle
tile.cloth.yellow.name=Gelbe Wolle
tile.cobbleWall.mossy.name=Bemooste Bruchsteinmauer
tile.cobbleWall.normal.name=Bruchsteinmauer
tile.commandBlock.name=Befehlsblock
tile.concrete.black.name=Schwarzer Beton
tile.concrete.blue.name=Blauer Beton
tile.concrete.brown.name=Brauner Beton
tile.concrete.cyan.name=Türkiser Beton
tile.concrete.gray.name=Grauer Beton
tile.concrete.green.name=Grüner Beton
tile.concrete.lightBlue.name=Hellblauer Beton
tile.concrete.lime.name=Hellgrüner Beton
tile.concrete.magenta.name=Magenta Beton
tile.concrete.orange.name=Oranger Beton
tile.concrete.pink.name=Rosa Beton
tile.concrete.purple.name=Violetter Beton
tile.concrete.red.name=Roter Beton
tile.concrete.silver.name=Hellgrauer Beton
tile.concrete.white.name=Weißer Beton
tile.concrete.yellow.name=Gelber Beton
tile.concretePowder.black.name=Schwarzer Trockenbeton
tile.concretePowder.blue.name=Blauer Trockenbeton
tile.concretePowder.brown.name=Brauner Trockenbeton
tile.concretePowder.cyan.name=Türkiser Trockenbeton
tile.concretePowder.gray.name=Grauer Trockenbeton
tile.concretePowder.green.name=Grüner Trockenbeton
tile.concretePowder.lightBlue.name=Hellblauer Trockenbeton
tile.concretePowder.lime.name=Hellgrüner Trockenbeton
tile.concretePowder.magenta.name=Magenta Trockenbeton
tile.concretePowder.orange.name=Oranger Trockenbeton
tile.concretePowder.pink.name=Rosa Trockenbeton
tile.concretePowder.purple.name=Violetter Trockenbeton
tile.concretePowder.red.name=Roter Trockenbeton
tile.concretePowder.silver.name=Hellgrauer Trockenbeton
tile.concretePowder.white.name=Weißer Trockenbeton
tile.concretePowder.yellow.name=Gelber Trockenbeton
tile.darkOakFence.name=Schwarzeichenholzzaun
tile.darkOakFenceGate.name=Schwarzeichenholzzauntor
tile.daylightDetector.name=Tageslichtsensor
tile.deadbush.name=Toter Busch
tile.detectorRail.name=Sensorschiene
tile.dirt.coarse.name=Grobe Erde
tile.dirt.default.name=Erde
tile.dirt.podzol.name=Podsol
tile.dispenser.name=Werfer
tile.doublePlant.fern.name=Großer Farn
tile.doublePlant.grass.name=Hohes Gras
tile.doublePlant.paeonia.name=Pfingstrose
tile.doublePlant.rose.name=Rosenstrauch
tile.doublePlant.sunflower.name=Sonnenblume
tile.doublePlant.syringa.name=Flieder
tile.dragonEgg.name=Drachenei
tile.dropper.name=Spender
tile.enchantmentTable.name=Zaubertisch
tile.endBricks.name=Endsteinziegel
tile.endPortalFrame.name=Endportalrahmen
tile.endRod.name=Endstab
tile.enderChest.name=Endertruhe
tile.farmland.name=Ackerboden
tile.fence.name=Eichenholzzaun
tile.fenceGate.name=Eichenholzzauntor
tile.fenceIron.name=Eisengitter
tile.fire.name=Feuer
tile.flower1.dandelion.name=Löwenzahn
tile.flower2.allium.name=Sternlauch
tile.flower2.blueOrchid.name=Blaue Orchidee
tile.flower2.houstonia.name=Porzellansternchen
tile.flower2.oxeyeDaisy.name=Margerite
tile.flower2.poppy.name=Mohn
tile.flower2.tulipOrange.name=Orange Tulpe
tile.flower2.tulipPink.name=Rosa Tulpe
tile.flower2.tulipRed.name=Rote Tulpe
tile.flower2.tulipWhite.name=Weiße Tulpe
tile.furnace.name=Ofen
tile.glass.name=Glas
tile.glazedTerracottaBlack.name=Schwarze glasierte Keramik
tile.glazedTerracottaBlue.name=Blaue glasierte Keramik
tile.glazedTerracottaBrown.name=Braune glasierte Keramik
tile.glazedTerracottaCyan.name=Türkise glasierte Keramik
tile.glazedTerracottaGray.name=Graue glasierte Keramik
tile.glazedTerracottaGreen.name=Grüne glasierte Keramik
tile.glazedTerracottaLightBlue.name=Hellblaue glasierte Keramik
tile.glazedTerracottaLime.name=Hellgrüne glasierte Keramik
tile.glazedTerracottaMagenta.name=Magenta glasierte Keramik
tile.glazedTerracottaOrange.name=Orange glasierte Keramik
tile.glazedTerracottaPink.name=Rosa glasierte Keramik
tile.glazedTerracottaPurple.name=Violette glasierte Keramik
tile.glazedTerracottaRed.name=Rote glasierte Keramik
tile.glazedTerracottaSilver.name=Hellgraue glasierte Keramik
tile.glazedTerracottaWhite.name=Weiße glasierte Keramik
tile.glazedTerracottaYellow.name=Gelbe glasierte Keramik
tile.goldenRail.name=Antriebsschiene
tile.grass.name=Grasblock
tile.grassPath.name=Trampelpfad
tile.gravel.name=Kies
tile.hayBlock.name=Strohballen
tile.hellrock.name=Netherrack
tile.hellsand.name=Seelensand
tile.hopper.name=Trichter
tile.ice.name=Eis
tile.icePacked.name=Packeis
tile.ironTrapdoor.name=Eisenfalltür
tile.jukebox.name=Plattenspieler
tile.jungleFence.name=Tropenholzzaun
tile.jungleFenceGate.name=Tropenholzzauntor
tile.ladder.name=Leiter
tile.lava.name=Lava
tile.leaves.acacia.name=Akazienlaub
tile.leaves.big_oak.name=Schwarzeichenlaub
tile.leaves.birch.name=Birkenlaub
tile.leaves.jungle.name=Tropenbaumlaub
tile.leaves.oak.name=Eichenlaub
tile.leaves.spruce.name=Fichtennadeln
tile.lever.name=Hebel
tile.lightgem.name=Glowstone
tile.litpumpkin.name=Kürbislaterne
tile.log.acacia.name=Akazienholz
tile.log.big_oak.name=Schwarzeichenholz
tile.log.birch.name=Birkenholz
tile.log.jungle.name=Tropenholz
tile.log.oak.name=Eichenholz
tile.log.spruce.name=Fichtenholz
tile.magma.name=Magmablock
tile.melon.name=Melone
tile.mobSpawner.name=Monsterspawner
tile.monsterStoneEgg.brick.name=Steinziegel (Silberfischchen)
tile.monsterStoneEgg.chiseledbrick.name=Gemeißelte Steinziegel (Silberfischchen)
tile.monsterStoneEgg.cobble.name=Bruchstein (Silberfischchen)
tile.monsterStoneEgg.crackedbrick.name=Rissige Steinziegel (Silberfischchen)
tile.monsterStoneEgg.mossybrick.name=Bemooste Steinziegel (Silberfischchen)
tile.monsterStoneEgg.stone.name=Stein (Silberfischchen)
tile.mushroom.name=Pilz
tile.musicBlock.name=Notenblock
tile.mycel.name=Myzel
tile.netherBrick.name=Netherziegel
tile.netherFence.name=Netherziegelzaun
tile.netherStalk.name=Netherwarze
tile.netherWartBlock.name=Netherwarzenblock
tile.netherquartz.name=Netherquarzerz
tile.notGate.name=Redstone-Fackel
tile.observer.name=Beobachter
tile.obsidian.name=Obsidian
tile.oreCoal.name=Steinkohle
tile.oreDiamond.name=Diamanterz
tile.oreEmerald.name=Smaragderz
tile.oreGold.name=Golderz
tile.oreIron.name=Eisenerz
tile.oreLapis.name=Lapislazulierz
tile.oreRedstone.name=Redstone-Erz
tile.pistonBase.name=Kolben
tile.pistonStickyBase.name=Klebriger Kolben
tile.portal.name=Portal
tile.pressurePlateStone.name=Steindruckplatte
tile.pressurePlateWood.name=Holzdruckplatte
tile.prismarine.bricks.name=Prismarinziegel
tile.prismarine.dark.name=Dunkler Prismarin
tile.prismarine.rough.name=Prismarin
tile.pumpkin.name=Kürbis
tile.purpurBlock.name=Purpurblock
tile.purpurPillar.name=Purpursäule
tile.purpurSlab.name=Purpurstufe
tile.quartzBlock.chiseled.name=Gemeißelter Quarzblock
tile.quartzBlock.default.name=Quarzblock
tile.quartzBlock.lines.name=Quarzsäule
tile.rail.name=Schiene
tile.redNetherBrick.name=Rote Netherziegel
tile.redSandStone.chiseled.name=Gemeißelter roter Sandstein
tile.redSandStone.default.name=Roter Sandstein
tile.redSandStone.smooth.name=Glatter roter Sandstein
tile.redstoneLight.name=Redstone-Lampe
tile.repeatingCommandBlock.name=Wiederhol-Befehlsblock
tile.sand.default.name=Sand
tile.sand.red.name=Roter Sand
tile.sandStone.chiseled.name=Gemeißelter Sandstein
tile.sandStone.default.name=Sandstein
tile.sandStone.smooth.name=Glatter Sandstein
tile.sapling.acacia.name=Akaziensetzling
tile.sapling.big_oak.name=Schwarzeichensetzling
tile.sapling.birch.name=Birkensetzling
tile.sapling.jungle.name=Tropenbaumsetzling
tile.sapling.oak.name=Eichensetzling
tile.sapling.spruce.name=Fichtensetzling
tile.seaLantern.name=Seelaterne
tile.shulkerBoxBlack.name=Schwarze Shulkerkiste
tile.shulkerBoxBlue.name=Blaue Shulkerkiste
tile.shulkerBoxBrown.name=Braune Shulkerkiste
tile.shulkerBoxCyan.name=Türkise Shulkerkiste
tile.shulkerBoxGray.name=Graue Shulkerkiste
tile.shulkerBoxGreen.name=Grüne Shulkerkiste
tile.shulkerBoxLightBlue.name=Hellblaue Shulkerkiste
tile.shulkerBoxLime.name=Hellgrüne Shulkerkiste
tile.shulkerBoxMagenta.name=Magenta Shulkerkiste
tile.shulkerBoxOrange.name=Orange Shulkerkiste
tile.shulkerBoxPink.name=Rosa Shulkerkiste
tile.shulkerBoxPurple.name=Violette Shulkerkiste
tile.shulkerBoxRed.name=Rote Shulkerkiste
tile.shulkerBoxSilver.name=Hellgraue Shulkerkiste
tile.shulkerBoxWhite.name=Weiße Shulkerkiste
tile.shulkerBoxYellow.name=Gelbe Shulkerkiste
tile.slime.name=Schleimblock
tile.snow.name=Schnee
tile.sponge.dry.name=Schwamm
tile.sponge.wet.name=Nasser Schwamm
tile.spruceFence.name=Fichtenholzzaun
tile.spruceFenceGate.name=Fichtenholzzauntor
tile.stainedGlass.black.name=Schwarzes Glas
tile.stainedGlass.blue.name=Blaues Glas
tile.stainedGlass.brown.name=Braunes Glas
tile.stainedGlass.cyan.name=Türkises Glas
tile.stainedGlass.gray.name=Graues Glas
tile.stainedGlass.green.name=Grünes Glas
tile.stainedGlass.lightBlue.name=Hellblaues Glas
tile.stainedGlass.lime.name=Hellgrünes Glas
tile.stainedGlass.magenta.name=Magenta Glas
tile.stainedGlass.orange.name=Oranges Glas
tile.stainedGlass.pink.name=Rosa Glas
tile.stainedGlass.purple.name=Violettes Glas
tile.stainedGlass.red.name=Rotes Glas
tile.stainedGlass.silver.name=Hellgraues Glas
tile.stainedGlass.white.name=Weißes Glas
tile.stainedGlass.yellow.name=Gelbes Glas
tile.stairsBrick.name=Ziegeltreppe
tile.stairsNetherBrick.name=Netherziegeltreppe
tile.stairsPurpur.name=Purpurtreppe
tile.stairsQuartz.name=Quarztreppe
tile.stairsRedSandStone.name=Rote Sandsteintreppe
tile.stairsSandStone.name=Sandsteintreppe
tile.stairsStone.name=Bruchsteintreppe
tile.stairsStoneBrickSmooth.name=Steinziegeltreppe
tile.stairsWood.name=Eichenholztreppe
tile.stairsWoodAcacia.name=Akazienholztreppe
tile.stairsWoodBirch.name=Birkenholztreppe
tile.stairsWoodDarkOak.name=Schwarzeichenholztreppe
tile.stairsWoodJungle.name=Tropenholztreppe
tile.stairsWoodSpruce.name=Fichtenholztreppe
tile.stone.andesite.name=Andesit
tile.stone.andesiteSmooth.name=Polierter Andesit
tile.stone.diorite.name=Diorit
tile.stone.dioriteSmooth.name=Polierter Diorit
tile.stone.granite.name=Granit
tile.stone.graniteSmooth.name=Polierter Granit
tile.stone.stone.name=Stein
tile.stoneMoss.name=Bemooster Bruchstein
tile.stoneSlab.brick.name=Ziegelstufe
tile.stoneSlab.cobble.name=Bruchsteinstufe
tile.stoneSlab.netherBrick.name=Netherziegelstufe
tile.stoneSlab.quartz.name=Quarzstufe
tile.stoneSlab.sand.name=Sandsteinstufe
tile.stoneSlab.smoothStoneBrick.name=Steinziegelstufe
tile.stoneSlab.stone.name=Steinstufe
tile.stoneSlab.wood.name=Holzstufe
tile.stoneSlab2.red_sandstone.name=Rote Sandsteinstufe
tile.stonebrick.name=Bruchstein
tile.stonebricksmooth.chiseled.name=Gemeißelte Steinziegel
tile.stonebricksmooth.cracked.name=Rissige Steinziegel
tile.stonebricksmooth.default.name=Steinziegel
tile.stonebricksmooth.mossy.name=Bemooste Steinziegel
tile.structureBlock.name=Konstruktionsblock
tile.structureVoid.name=Konstruktionsleere
tile.tallgrass.fern.name=Farn
tile.tallgrass.grass.name=Gras
tile.tallgrass.shrub.name=Busch
tile.thinGlass.name=Glasscheibe
tile.thinStainedGlass.black.name=Schwarze Glasscheibe
tile.thinStainedGlass.blue.name=Blaue Glasscheibe
tile.thinStainedGlass.brown.name=Braune Glasscheibe
tile.thinStainedGlass.cyan.name=Türkise Glasscheibe
tile.thinStainedGlass.gray.name=Graue Glasscheibe
tile.thinStainedGlass.green.name=Grüne Glasscheibe
tile.thinStainedGlass.lightBlue.name=Hellblaue Glasscheibe
tile.thinStainedGlass.lime.name=Hellgrüne Glasscheibe
tile.thinStainedGlass.magenta.name=Magenta Glasscheibe
tile.thinStainedGlass.orange.name=Orange Glasscheibe
tile.thinStainedGlass.pink.name=Rosa Glasscheibe
tile.thinStainedGlass.purple.name=Violette Glasscheibe
tile.thinStainedGlass.red.name=Rote Glasscheibe
tile.thinStainedGlass.silver.name=Hellgraue Glasscheibe
tile.thinStainedGlass.white.name=Weiße Glasscheibe
tile.thinStainedGlass.yellow.name=Gelbe Glasscheibe
tile.tnt.name=TNT
tile.torch.name=Fackel
tile.trapdoor.name=Holzfalltür
tile.tripWireSource.name=Haken
tile.vine.name=Ranken
tile.water.name=Wasser
tile.waterlily.name=Seerosenblatt
tile.web.name=Spinnennetz
tile.weightedPlate_heavy.name=Wägeplatte (hohe Gewichte)
tile.weightedPlate_light.name=Wägeplatte (niedrige Gewichte)
tile.whiteStone.name=Endstein
tile.wood.acacia.name=Akazienholzbretter
tile.wood.big_oak.name=Schwarzeichenholzbretter
tile.wood.birch.name=Birkenholzbretter
tile.wood.jungle.name=Tropenholzbretter
tile.wood.oak.name=Eichenholzbretter
tile.wood.spruce.name=Fichtenholzbretter
tile.woodSlab.acacia.name=Akazienholzstufe
tile.woodSlab.big_oak.name=Schwarzeichenholzstufe
tile.woodSlab.birch.name=Birkenholzstufe
tile.woodSlab.jungle.name=Tropenholzstufe
tile.woodSlab.oak.name=Eichenholzstufe
tile.woodSlab.spruce.name=Fichtenholzstufe
tile.woolCarpet.black.name=Schwarzer Teppich
tile.woolCarpet.blue.name=Blauer Teppich
tile.woolCarpet.brown.name=Brauner Teppich
tile.woolCarpet.cyan.name=Türkiser Teppich
tile.woolCarpet.gray.name=Grauer Teppich
tile.woolCarpet.green.name=Grüner Teppich
tile.woolCarpet.lightBlue.name=Hellblauer Teppich
tile.woolCarpet.lime.name=Hellgrüner Teppich
tile.woolCarpet.magenta.name=Magenta Teppich
tile.woolCarpet.orange.name=Oranger Teppich
tile.woolCarpet.pink.name=Rosa Teppich
tile.woolCarpet.purple.name=Violetter Teppich
tile.woolCarpet.red.name=Roter Teppich
tile.woolCarpet.silver.name=Hellgrauer Teppich
tile.woolCarpet.white.name=Weißer Teppich
tile.woolCarpet.yellow.name=Gelber Teppich
tile.workbench.name=Werkbank
tipped_arrow.effect.awkward=Getränkter Pfeil
tipped_arrow.effect.empty=Nicht herstellbarer getränkter Pfeil
tipped_arrow.effect.fire_resistance=Pfeil der Feuerresistenz
tipped_arrow.effect.harming=Pfeil des Schadens
tipped_arrow.effect.healing=Pfeil der Heilung
tipped_arrow.effect.invisibility=Pfeil der Unsichtbarkeit
tipped_arrow.effect.leaping=Pfeil der Sprungkraft
tipped_arrow.effect.luck=Pfeil des Glücks
tipped_arrow.effect.mundane=Getränkter Pfeil
tipped_arrow.effect.night_vision=Pfeil der Nachtsicht
tipped_arrow.effect.poison=Pfeil der Vergiftung
tipped_arrow.effect.regeneration=Pfeil der Regeneration
tipped_arrow.effect.slowness=Pfeil der Langsamkeit
tipped_arrow.effect.strength=Pfeil der Stärke
tipped_arrow.effect.swiftness=Pfeil der Schnelligkeit
tipped_arrow.effect.thick=Getränkter Pfeil
tipped_arrow.effect.water=Nasser Pfeil
tipped_arrow.effect.water_breathing=Pfeil der Unterwasseratmung
tipped_arrow.effect.weakness=Pfeil der Schwäche

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,93 +0,0 @@
# Do not change anything in here unless you know what you're doing!
name: ${project.name}
main: de.epiceric.shopchest.ShopChest
version: ${project.version}
author: EpicEric
website: ${project.url}
description: Create your own nice-looking chest shops and sell your stuff to other players!
softdepend: [WorldGuard, Towny, AuthMe, PlotSquared, uSkyBlock, ASkyBlock, IslandWorld, GriefPrevention, AreaShop, Multiverse-Core, MultiWorld, BentoBox]
depend: [Vault]
api-version: 1.13
permissions:
shopchest.*:
description: Gives access to all ShopChest permissions.
children:
shopchest.create: true
shopchest.create.buy: true
shopchest.create.sell: true
shopchest.create.admin: true
shopchest.create.protected: true
shopchest.remove.other: true
shopchest.remove.admin: true
shopchest.buy: true
shopchest.openOther: true
shopchest.notification.update: true
shopchest.reload: true
shopchest.update: true
shopchest.limit.*: true
shopchest.config: true
shopchest.extend.other: true
shopchest.extend.protected: true
shopchest.external.bypass: true
shopchest.create:
description: Allows you to create a shop.
children:
shopchest.create.buy: true
shopchest.create.sell: true
default: true
shopchest.create.buy:
description: Allows you to create a buy-shop.
default: true
shopchest.create.sell:
description: Allows you to create a sell-shop.
default: true
shopchest.create.admin:
description: Allows you to create an admin shop.
children:
shopchest.create: true
default: op
shopchest.create.protected:
description: Allows you to create a shop on a protected chest or in a protected region.
children:
shopchest.create: true
default: op
shopchest.remove.other:
description: Allows you to remove other players' shops.
default: op
shopchest.remove.admin:
description: Allows you to remove admin shops.
default: op
shopchest.buy:
description: Allows you to buy something.
default: true
shopchest.sell:
description: Allows you to sell something.
default: true
shopchest.openOther:
description: Allows you to open other players' shops.
default: op
shopchest.notification.update:
description: Allows you to get update notification on join.
default: op
shopchest.reload:
description: Allows you to reload the shops.
default: op
shopchest.update:
description: Allows you to check for updates.
default: op
shopchest.limit.*:
default: op
shopchest.config:
description: Allows you to change configuration values per command.
default: op
shopchest.extend.other:
description: Allows you to extend other players' shops.
default: op
shopchest.extend.protected:
description: Allows you to extend shops into a protected region.
default: op
shopchest.external.bypass:
description: Allows you to to use shops regions/plots that deny shop use.
default: op