mirror of
https://github.com/amalthea-mc/ShopChest.git
synced 2024-12-23 01:21:47 +00:00
Move plugin src directory into a sub module
This commit is contained in:
parent
f2a96f178b
commit
a77926b3b1
19
plugin/pom.xml
Normal file
19
plugin/pom.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ShopChest-parent</artifactId>
|
||||
<groupId>de.epiceric</groupId>
|
||||
<version>1.14.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>plugin</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
740
plugin/src/main/java/de/epiceric/shopchest/ShopChest.java
Normal file
740
plugin/src/main/java/de/epiceric/shopchest/ShopChest.java
Normal file
@ -0,0 +1,740 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
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<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,576 @@
|
||||
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")));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
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);
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
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<>();
|
||||
}
|
||||
}
|
614
plugin/src/main/java/de/epiceric/shopchest/config/Config.java
Normal file
614
plugin/src/main/java/de/epiceric/shopchest/config/Config.java
Normal file
@ -0,0 +1,614 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package de.epiceric.shopchest.exceptions;
|
||||
|
||||
public class ChestNotFoundException extends Exception {
|
||||
private static final long serialVersionUID = -6446875473671870708L;
|
||||
|
||||
public ChestNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package de.epiceric.shopchest.exceptions;
|
||||
|
||||
public class NotEnoughSpaceException extends Exception {
|
||||
private static final long serialVersionUID = 3718475607700458355L;
|
||||
|
||||
public NotEnoughSpaceException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
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 + "\"");
|
||||
}
|
||||
}
|
25
plugin/src/main/java/de/epiceric/shopchest/external/BentoBoxShopFlag.java
vendored
Normal file
25
plugin/src/main/java/de/epiceric/shopchest/external/BentoBoxShopFlag.java
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
112
plugin/src/main/java/de/epiceric/shopchest/external/PlotSquaredOldShopFlag.java
vendored
Normal file
112
plugin/src/main/java/de/epiceric/shopchest/external/PlotSquaredOldShopFlag.java
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
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'";
|
||||
}
|
||||
}
|
||||
}
|
154
plugin/src/main/java/de/epiceric/shopchest/external/PlotSquaredShopFlag.java
vendored
Normal file
154
plugin/src/main/java/de/epiceric/shopchest/external/PlotSquaredShopFlag.java
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
31
plugin/src/main/java/de/epiceric/shopchest/external/WorldGuardShopFlag.java
vendored
Normal file
31
plugin/src/main/java/de/epiceric/shopchest/external/WorldGuardShopFlag.java
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
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()));
|
||||
}
|
||||
|
||||
}
|
62
plugin/src/main/java/de/epiceric/shopchest/external/listeners/ASkyBlockListener.java
vendored
Normal file
62
plugin/src/main/java/de/epiceric/shopchest/external/listeners/ASkyBlockListener.java
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
57
plugin/src/main/java/de/epiceric/shopchest/external/listeners/BentoBoxListener.java
vendored
Normal file
57
plugin/src/main/java/de/epiceric/shopchest/external/listeners/BentoBoxListener.java
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
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;
|
||||
}
|
||||
}
|
62
plugin/src/main/java/de/epiceric/shopchest/external/listeners/GriefPreventionListener.java
vendored
Normal file
62
plugin/src/main/java/de/epiceric/shopchest/external/listeners/GriefPreventionListener.java
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
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;
|
||||
}
|
||||
}
|
59
plugin/src/main/java/de/epiceric/shopchest/external/listeners/IslandWorldListener.java
vendored
Normal file
59
plugin/src/main/java/de/epiceric/shopchest/external/listeners/IslandWorldListener.java
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
90
plugin/src/main/java/de/epiceric/shopchest/external/listeners/PlotSquaredListener.java
vendored
Normal file
90
plugin/src/main/java/de/epiceric/shopchest/external/listeners/PlotSquaredListener.java
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
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;
|
||||
}
|
||||
}
|
85
plugin/src/main/java/de/epiceric/shopchest/external/listeners/TownyListener.java
vendored
Normal file
85
plugin/src/main/java/de/epiceric/shopchest/external/listeners/TownyListener.java
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
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;
|
||||
}
|
||||
}
|
61
plugin/src/main/java/de/epiceric/shopchest/external/listeners/USkyBlockListener.java
vendored
Normal file
61
plugin/src/main/java/de/epiceric/shopchest/external/listeners/USkyBlockListener.java
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
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;
|
||||
}
|
||||
}
|
97
plugin/src/main/java/de/epiceric/shopchest/external/listeners/WorldGuardListener.java
vendored
Normal file
97
plugin/src/main/java/de/epiceric/shopchest/external/listeners/WorldGuardListener.java
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
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
@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
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
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,157 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
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());
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
253
plugin/src/main/java/de/epiceric/shopchest/nms/Hologram.java
Normal file
253
plugin/src/main/java/de/epiceric/shopchest/nms/Hologram.java
Normal file
@ -0,0 +1,253 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
252
plugin/src/main/java/de/epiceric/shopchest/nms/JsonBuilder.java
Normal file
252
plugin/src/main/java/de/epiceric/shopchest/nms/JsonBuilder.java
Normal file
@ -0,0 +1,252 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
480
plugin/src/main/java/de/epiceric/shopchest/shop/Shop.java
Normal file
480
plugin/src/main/java/de/epiceric/shopchest/shop/Shop.java
Normal file
@ -0,0 +1,480 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
164
plugin/src/main/java/de/epiceric/shopchest/shop/ShopItem.java
Normal file
164
plugin/src/main/java/de/epiceric/shopchest/shop/ShopItem.java
Normal file
@ -0,0 +1,164 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
877
plugin/src/main/java/de/epiceric/shopchest/sql/Database.java
Normal file
877
plugin/src/main/java/de/epiceric/shopchest/sql/Database.java
Normal file
@ -0,0 +1,877 @@
|
||||
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
|
||||
}
|
||||
}
|
110
plugin/src/main/java/de/epiceric/shopchest/sql/MySQL.java
Normal file
110
plugin/src/main/java/de/epiceric/shopchest/sql/MySQL.java
Normal file
@ -0,0 +1,110 @@
|
||||
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=?";
|
||||
}
|
||||
}
|
126
plugin/src/main/java/de/epiceric/shopchest/sql/SQLite.java
Normal file
126
plugin/src/main/java/de/epiceric/shopchest/sql/SQLite.java
Normal file
@ -0,0 +1,126 @@
|
||||
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=?";
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
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);
|
||||
}
|
||||
}
|
222
plugin/src/main/java/de/epiceric/shopchest/utils/ClickType.java
Normal file
222
plugin/src/main/java/de/epiceric/shopchest/utils/ClickType.java
Normal file
@ -0,0 +1,222 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
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();
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
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";
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
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);
|
||||
}
|
||||
}
|
524
plugin/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java
Normal file
524
plugin/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java
Normal file
@ -0,0 +1,524 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
649
plugin/src/main/java/de/epiceric/shopchest/utils/Utils.java
Normal file
649
plugin/src/main/java/de/epiceric/shopchest/utils/Utils.java
Normal file
@ -0,0 +1,649 @@
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
224
plugin/src/main/resources/config.yml
Normal file
224
plugin/src/main/resources/config.yml
Normal file
@ -0,0 +1,224 @@
|
||||
# ===============================================
|
||||
# ====== 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: ""
|
78
plugin/src/main/resources/hologram-format.yml
Normal file
78
plugin/src/main/resources/hologram-format.yml
Normal file
@ -0,0 +1,78 @@
|
||||
# ===============================================
|
||||
# === 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
|
1230
plugin/src/main/resources/item_names.txt
Normal file
1230
plugin/src/main/resources/item_names.txt
Normal file
File diff suppressed because it is too large
Load Diff
919
plugin/src/main/resources/lang/de_DE-legacy.lang
Normal file
919
plugin/src/main/resources/lang/de_DE-legacy.lang
Normal file
@ -0,0 +1,919 @@
|
||||
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
|
1518
plugin/src/main/resources/lang/de_DE.lang
Normal file
1518
plugin/src/main/resources/lang/de_DE.lang
Normal file
File diff suppressed because it is too large
Load Diff
1163
plugin/src/main/resources/lang/en_US-legacy.lang
Normal file
1163
plugin/src/main/resources/lang/en_US-legacy.lang
Normal file
File diff suppressed because it is too large
Load Diff
1754
plugin/src/main/resources/lang/en_US.lang
Normal file
1754
plugin/src/main/resources/lang/en_US.lang
Normal file
File diff suppressed because it is too large
Load Diff
93
plugin/src/main/resources/plugin.yml
Normal file
93
plugin/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,93 @@
|
||||
# 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
|
Loading…
Reference in New Issue
Block a user