mirror of
https://github.com/amalthea-mc/ShopChest.git
synced 2024-11-25 20:02:23 +00:00
Few improvements (#135)
* Fix: Few improvements * Few improvements * Hologram/item can wait after shop creation * Compare worlds using their name * Fix holograms display * Changed version to 1.12.4 * Display shop after creation * Fix requested changed * Improve performance for simple hologram conditions
This commit is contained in:
parent
efced89eb1
commit
3b9d26c079
2
pom.xml
2
pom.xml
@ -25,7 +25,7 @@
|
|||||||
<maven.compiler.target>${jdkVersion}</maven.compiler.target>
|
<maven.compiler.target>${jdkVersion}</maven.compiler.target>
|
||||||
|
|
||||||
<!-- Versioning -->
|
<!-- Versioning -->
|
||||||
<version.number>1.12.3</version.number>
|
<version.number>1.12.4</version.number>
|
||||||
<version.final>${version.number}-${version.git}</version.final>
|
<version.final>${version.number}-${version.git}</version.final>
|
||||||
|
|
||||||
<!-- Others -->
|
<!-- Others -->
|
||||||
|
@ -204,7 +204,7 @@ public class ShopChest extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (database != null) {
|
if (database != null) {
|
||||||
for (Shop shop : shopUtils.getShops()) {
|
for (Shop shop : shopUtils.getShopsCopy()) {
|
||||||
shopUtils.removeShop(shop, false);
|
shopUtils.removeShop(shop, false);
|
||||||
debug("Removed shop (#" + shop.getID() + ")");
|
debug("Removed shop (#" + shop.getID() + ")");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package de.epiceric.shopchest.config;
|
package de.epiceric.shopchest.config;
|
||||||
|
|
||||||
import de.epiceric.shopchest.ShopChest;
|
import de.epiceric.shopchest.ShopChest;
|
||||||
|
import de.epiceric.shopchest.utils.Operator;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
@ -21,6 +22,13 @@ public class HologramFormat {
|
|||||||
NORMAL_SHOP, IN_STOCK, MAX_STACK, CHEST_SPACE, DURABILITY
|
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 ShopChest plugin;
|
private ShopChest plugin;
|
||||||
private File configFile;
|
private File configFile;
|
||||||
private YamlConfiguration config;
|
private YamlConfiguration config;
|
||||||
@ -110,32 +118,78 @@ public class HologramFormat {
|
|||||||
* @return Result of the condition
|
* @return Result of the condition
|
||||||
*/
|
*/
|
||||||
public boolean evalRequirement(String condition, Map<Requirement, Object> values) {
|
public boolean evalRequirement(String condition, Map<Requirement, Object> values) {
|
||||||
try {
|
String cond = condition;
|
||||||
ScriptEngineManager manager = new ScriptEngineManager();
|
|
||||||
ScriptEngine engine = manager.getEngineByName("JavaScript");
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
for (HologramFormat.Requirement req : HologramFormat.Requirement.values()) {
|
if (val instanceof String && !(sVal.startsWith("\"") && sVal.endsWith("\""))) {
|
||||||
if (cond.contains(req.toString()) && values.containsKey(req)) {
|
sVal = String.format("\"%s\"", sVal);
|
||||||
Object val = values.get(req);
|
}
|
||||||
String sVal = String.valueOf(val);
|
|
||||||
|
|
||||||
if (val instanceof String && !(sVal.startsWith("\"") && sVal.endsWith("\""))) {
|
cond = cond.replace(req.toString(), sVal);
|
||||||
sVal = String.format("\"%s\"", 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
|
||||||
}
|
}
|
||||||
|
|
||||||
cond = cond.replace(req.toString(), sVal);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (boolean) engine.eval(cond);
|
// string cond: first char must be a: "
|
||||||
} catch (ScriptException e) {
|
if (firstChar == '"') {
|
||||||
plugin.debug("Failed to eval condition: " + condition);
|
Matcher matcher = SIMPLE_STRING_CONDITION.matcher(cond);
|
||||||
plugin.debug(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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 {
|
||||||
|
ScriptEngineManager manager = new ScriptEngineManager();
|
||||||
|
ScriptEngine engine = manager.getEngineByName("JavaScript");
|
||||||
|
return (boolean) engine.eval(cond);
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
plugin.debug("Failed to eval condition: " + condition);
|
||||||
|
plugin.debug(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -691,17 +691,9 @@ public class ShopInteractListener implements Listener {
|
|||||||
executor.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.SHOP_CREATED, placeholder));
|
executor.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.SHOP_CREATED, placeholder));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Player p : location.getWorld().getPlayers()) {
|
// next update will display the new shop
|
||||||
if (p.getLocation().distanceSquared(location) <= Math.pow(config.maximal_distance, 2)) {
|
for (Player player : location.getWorld().getPlayers()) {
|
||||||
if (shop.getHologram() != null) {
|
plugin.getShopUtils().resetPlayerLocation(player);
|
||||||
shop.getHologram().showPlayer(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (p.getLocation().distanceSquared(location) <= Math.pow(config.maximal_item_distance, 2)) {
|
|
||||||
if (shop.getItem() != null) {
|
|
||||||
shop.getItem().setVisible(p, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,18 +27,6 @@ public class ShopItemListener implements Listener {
|
|||||||
this.shopUtils = plugin.getShopUtils();
|
this.shopUtils = plugin.getShopUtils();
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPlayerLeave(PlayerQuitEvent e) {
|
|
||||||
for (Shop shop : plugin.getShopUtils().getShops()) {
|
|
||||||
if (shop.getItem() != null) {
|
|
||||||
shop.getItem().setVisible(e.getPlayer(), false);
|
|
||||||
}
|
|
||||||
if (shop.getHologram() != null) {
|
|
||||||
shop.getHologram().hidePlayer(e.getPlayer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGH)
|
@EventHandler(priority = EventPriority.HIGH)
|
||||||
public void onBlockPlace(BlockPlaceEvent e) {
|
public void onBlockPlace(BlockPlaceEvent e) {
|
||||||
Block b = e.getBlockPlaced();
|
Block b = e.getBlockPlaced();
|
||||||
|
@ -8,6 +8,7 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.event.world.WorldLoadEvent;
|
import org.bukkit.event.world.WorldLoadEvent;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
@ -20,6 +21,20 @@ public class ShopUpdateListener implements Listener {
|
|||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerLeave(PlayerQuitEvent e) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public void onPlayerTeleport(PlayerTeleportEvent e) {
|
public void onPlayerTeleport(PlayerTeleportEvent e) {
|
||||||
Location from = e.getFrom();
|
Location from = e.getFrom();
|
||||||
@ -28,7 +43,7 @@ public class ShopUpdateListener implements Listener {
|
|||||||
|
|
||||||
// Wait till the chunk should have loaded on the client
|
// Wait till the chunk should have loaded on the client
|
||||||
// Update IF worlds are different OR chunks are different (as many teleports are in same chunk)
|
// Update IF worlds are different OR chunks are different (as many teleports are in same chunk)
|
||||||
if (!from.getWorld().equals(to.getWorld())
|
if (!from.getWorld().getName().equals(to.getWorld().getName())
|
||||||
|| from.getChunk().getX() != to.getChunk().getX()
|
|| from.getChunk().getX() != to.getChunk().getX()
|
||||||
|| from.getChunk().getZ() != to.getChunk().getZ()) {
|
|| from.getChunk().getZ() != to.getChunk().getZ()) {
|
||||||
// Wait for 15 ticks before we actually put it in the queue
|
// Wait for 15 ticks before we actually put it in the queue
|
||||||
@ -40,13 +55,15 @@ public class ShopUpdateListener implements Listener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
if (p.isOnline()) {
|
if (p.isOnline()) {
|
||||||
for (Shop shop : plugin.getShopUtils().getShops()) {
|
for (Shop shop : plugin.getShopUtils().getShops()) {
|
||||||
if (shop.getItem() != null) {
|
if (shop.hasItem()) {
|
||||||
shop.getItem().setVisible(p, false);
|
shop.getItem().hidePlayer(p);
|
||||||
}
|
}
|
||||||
if (shop.getHologram() != null) {
|
if (shop.hasHologram()) {
|
||||||
shop.getHologram().hidePlayer(p);
|
shop.getHologram().hidePlayer(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// so next update will update correctly
|
||||||
|
plugin.getShopUtils().resetPlayerLocation(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,7 @@ public class ArmorStandWrapper {
|
|||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private int entityId;
|
private int entityId;
|
||||||
|
|
||||||
public ArmorStandWrapper(ShopChest plugin, Location location, String customName) {
|
public ArmorStandWrapper(ShopChest plugin, Location location, String customName, boolean interactable) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.customName = customName;
|
this.customName = customName;
|
||||||
@ -58,9 +58,12 @@ public class ArmorStandWrapper {
|
|||||||
entityArmorStandClass.getMethod("setInvisible", boolean.class).invoke(entity, true);
|
entityArmorStandClass.getMethod("setInvisible", boolean.class).invoke(entity, true);
|
||||||
|
|
||||||
// Adds the entity to some lists so it can call interact events
|
// Adds the entity to some lists so it can call interact events
|
||||||
Method addEntityMethod = worldServerClass.getDeclaredMethod((Utils.getMajorVersion() == 8 ? "a" : "b"), entityClass);
|
// It will also automatically load/unload it when far away
|
||||||
addEntityMethod.setAccessible(true);
|
if (interactable) {
|
||||||
addEntityMethod.invoke(worldServerClass.cast(nmsWorld), entity);
|
Method addEntityMethod = worldServerClass.getDeclaredMethod((Utils.getMajorVersion() == 8 ? "a" : "b"), entityClass);
|
||||||
|
addEntityMethod.setAccessible(true);
|
||||||
|
addEntityMethod.invoke(worldServerClass.cast(nmsWorld), entity);
|
||||||
|
}
|
||||||
|
|
||||||
uuid = (UUID) entityClass.getMethod("getUniqueID").invoke(entity);
|
uuid = (UUID) entityClass.getMethod("getUniqueID").invoke(entity);
|
||||||
entityId = (int) entityArmorStandClass.getMethod("getId").invoke(entity);
|
entityId = (int) entityArmorStandClass.getMethod("getId").invoke(entity);
|
||||||
|
@ -2,27 +2,49 @@ package de.epiceric.shopchest.nms;
|
|||||||
|
|
||||||
import de.epiceric.shopchest.ShopChest;
|
import de.epiceric.shopchest.ShopChest;
|
||||||
import de.epiceric.shopchest.config.Config;
|
import de.epiceric.shopchest.config.Config;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.ArmorStand;
|
import org.bukkit.entity.ArmorStand;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class Hologram {
|
public class Hologram {
|
||||||
|
|
||||||
private static List<Hologram> holograms = new ArrayList<>();
|
private static final List<Hologram> HOLOGRAMS = new ArrayList<>();
|
||||||
|
|
||||||
private final Set<UUID> visibility = Collections.newSetFromMap(new ConcurrentHashMap<UUID, Boolean>());
|
/**
|
||||||
|
* @param armorStand Armor stand that's part of a hologram
|
||||||
|
* @return Hologram, the armor stand is part of
|
||||||
|
*/
|
||||||
|
public static Hologram getHologram(ArmorStand armorStand) {
|
||||||
|
for (Hologram hologram : HOLOGRAMS) {
|
||||||
|
if (hologram.contains(armorStand)) {
|
||||||
|
return hologram;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param armorStand Armor stand to check
|
||||||
|
* @return Whether the armor stand is part of a hologram
|
||||||
|
*/
|
||||||
|
public static boolean isPartOfHologram(ArmorStand armorStand) {
|
||||||
|
return getHologram(armorStand) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// concurrent since update task is in async thread
|
||||||
|
// since this is a fake entity, hologram is hidden per default
|
||||||
|
private final Set<UUID> viewers = Collections.newSetFromMap(new ConcurrentHashMap<UUID, Boolean>());
|
||||||
private final List<ArmorStandWrapper> wrappers = new ArrayList<>();
|
private final List<ArmorStandWrapper> wrappers = new ArrayList<>();
|
||||||
private final Location location;
|
private final Location location;
|
||||||
private final ShopChest plugin;
|
private final ShopChest plugin;
|
||||||
private final Config config;
|
private final Config config;
|
||||||
|
|
||||||
private boolean exists = false;
|
private boolean exists;
|
||||||
private ArmorStandWrapper interactArmorStandWrapper;
|
private ArmorStandWrapper interactArmorStandWrapper;
|
||||||
|
|
||||||
public Hologram(ShopChest plugin, String[] lines, Location location) {
|
public Hologram(ShopChest plugin, String[] lines, Location location) {
|
||||||
@ -30,98 +52,6 @@ public class Hologram {
|
|||||||
this.config = plugin.getShopChestConfig();
|
this.config = plugin.getShopChestConfig();
|
||||||
this.location = location;
|
this.location = location;
|
||||||
|
|
||||||
create(lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.hologram_fixed_bottom) {
|
|
||||||
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.hologram_fixed_bottom) {
|
|
||||||
loc.subtract(0, line * 0.25, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArmorStandWrapper wrapper = new ArmorStandWrapper(plugin, loc, text);
|
|
||||||
wrappers.add(line, wrapper);
|
|
||||||
|
|
||||||
if (forceUpdateLine) {
|
|
||||||
for (UUID uuid : visibility) {
|
|
||||||
Player player = Bukkit.getPlayer(uuid);
|
|
||||||
if (player != null) {
|
|
||||||
wrapper.setVisible(player, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeLine(int line) {
|
|
||||||
if (line < wrappers.size()) {
|
|
||||||
if (config.hologram_fixed_bottom) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getLines() {
|
|
||||||
List<String> lines = new ArrayList<>();
|
|
||||||
for (ArmorStandWrapper wrapper : wrappers) {
|
|
||||||
lines.add(wrapper.getCustomName());
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines.toArray(new String[lines.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void create(String[] lines) {
|
|
||||||
for (int i = 0; i < lines.length; i++) {
|
for (int i = 0; i < lines.length; i++) {
|
||||||
addLine(i, lines[i]);
|
addLine(i, lines[i]);
|
||||||
}
|
}
|
||||||
@ -131,15 +61,11 @@ public class Hologram {
|
|||||||
if (config.hologram_fixed_bottom) y = 0.85;
|
if (config.hologram_fixed_bottom) y = 0.85;
|
||||||
|
|
||||||
Location loc = getLocation().add(0, y, 0);
|
Location loc = getLocation().add(0, y, 0);
|
||||||
interactArmorStandWrapper = new ArmorStandWrapper(plugin, loc, null);
|
interactArmorStandWrapper = new ArmorStandWrapper(plugin, loc, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Player player : location.getWorld().getPlayers()) {
|
this.exists = true;
|
||||||
plugin.getShopUtils().updateShops(player, true);
|
HOLOGRAMS.add(this);
|
||||||
}
|
|
||||||
|
|
||||||
holograms.add(this);
|
|
||||||
exists = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,58 +75,6 @@ public class Hologram {
|
|||||||
return location.clone();
|
return location.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param p Player to which the hologram should be shown
|
|
||||||
*/
|
|
||||||
public void showPlayer(final Player p) {
|
|
||||||
if (!isVisible(p)) {
|
|
||||||
new BukkitRunnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (ArmorStandWrapper wrapper : wrappers) {
|
|
||||||
wrapper.setVisible(p, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interactArmorStandWrapper != null) {
|
|
||||||
interactArmorStandWrapper.setVisible(p, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.runTaskAsynchronously(plugin);
|
|
||||||
|
|
||||||
visibility.add(p.getUniqueId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param p Player from which the hologram should be hidden
|
|
||||||
*/
|
|
||||||
public void hidePlayer(final Player p) {
|
|
||||||
if (isVisible(p)) {
|
|
||||||
new BukkitRunnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (ArmorStandWrapper wrapper : wrappers) {
|
|
||||||
wrapper.setVisible(p, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interactArmorStandWrapper != null) {
|
|
||||||
interactArmorStandWrapper.setVisible(p, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.runTaskAsynchronously(plugin);
|
|
||||||
|
|
||||||
visibility.remove(p.getUniqueId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param p Player to check
|
|
||||||
* @return Whether the hologram is visible to the player
|
|
||||||
*/
|
|
||||||
public boolean isVisible(Player p) {
|
|
||||||
return visibility.contains(p.getUniqueId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Whether the hologram exists and is not dead
|
* @return Whether the hologram exists and is not dead
|
||||||
*/
|
*/
|
||||||
@ -231,45 +105,189 @@ public class Hologram {
|
|||||||
return interactArmorStandWrapper;
|
return interactArmorStandWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 or not
|
||||||
|
*/
|
||||||
|
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 or not
|
||||||
|
*/
|
||||||
|
public void hidePlayer(Player p, boolean force) {
|
||||||
|
if (viewers.remove(p.getUniqueId()) || force) {
|
||||||
|
togglePlayer(p, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the hologram. <br>
|
* Removes the hologram. <br>
|
||||||
* Hologram will be hidden from all players and will be killed
|
* Hologram will be hidden from all players and will be killed
|
||||||
*/
|
*/
|
||||||
public void remove() {
|
public void remove() {
|
||||||
|
viewers.clear();
|
||||||
|
|
||||||
for (ArmorStandWrapper wrapper : wrappers) {
|
for (ArmorStandWrapper wrapper : wrappers) {
|
||||||
wrapper.remove();
|
wrapper.remove();
|
||||||
}
|
}
|
||||||
|
wrappers.clear();
|
||||||
|
|
||||||
if (interactArmorStandWrapper != null) {
|
if (interactArmorStandWrapper != null) {
|
||||||
interactArmorStandWrapper.remove();
|
interactArmorStandWrapper.remove();
|
||||||
}
|
}
|
||||||
|
interactArmorStandWrapper = null;
|
||||||
wrappers.clear();
|
|
||||||
|
|
||||||
exists = false;
|
exists = false;
|
||||||
holograms.remove(this);
|
HOLOGRAMS.remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetVisible(Player p) {
|
||||||
|
viewers.remove(p.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void togglePlayer(Player p, boolean visible) {
|
||||||
|
for (ArmorStandWrapper wrapper : wrappers) {
|
||||||
|
wrapper.setVisible(p, visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interactArmorStandWrapper != null) {
|
||||||
|
interactArmorStandWrapper.setVisible(p, visible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param armorStand Armor stand that's part of a hologram
|
* Get all hologram lines
|
||||||
* @return Hologram, the armor stand is part of
|
*
|
||||||
|
* @return Hologram lines
|
||||||
*/
|
*/
|
||||||
public static Hologram getHologram(ArmorStand armorStand) {
|
public String[] getLines() {
|
||||||
for (Hologram hologram : holograms) {
|
List<String> lines = new ArrayList<>();
|
||||||
if (hologram.contains(armorStand)) {
|
for (ArmorStandWrapper wrapper : wrappers) {
|
||||||
return hologram;
|
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.hologram_fixed_bottom) {
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
Location loc = getLocation();
|
||||||
|
|
||||||
|
if (!config.hologram_fixed_bottom) {
|
||||||
|
loc.subtract(0, line * 0.25, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArmorStandWrapper wrapper = new ArmorStandWrapper(plugin, loc, text, false);
|
||||||
|
wrappers.add(line, wrapper);
|
||||||
|
|
||||||
|
if (forceUpdateLine) {
|
||||||
|
for (Player player : location.getWorld().getPlayers()) {
|
||||||
|
if (viewers.contains(player.getUniqueId())) {
|
||||||
|
wrapper.setVisible(player, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param armorStand Armor stand to check
|
* Set a line
|
||||||
* @return Whether the armor stand is part of a hologram
|
*
|
||||||
|
* @param line index to change
|
||||||
|
* @param text text to display
|
||||||
*/
|
*/
|
||||||
public static boolean isPartOfHologram(ArmorStand armorStand) {
|
public void setLine(int line, String text) {
|
||||||
return getHologram(armorStand) != null;
|
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.hologram_fixed_bottom) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import de.epiceric.shopchest.language.LanguageUtils;
|
|||||||
import de.epiceric.shopchest.nms.Hologram;
|
import de.epiceric.shopchest.nms.Hologram;
|
||||||
import de.epiceric.shopchest.utils.ItemUtils;
|
import de.epiceric.shopchest.utils.ItemUtils;
|
||||||
import de.epiceric.shopchest.utils.Utils;
|
import de.epiceric.shopchest.utils.Utils;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
@ -19,29 +18,31 @@ import org.bukkit.block.Block;
|
|||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.Chest;
|
import org.bukkit.block.Chest;
|
||||||
import org.bukkit.block.DoubleChest;
|
import org.bukkit.block.DoubleChest;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class Shop {
|
public class Shop {
|
||||||
|
|
||||||
|
public enum ShopType {
|
||||||
|
NORMAL,
|
||||||
|
ADMIN,
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ShopChest plugin;
|
||||||
|
private final OfflinePlayer vendor;
|
||||||
|
private final ItemStack product;
|
||||||
|
private final Location location;
|
||||||
|
private final double buyPrice;
|
||||||
|
private final double sellPrice;
|
||||||
|
private final ShopType shopType;
|
||||||
|
private final Config config;
|
||||||
|
|
||||||
private boolean created;
|
private boolean created;
|
||||||
private int id;
|
private int id;
|
||||||
private ShopChest plugin;
|
|
||||||
private OfflinePlayer vendor;
|
|
||||||
private ItemStack product;
|
|
||||||
private Location location;
|
|
||||||
private Hologram hologram;
|
private Hologram hologram;
|
||||||
private ShopItem item;
|
private ShopItem item;
|
||||||
private double buyPrice;
|
|
||||||
private double sellPrice;
|
|
||||||
private ShopType shopType;
|
|
||||||
private Config config;
|
|
||||||
|
|
||||||
public Shop(int id, ShopChest plugin, OfflinePlayer vendor, ItemStack product, Location location, double buyPrice, double sellPrice, ShopType shopType) {
|
public Shop(int id, ShopChest plugin, OfflinePlayer vendor, ItemStack product, Location location, double buyPrice, double sellPrice, ShopType shopType) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -59,6 +60,34 @@ public class Shop {
|
|||||||
this(-1, plugin, vendor, product, location, buyPrice, sellPrice, 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) {
|
public boolean create(boolean showConsoleMessages) {
|
||||||
if (created) return false;
|
if (created) return false;
|
||||||
|
|
||||||
@ -123,11 +152,7 @@ public class Shop {
|
|||||||
itemStack = product.clone();
|
itemStack = product.clone();
|
||||||
itemStack.setAmount(1);
|
itemStack.setAmount(1);
|
||||||
|
|
||||||
this.item = new ShopItem(plugin, itemStack, itemLocation);
|
item = new ShopItem(plugin, itemStack, itemLocation);
|
||||||
|
|
||||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
||||||
item.setVisible(p, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +188,9 @@ public class Shop {
|
|||||||
hologram = new Hologram(plugin, holoText, holoLocation);
|
hologram = new Hologram(plugin, holoText, holoLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep hologram text up to date
|
||||||
|
*/
|
||||||
public void updateHologramText() {
|
public void updateHologramText() {
|
||||||
String[] lines = getHologramText();
|
String[] lines = getHologramText();
|
||||||
String[] currentLines = hologram.getLines();
|
String[] currentLines = hologram.getLines();
|
||||||
@ -181,7 +209,7 @@ public class Shop {
|
|||||||
private String[] getHologramText() {
|
private String[] getHologramText() {
|
||||||
List<String> lines = new ArrayList<>();
|
List<String> lines = new ArrayList<>();
|
||||||
|
|
||||||
Map<HologramFormat.Requirement, Object> requirements = new HashMap<>();
|
Map<HologramFormat.Requirement, Object> requirements = new EnumMap<>(HologramFormat.Requirement.class);
|
||||||
requirements.put(HologramFormat.Requirement.VENDOR, getVendor().getName());
|
requirements.put(HologramFormat.Requirement.VENDOR, getVendor().getName());
|
||||||
requirements.put(HologramFormat.Requirement.AMOUNT, getProduct().getAmount());
|
requirements.put(HologramFormat.Requirement.AMOUNT, getProduct().getAmount());
|
||||||
requirements.put(HologramFormat.Requirement.ITEM_TYPE, getProduct().getType() + (getProduct().getDurability() > 0 ? ":" + getProduct().getDurability() : ""));
|
requirements.put(HologramFormat.Requirement.ITEM_TYPE, getProduct().getType() + (getProduct().getDurability() > 0 ? ":" + getProduct().getDurability() : ""));
|
||||||
@ -200,7 +228,7 @@ public class Shop {
|
|||||||
requirements.put(HologramFormat.Requirement.CHEST_SPACE, Utils.getFreeSpaceForItem(getInventoryHolder().getInventory(), getProduct()));
|
requirements.put(HologramFormat.Requirement.CHEST_SPACE, Utils.getFreeSpaceForItem(getInventoryHolder().getInventory(), getProduct()));
|
||||||
requirements.put(HologramFormat.Requirement.DURABILITY, getProduct().getDurability());
|
requirements.put(HologramFormat.Requirement.DURABILITY, getProduct().getDurability());
|
||||||
|
|
||||||
Map<Placeholder, Object> placeholders = new HashMap<>();
|
Map<Placeholder, Object> placeholders = new EnumMap<>(Placeholder.class);
|
||||||
placeholders.put(Placeholder.VENDOR, getVendor().getName());
|
placeholders.put(Placeholder.VENDOR, getVendor().getName());
|
||||||
placeholders.put(Placeholder.AMOUNT, getProduct().getAmount());
|
placeholders.put(Placeholder.AMOUNT, getProduct().getAmount());
|
||||||
placeholders.put(Placeholder.ITEM_NAME, LanguageUtils.getItemName(getProduct()));
|
placeholders.put(Placeholder.ITEM_NAME, LanguageUtils.getItemName(getProduct()));
|
||||||
@ -220,7 +248,7 @@ public class Shop {
|
|||||||
for (int i = 0; i < lineCount; i++) {
|
for (int i = 0; i < lineCount; i++) {
|
||||||
String format = plugin.getHologramFormat().getFormat(i, requirements, placeholders);
|
String format = plugin.getHologramFormat().getFormat(i, requirements, placeholders);
|
||||||
for (Placeholder placeholder : placeholders.keySet()) {
|
for (Placeholder placeholder : placeholders.keySet()) {
|
||||||
String replace = "";
|
String replace;
|
||||||
|
|
||||||
switch (placeholder) {
|
switch (placeholder) {
|
||||||
case BUY_PRICE:
|
case BUY_PRICE:
|
||||||
@ -241,7 +269,7 @@ public class Shop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.toArray(new String[lines.size()]);
|
return lines.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Location getHologramLocation(boolean doubleChest, Chest[] chests) {
|
private Location getHologramLocation(boolean doubleChest, Chest[] chests) {
|
||||||
@ -374,6 +402,14 @@ public class Shop {
|
|||||||
return item;
|
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.
|
* @return {@link InventoryHolder} of the shop or <b>null</b> if the shop has no chest.
|
||||||
*/
|
*/
|
||||||
@ -388,9 +424,4 @@ public class Shop {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ShopType {
|
|
||||||
NORMAL,
|
|
||||||
ADMIN
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,39 +10,40 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class ShopItem {
|
public class ShopItem {
|
||||||
|
|
||||||
private final ShopChest plugin;
|
private final ShopChest plugin;
|
||||||
private final Set<UUID> visibility = Collections.newSetFromMap(new ConcurrentHashMap<UUID, Boolean>());
|
|
||||||
|
// concurrent since update task is in async thread
|
||||||
|
// since this is a fake entity, item is hidden per default
|
||||||
|
private final Set<UUID> viewers = Collections.newSetFromMap(new ConcurrentHashMap<UUID, Boolean>());
|
||||||
private final ItemStack itemStack;
|
private final ItemStack itemStack;
|
||||||
private final Location location;
|
private final Location location;
|
||||||
|
|
||||||
private Object entityItem;
|
private final Class<?> packetPlayOutEntityDestroyClass = Utils.getNMSClass("PacketPlayOutEntityDestroy");
|
||||||
private int entityId;
|
private final Object[] creationPackets = new Object[3];
|
||||||
private Object[] creationPackets = new Object[3];
|
private final Object entityItem;
|
||||||
|
private final int entityId;
|
||||||
private Class<?> nmsWorldClass = Utils.getNMSClass("World");
|
|
||||||
private Class<?> craftWorldClass = Utils.getCraftClass("CraftWorld");
|
|
||||||
private Class<?> nmsItemStackClass = Utils.getNMSClass("ItemStack");
|
|
||||||
private Class<?> craftItemStackClass = Utils.getCraftClass("inventory.CraftItemStack");
|
|
||||||
private Class<?> entityItemClass = Utils.getNMSClass("EntityItem");
|
|
||||||
private Class<?> entityClass = Utils.getNMSClass("Entity");
|
|
||||||
private Class<?> packetPlayOutSpawnEntityClass = Utils.getNMSClass("PacketPlayOutSpawnEntity");
|
|
||||||
private Class<?> packetPlayOutEntityMetadataClass = Utils.getNMSClass("PacketPlayOutEntityMetadata");
|
|
||||||
private Class<?> dataWatcherClass = Utils.getNMSClass("DataWatcher");
|
|
||||||
private Class<?> packetPlayOutEntityDestroyClass = Utils.getNMSClass("PacketPlayOutEntityDestroy");
|
|
||||||
private Class<?> packetPlayOutEntityVelocityClass = Utils.getNMSClass("PacketPlayOutEntityVelocity");
|
|
||||||
|
|
||||||
public ShopItem(ShopChest plugin, ItemStack itemStack, Location location) {
|
public ShopItem(ShopChest plugin, ItemStack itemStack, Location location) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.itemStack = itemStack;
|
this.itemStack = itemStack;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
|
|
||||||
|
Class<?> packetPlayOutEntityVelocityClass = Utils.getNMSClass("PacketPlayOutEntityVelocity");
|
||||||
|
Class<?> dataWatcherClass = Utils.getNMSClass("DataWatcher");
|
||||||
|
Class<?> packetPlayOutEntityMetadataClass = Utils.getNMSClass("PacketPlayOutEntityMetadata");
|
||||||
|
Class<?> packetPlayOutSpawnEntityClass = Utils.getNMSClass("PacketPlayOutSpawnEntity");
|
||||||
|
Class<?> entityClass = Utils.getNMSClass("Entity");
|
||||||
|
Class<?> entityItemClass = Utils.getNMSClass("EntityItem");
|
||||||
|
Class<?> craftItemStackClass = Utils.getCraftClass("inventory.CraftItemStack");
|
||||||
|
Class<?> nmsItemStackClass = Utils.getNMSClass("ItemStack");
|
||||||
|
Class<?> craftWorldClass = Utils.getCraftClass("CraftWorld");
|
||||||
|
Class<?> nmsWorldClass = Utils.getNMSClass("World");
|
||||||
|
|
||||||
Class[] requiredClasses = new Class[] {
|
Class[] requiredClasses = new Class[] {
|
||||||
nmsWorldClass, craftWorldClass, nmsItemStackClass, craftItemStackClass, entityItemClass,
|
nmsWorldClass, craftWorldClass, nmsItemStackClass, craftItemStackClass, entityItemClass,
|
||||||
packetPlayOutSpawnEntityClass, packetPlayOutEntityMetadataClass, dataWatcherClass,
|
packetPlayOutSpawnEntityClass, packetPlayOutEntityMetadataClass, dataWatcherClass,
|
||||||
@ -52,14 +53,15 @@ public class ShopItem {
|
|||||||
for (Class c : requiredClasses) {
|
for (Class c : requiredClasses) {
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
plugin.debug("Failed to create shop item: Could not find all required classes");
|
plugin.debug("Failed to create shop item: Could not find all required classes");
|
||||||
|
entityItem = null;
|
||||||
|
entityId = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create();
|
Object tmpEntityItem = null;
|
||||||
}
|
int tmpEntityId = -1;
|
||||||
|
|
||||||
private void create() {
|
|
||||||
try {
|
try {
|
||||||
Object craftWorld = craftWorldClass.cast(location.getWorld());
|
Object craftWorld = craftWorldClass.cast(location.getWorld());
|
||||||
Object nmsWorld = craftWorldClass.getMethod("getHandle").invoke(craftWorld);
|
Object nmsWorld = craftWorldClass.getMethod("getHandle").invoke(craftWorld);
|
||||||
@ -67,74 +69,32 @@ public class ShopItem {
|
|||||||
Object nmsItemStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, itemStack);
|
Object nmsItemStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, itemStack);
|
||||||
|
|
||||||
Constructor<?> entityItemConstructor = entityItemClass.getConstructor(nmsWorldClass);
|
Constructor<?> entityItemConstructor = entityItemClass.getConstructor(nmsWorldClass);
|
||||||
entityItem = entityItemConstructor.newInstance(nmsWorld);
|
tmpEntityItem = entityItemConstructor.newInstance(nmsWorld);
|
||||||
|
|
||||||
entityItemClass.getMethod("setPosition", double.class, double.class, double.class).invoke(entityItem, location.getX(), location.getY(), location.getZ());
|
entityItemClass.getMethod("setPosition", double.class, double.class, double.class).invoke(tmpEntityItem, location.getX(), location.getY(), location.getZ());
|
||||||
entityItemClass.getMethod("setItemStack", nmsItemStackClass).invoke(entityItem, nmsItemStack);
|
entityItemClass.getMethod("setItemStack", nmsItemStackClass).invoke(tmpEntityItem, nmsItemStack);
|
||||||
if (Utils.getMajorVersion() >= 10) entityItemClass.getMethod("setNoGravity", boolean.class).invoke(entityItem, true);
|
if (Utils.getMajorVersion() >= 10) entityItemClass.getMethod("setNoGravity", boolean.class).invoke(tmpEntityItem, true);
|
||||||
|
|
||||||
Field ageField = entityItemClass.getDeclaredField("age");
|
Field ageField = entityItemClass.getDeclaredField("age");
|
||||||
ageField.setAccessible(true);
|
ageField.setAccessible(true);
|
||||||
ageField.setInt(entityItem, -32768);
|
ageField.setInt(tmpEntityItem, -32768);
|
||||||
|
|
||||||
entityId = (int) entityItemClass.getMethod("getId").invoke(entityItem);
|
tmpEntityId = (int) entityItemClass.getMethod("getId").invoke(tmpEntityItem);
|
||||||
Object dataWatcher = entityItemClass.getMethod("getDataWatcher").invoke(entityItem);
|
Object dataWatcher = entityItemClass.getMethod("getDataWatcher").invoke(tmpEntityItem);
|
||||||
|
|
||||||
creationPackets[0] = packetPlayOutSpawnEntityClass.getConstructor(entityClass, int.class).newInstance(entityItem, 2);
|
creationPackets[0] = packetPlayOutSpawnEntityClass.getConstructor(entityClass, int.class).newInstance(tmpEntityItem, 2);
|
||||||
creationPackets[1] = packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class).newInstance(entityId, dataWatcher, true);
|
creationPackets[1] = packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class).newInstance(tmpEntityId, dataWatcher, true);
|
||||||
creationPackets[2] = packetPlayOutEntityVelocityClass.getConstructor(int.class, double.class, double.class, double.class).newInstance(entityId, 0D, 0D, 0D);
|
creationPackets[2] = packetPlayOutEntityVelocityClass.getConstructor(int.class, double.class, double.class, double.class).newInstance(tmpEntityId, 0D, 0D, 0D);
|
||||||
} catch (NoSuchMethodException | NoSuchFieldException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
} catch (NoSuchMethodException | NoSuchFieldException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
plugin.getLogger().severe("Failed to create shop item");
|
plugin.getLogger().severe("Failed to create shop item");
|
||||||
plugin.debug("Failed to create shop item with reflection");
|
plugin.debug("Failed to create shop item with reflection");
|
||||||
plugin.debug(e);
|
plugin.debug(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entityItem = tmpEntityItem;
|
||||||
|
entityId = tmpEntityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
for (UUID uuid : visibility) {
|
|
||||||
Player p = Bukkit.getPlayer(uuid);
|
|
||||||
if (p != null) setVisible(p, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
setVisible(p, false);
|
|
||||||
setVisible(p, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVisible(Player p) {
|
|
||||||
return visibility.contains(p.getUniqueId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVisible(final Player p, boolean visible) {
|
|
||||||
if (isVisible(p) == visible)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
for (Object packet : this.creationPackets) {
|
|
||||||
Utils.sendPacket(plugin, packet, p);
|
|
||||||
}
|
|
||||||
visibility.add(p.getUniqueId());
|
|
||||||
} else {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
visibility.remove(p.getUniqueId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Clone of the location, where the shop item should be (it could have been moved by something, even though it shouldn't)
|
* @return Clone of the location, where the shop item should be (it could have been moved by something, even though it shouldn't)
|
||||||
* To get the exact location, use reflection and extract the location of the {@code EntityItem}
|
* To get the exact location, use reflection and extract the location of the {@code EntityItem}
|
||||||
@ -157,4 +117,83 @@ public class ShopItem {
|
|||||||
public ItemStack getItemStack() {
|
public ItemStack getItemStack() {
|
||||||
return itemStack.clone();
|
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) {
|
||||||
|
for (Object packet : creationPackets) {
|
||||||
|
Utils.sendPacket(plugin, packet, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
42
src/main/java/de/epiceric/shopchest/utils/FastMath.java
Normal file
42
src/main/java/de/epiceric/shopchest/utils/FastMath.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
75
src/main/java/de/epiceric/shopchest/utils/Operator.java
Normal file
75
src/main/java/de/epiceric/shopchest/utils/Operator.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -13,18 +13,15 @@ import org.bukkit.inventory.InventoryHolder;
|
|||||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class ShopUtils {
|
public class ShopUtils {
|
||||||
|
|
||||||
private final HashMap<Location, Shop> shopLocation = new HashMap<>();
|
// concurrent since it is updated in async task
|
||||||
private final HashMap<UUID, Location> playerLocation = new HashMap<>();
|
private final Map<UUID, Location> playerLocation = new ConcurrentHashMap<>();
|
||||||
|
private final Map<Location, Shop> shopLocation = new HashMap<>();
|
||||||
|
private final Collection<Shop> shopLocationValues = Collections.unmodifiableCollection(shopLocation.values());
|
||||||
private final ShopChest plugin;
|
private final ShopChest plugin;
|
||||||
|
|
||||||
public ShopUtils(ShopChest plugin) {
|
public ShopUtils(ShopChest plugin) {
|
||||||
@ -55,10 +52,24 @@ public class ShopUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all shops
|
* Get all shops
|
||||||
|
* Do not use for removing while iteration!
|
||||||
|
*
|
||||||
|
* @see #getShopsCopy()
|
||||||
* @return Read-only collection of all shops, may contain duplicates
|
* @return Read-only collection of all shops, may contain duplicates
|
||||||
*/
|
*/
|
||||||
public Collection<Shop> getShops() {
|
public Collection<Shop> getShops() {
|
||||||
return Collections.unmodifiableCollection(new ArrayList<>(shopLocation.values()));
|
return shopLocationValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all shops
|
||||||
|
* Same as {@link #getShops()} but this is safe to remove while iterating
|
||||||
|
*
|
||||||
|
* @see #getShops()
|
||||||
|
* @return Copy of collection of all shops, may contain duplicates
|
||||||
|
*/
|
||||||
|
public Collection<Shop> getShopsCopy() {
|
||||||
|
return new ArrayList<>(getShops());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,7 +237,7 @@ public class ShopUtils {
|
|||||||
plugin.getShopDatabase().connect(new Callback<Integer>(plugin) {
|
plugin.getShopDatabase().connect(new Callback<Integer>(plugin) {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(Integer result) {
|
public void onResult(Integer result) {
|
||||||
for (Shop shop : getShops()) {
|
for (Shop shop : getShopsCopy()) {
|
||||||
removeShop(shop, false);
|
removeShop(shop, false);
|
||||||
plugin.debug("Removed shop (#" + shop.getID() + ")");
|
plugin.debug("Removed shop (#" + shop.getID() + ")");
|
||||||
}
|
}
|
||||||
@ -278,36 +289,7 @@ public class ShopUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.getShopChestConfig().only_show_shops_in_sight) {
|
if (plugin.getShopChestConfig().only_show_shops_in_sight) {
|
||||||
Set<Shop> sight = getShopsInSight(player);
|
updateVisibleShops(player);
|
||||||
|
|
||||||
Set<Shop> _sight = new HashSet<>();
|
|
||||||
|
|
||||||
for (Shop shop : sight) {
|
|
||||||
_sight.add(shop);
|
|
||||||
if (shop.getHologram() != null && !shop.getHologram().isVisible(player)) {
|
|
||||||
shop.getHologram().showPlayer(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (plugin.getShopChestConfig().only_show_first_shop_in_sight) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
double itemDistSqr = Math.pow(plugin.getShopChestConfig().maximal_item_distance, 2);
|
|
||||||
|
|
||||||
for (Shop shop : getShops()) {
|
|
||||||
if (shop.getItem() != null && shop.getLocation().getWorld().getName().equals(player.getWorld().getName())) {
|
|
||||||
if (shop.getLocation().distanceSquared(player.getEyeLocation()) <= itemDistSqr) {
|
|
||||||
shop.getItem().setVisible(player, true);
|
|
||||||
} else {
|
|
||||||
shop.getItem().setVisible(player, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_sight.contains(shop)) {
|
|
||||||
if (shop.getHologram() != null) {
|
|
||||||
shop.getHologram().hidePlayer(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
updateNearestShops(player);
|
updateNearestShops(player);
|
||||||
}
|
}
|
||||||
@ -315,55 +297,109 @@ public class ShopUtils {
|
|||||||
playerLocation.put(player.getUniqueId(), player.getLocation());
|
playerLocation.put(player.getUniqueId(), player.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Shop> getShopsInSight(Player player) {
|
/**
|
||||||
double dist = plugin.getShopChestConfig().maximal_distance;
|
* Remove a player from the {@code playerLocation} map.
|
||||||
|
* This should only be called when really needed
|
||||||
Location loc = player.getEyeLocation();
|
*/
|
||||||
Vector direction = loc.getDirection();
|
public void resetPlayerLocation(Player player) {
|
||||||
|
playerLocation.remove(player.getUniqueId());
|
||||||
Set<Shop> shops = new HashSet<>();
|
|
||||||
|
|
||||||
double i = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
Location below = loc.clone().subtract(0, 1, 0);
|
|
||||||
|
|
||||||
Shop shop = getShop(loc);
|
|
||||||
if (shop != null) {
|
|
||||||
shops.add(shop);
|
|
||||||
} else if ((shop = getShop(below)) != null) {
|
|
||||||
shops.add(shop);
|
|
||||||
}
|
|
||||||
|
|
||||||
loc.add(direction);
|
|
||||||
i++;
|
|
||||||
} while (i <= dist - (dist%1));
|
|
||||||
|
|
||||||
direction.multiply(dist - (dist%1));
|
|
||||||
loc.add(direction);
|
|
||||||
|
|
||||||
Location below = loc.clone().subtract(0, 1, 0);
|
|
||||||
|
|
||||||
Shop shop = getShop(loc);
|
|
||||||
if (shop != null) {
|
|
||||||
shops.add(shop);
|
|
||||||
} else if ((shop = getShop(below)) != null) {
|
|
||||||
shops.add(shop);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shops;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static final double TARGET_THRESHOLD = 1;
|
||||||
* Update hologram and item of the shop for a player based on their distance to each other
|
|
||||||
* @param shop Shop to update
|
|
||||||
* @param player Player to show the update
|
|
||||||
*/
|
|
||||||
public void updateShop(Shop shop, Player player) {
|
|
||||||
double holoDistSqr = Math.pow(plugin.getShopChestConfig().maximal_distance, 2);
|
|
||||||
double itemDistSqr = Math.pow(plugin.getShopChestConfig().maximal_item_distance, 2);
|
|
||||||
|
|
||||||
updateShop(shop, player, holoDistSqr, itemDistSqr);
|
private void updateVisibleShops(Player player) {
|
||||||
|
double itemDistSquared = Math.pow(plugin.getShopChestConfig().maximal_item_distance, 2);
|
||||||
|
double hologramDistSquared = Math.pow(plugin.getShopChestConfig().maximal_distance, 2);
|
||||||
|
|
||||||
|
boolean firstShopInSight = plugin.getShopChestConfig().only_show_first_shop_in_sight;
|
||||||
|
|
||||||
|
// used if only_show_first_shop_in_sight
|
||||||
|
List<Shop> otherShopsInSight = firstShopInSight ? new ArrayList<Shop>() : null;
|
||||||
|
double nearestDistance = 0;
|
||||||
|
Shop nearestShop = null;
|
||||||
|
|
||||||
|
Location pLoc = player.getEyeLocation();
|
||||||
|
double pX = pLoc.getX();
|
||||||
|
double pY = pLoc.getY();
|
||||||
|
double pZ = pLoc.getZ();
|
||||||
|
Vector pDir = pLoc.getDirection();
|
||||||
|
double dirLength = pDir.length();
|
||||||
|
|
||||||
|
for (Shop shop : getShops()) {
|
||||||
|
Location shopLocation = shop.getLocation();
|
||||||
|
|
||||||
|
if (shopLocation.getWorld().getName().equals(player.getWorld().getName())) {
|
||||||
|
double distanceSquared = shop.getLocation().distanceSquared(player.getLocation());
|
||||||
|
|
||||||
|
// Display item based on distance
|
||||||
|
if (shop.hasItem()) {
|
||||||
|
if (distanceSquared <= itemDistSquared) {
|
||||||
|
shop.getItem().showPlayer(player);
|
||||||
|
} else {
|
||||||
|
shop.getItem().hidePlayer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display hologram based on sight
|
||||||
|
if (shop.hasHologram()) {
|
||||||
|
if (distanceSquared < hologramDistSquared) {
|
||||||
|
Location holoLocation = shop.getHologram().getLocation();
|
||||||
|
|
||||||
|
double x = holoLocation.getX() - pX;
|
||||||
|
double y = shopLocation.getY() - pY + 1.15; // chest block + item offset
|
||||||
|
double z = holoLocation.getZ() - pZ;
|
||||||
|
|
||||||
|
// See: org.bukkit.util.Vector#angle(Vector)
|
||||||
|
double angle = FastMath.acos(
|
||||||
|
(x * pDir.getX() + y * pDir.getY() + z * pDir.getZ())
|
||||||
|
/ (FastMath.sqrt(x * x + y * y + z * z) * dirLength)
|
||||||
|
);
|
||||||
|
|
||||||
|
double distance = FastMath.sqrt(distanceSquared);
|
||||||
|
|
||||||
|
// Check if is targeted
|
||||||
|
if (angle * distance < TARGET_THRESHOLD) {
|
||||||
|
// Display even if not the nearest
|
||||||
|
if (!firstShopInSight) {
|
||||||
|
shop.getHologram().showPlayer(player);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearestShop == null) {
|
||||||
|
// nearestShop is null
|
||||||
|
// => we guess this one will be the nearest
|
||||||
|
nearestShop = shop;
|
||||||
|
nearestDistance = distance;
|
||||||
|
continue;
|
||||||
|
} else if (distance < nearestDistance) {
|
||||||
|
// nearestShop is NOT null && this shop is nearest
|
||||||
|
// => we'll hide nearestShop, and guess this one will be the nearest
|
||||||
|
otherShopsInSight.add(nearestShop);
|
||||||
|
nearestShop = shop;
|
||||||
|
nearestDistance = distance;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// else: hologram is farther than nearest, so we hide it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not in sight
|
||||||
|
shop.getHologram().hidePlayer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstShopInSight) {
|
||||||
|
// we hide other shop as we wan't to display only the first
|
||||||
|
for (Shop shop : otherShopsInSight) {
|
||||||
|
// we already checked hasHologram() before adding it
|
||||||
|
shop.getHologram().hidePlayer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearestShop != null && nearestShop.hasHologram()) {
|
||||||
|
nearestShop.getHologram().showPlayer(player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateNearestShops(Player p) {
|
private void updateNearestShops(Player p) {
|
||||||
@ -371,27 +407,23 @@ public class ShopUtils {
|
|||||||
double itemDistSqr = Math.pow(plugin.getShopChestConfig().maximal_item_distance, 2);
|
double itemDistSqr = Math.pow(plugin.getShopChestConfig().maximal_item_distance, 2);
|
||||||
|
|
||||||
for (Shop shop : getShops()) {
|
for (Shop shop : getShops()) {
|
||||||
updateShop(shop, p, holoDistSqr, itemDistSqr);
|
if (p.getLocation().getWorld().getName().equals(shop.getLocation().getWorld().getName())) {
|
||||||
}
|
double distSqr = shop.getLocation().distanceSquared(p.getLocation());
|
||||||
}
|
|
||||||
|
|
||||||
private void updateShop(Shop shop, Player player, double holoDistSqr, double itemDistSqr) {
|
if (shop.hasHologram()) {
|
||||||
if (player.getLocation().getWorld().getName().equals(shop.getLocation().getWorld().getName())) {
|
if (distSqr <= holoDistSqr) {
|
||||||
double distSqr = shop.getLocation().distanceSquared(player.getLocation());
|
shop.getHologram().showPlayer(p);
|
||||||
|
} else {
|
||||||
if (shop.getHologram() != null) {
|
shop.getHologram().hidePlayer(p);
|
||||||
if (distSqr <= holoDistSqr) {
|
}
|
||||||
shop.getHologram().showPlayer(player);
|
|
||||||
} else {
|
|
||||||
shop.getHologram().hidePlayer(player);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (shop.getItem() != null) {
|
if (shop.hasItem()) {
|
||||||
if (distSqr <= itemDistSqr) {
|
if (distSqr <= itemDistSqr) {
|
||||||
shop.getItem().setVisible(player, true);
|
shop.getItem().showPlayer(p);
|
||||||
} else {
|
} else {
|
||||||
shop.getItem().setVisible(player, false);
|
shop.getItem().hidePlayer(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user