mirror of
https://github.com/IntellectualSites/PlotSquared.git
synced 2025-07-10 09:34:43 +02:00
Moved more packaged based on feedback
This commit is contained in:
@ -0,0 +1,53 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.chat.FancyMessage;
|
||||
import com.plotsquared.bukkit.player.BukkitPlayer;
|
||||
import com.plotsquared.config.Captions;
|
||||
import com.plotsquared.config.Settings;
|
||||
import com.plotsquared.player.ConsolePlayer;
|
||||
import com.plotsquared.plot.message.PlotMessage;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.plotsquared.util.ChatManager;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BukkitChatManager extends ChatManager<FancyMessage> {
|
||||
|
||||
@Override public FancyMessage builder() {
|
||||
return new FancyMessage("");
|
||||
}
|
||||
|
||||
@Override public void color(PlotMessage message, String color) {
|
||||
message.$(this).color(ChatColor.getByChar(Captions.color(color).substring(1)));
|
||||
}
|
||||
|
||||
@Override public void tooltip(PlotMessage message, PlotMessage... tooltips) {
|
||||
List<FancyMessage> lines =
|
||||
Arrays.stream(tooltips).map(tooltip -> tooltip.$(this)).collect(Collectors.toList());
|
||||
message.$(this).formattedTooltip(lines);
|
||||
}
|
||||
|
||||
@Override public void command(PlotMessage message, String command) {
|
||||
message.$(this).command(command);
|
||||
}
|
||||
|
||||
@Override public void text(PlotMessage message, String text) {
|
||||
message.$(this).then(ChatColor.stripColor(text));
|
||||
}
|
||||
|
||||
@Override public void send(PlotMessage plotMessage, PlotPlayer player) {
|
||||
if (player instanceof ConsolePlayer || !Settings.Chat.INTERACTIVE) {
|
||||
player.sendMessage(plotMessage.$(this).toOldMessageFormat());
|
||||
} else {
|
||||
plotMessage.$(this).send(((BukkitPlayer) player).player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void suggest(PlotMessage plotMessage, String command) {
|
||||
plotMessage.$(this).suggest(command);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.player.BukkitOfflinePlayer;
|
||||
import com.plotsquared.bukkit.player.BukkitPlayer;
|
||||
import com.plotsquared.player.OfflinePlotPlayer;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.plotsquared.util.EconHandler;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
public class BukkitEconHandler extends EconHandler {
|
||||
|
||||
private Economy econ;
|
||||
private Permission perms;
|
||||
|
||||
public boolean init() {
|
||||
if (this.econ == null || this.perms == null) {
|
||||
setupPermissions();
|
||||
setupEconomy();
|
||||
}
|
||||
return this.econ != null && this.perms != null;
|
||||
}
|
||||
|
||||
private boolean setupPermissions() {
|
||||
RegisteredServiceProvider<Permission> permissionProvider =
|
||||
Bukkit.getServer().getServicesManager().getRegistration(Permission.class);
|
||||
if (permissionProvider != null) {
|
||||
this.perms = permissionProvider.getProvider();
|
||||
}
|
||||
return this.perms != null;
|
||||
}
|
||||
|
||||
private boolean setupEconomy() {
|
||||
if (Bukkit.getServer().getPluginManager().getPlugin("Vault") == null) {
|
||||
return false;
|
||||
}
|
||||
RegisteredServiceProvider<Economy> economyProvider =
|
||||
Bukkit.getServer().getServicesManager().getRegistration(Economy.class);
|
||||
if (economyProvider != null) {
|
||||
this.econ = economyProvider.getProvider();
|
||||
}
|
||||
return this.econ != null;
|
||||
}
|
||||
|
||||
@Override public double getMoney(PlotPlayer player) {
|
||||
double bal = super.getMoney(player);
|
||||
if (Double.isNaN(bal)) {
|
||||
return this.econ.getBalance(((BukkitPlayer) player).player);
|
||||
}
|
||||
return bal;
|
||||
}
|
||||
|
||||
@Override public void withdrawMoney(PlotPlayer player, double amount) {
|
||||
this.econ.withdrawPlayer(((BukkitPlayer) player).player, amount);
|
||||
}
|
||||
|
||||
@Override public void depositMoney(PlotPlayer player, double amount) {
|
||||
this.econ.depositPlayer(((BukkitPlayer) player).player, amount);
|
||||
}
|
||||
|
||||
@Override public void depositMoney(OfflinePlotPlayer player, double amount) {
|
||||
this.econ.depositPlayer(((BukkitOfflinePlayer) player).player, amount);
|
||||
}
|
||||
|
||||
@Override public boolean hasPermission(String world, String player, String perm) {
|
||||
return this.perms.playerHas(world, Bukkit.getOfflinePlayer(player), perm);
|
||||
}
|
||||
|
||||
@Override public double getBalance(PlotPlayer player) {
|
||||
return this.econ.getBalance(player.getName());
|
||||
}
|
||||
|
||||
public void setPermission(String world, String player, String perm, boolean value) {
|
||||
if (value) {
|
||||
this.perms.playerAdd(world, player, perm);
|
||||
} else {
|
||||
this.perms.playerRemove(world, player, perm);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.player.BukkitPlayer;
|
||||
import com.plotsquared.plot.PlotInventory;
|
||||
import com.plotsquared.plot.PlotItemStack;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.plotsquared.util.InventoryUtil;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BukkitInventoryUtil extends InventoryUtil {
|
||||
|
||||
@Override public void open(PlotInventory inv) {
|
||||
BukkitPlayer bp = (BukkitPlayer) inv.player;
|
||||
Inventory inventory = Bukkit.createInventory(null, inv.size * 9, inv.getTitle());
|
||||
PlotItemStack[] items = inv.getItems();
|
||||
for (int i = 0; i < inv.size * 9; i++) {
|
||||
PlotItemStack item = items[i];
|
||||
if (item != null) {
|
||||
inventory.setItem(i, getItem(item));
|
||||
}
|
||||
}
|
||||
bp.player.openInventory(inventory);
|
||||
}
|
||||
|
||||
@Override public void close(PlotInventory inv) {
|
||||
if (!inv.isOpen()) {
|
||||
return;
|
||||
}
|
||||
BukkitPlayer bp = (BukkitPlayer) inv.player;
|
||||
bp.player.closeInventory();
|
||||
}
|
||||
|
||||
@Override public void setItem(PlotInventory inv, int index, PlotItemStack item) {
|
||||
BukkitPlayer bp = (BukkitPlayer) inv.player;
|
||||
InventoryView opened = bp.player.getOpenInventory();
|
||||
if (!inv.isOpen()) {
|
||||
return;
|
||||
}
|
||||
opened.setItem(index, getItem(item));
|
||||
bp.player.updateInventory();
|
||||
}
|
||||
|
||||
private static ItemStack getItem(PlotItemStack item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
ItemStack stack = new ItemStack(BukkitAdapter.adapt(item.getType()), item.amount);
|
||||
ItemMeta meta = null;
|
||||
if (item.name != null) {
|
||||
meta = stack.getItemMeta();
|
||||
meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', item.name));
|
||||
}
|
||||
if (item.lore != null) {
|
||||
if (meta == null) {
|
||||
meta = stack.getItemMeta();
|
||||
}
|
||||
List<String> lore = new ArrayList<>();
|
||||
for (String entry : item.lore) {
|
||||
lore.add(ChatColor.translateAlternateColorCodes('&', entry));
|
||||
}
|
||||
meta.setLore(lore);
|
||||
}
|
||||
if (meta != null) {
|
||||
stack.setItemMeta(meta);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
public PlotItemStack getItem(ItemStack item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
// int id = item.getTypeId();
|
||||
Material id = item.getType();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
int amount = item.getAmount();
|
||||
String name = null;
|
||||
String[] lore = null;
|
||||
if (item.hasItemMeta()) {
|
||||
assert meta != null;
|
||||
if (meta.hasDisplayName()) {
|
||||
name = meta.getDisplayName();
|
||||
}
|
||||
if (meta.hasLore()) {
|
||||
List<String> itemLore = meta.getLore();
|
||||
assert itemLore != null;
|
||||
lore = itemLore.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
return new PlotItemStack(id.name(), amount, name, lore);
|
||||
}
|
||||
|
||||
@Override public PlotItemStack[] getItems(PlotPlayer player) {
|
||||
BukkitPlayer bp = (BukkitPlayer) player;
|
||||
PlayerInventory inv = bp.player.getInventory();
|
||||
return IntStream.range(0, 36).mapToObj(i -> getItem(inv.getItem(i)))
|
||||
.toArray(PlotItemStack[]::new);
|
||||
}
|
||||
|
||||
@Override public boolean isOpen(PlotInventory plotInventory) {
|
||||
if (!plotInventory.isOpen()) {
|
||||
return false;
|
||||
}
|
||||
BukkitPlayer bp = (BukkitPlayer) plotInventory.player;
|
||||
InventoryView opened = bp.player.getOpenInventory();
|
||||
if (plotInventory.isOpen()) {
|
||||
if (opened.getType() == InventoryType.CRAFTING) {
|
||||
opened.getTitle();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.generator.BukkitPlotGenerator;
|
||||
import com.plotsquared.configuration.ConfigurationSection;
|
||||
import com.plotsquared.configuration.file.YamlConfiguration;
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.config.ConfigurationNode;
|
||||
import com.plotsquared.generator.GeneratorWrapper;
|
||||
import com.plotsquared.plot.PlotArea;
|
||||
import com.plotsquared.plot.PlotAreaType;
|
||||
import com.plotsquared.plot.SetupObject;
|
||||
import com.plotsquared.util.SetupUtils;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.WorldCreator;
|
||||
import org.bukkit.WorldType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
|
||||
public class BukkitSetupUtils extends SetupUtils {
|
||||
|
||||
@Override public void updateGenerators() {
|
||||
if (!SetupUtils.generators.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String testWorld = "CheckingPlotSquaredGenerator";
|
||||
for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
|
||||
try {
|
||||
if (plugin.isEnabled()) {
|
||||
ChunkGenerator generator = plugin.getDefaultWorldGenerator(testWorld, "");
|
||||
if (generator != null) {
|
||||
PlotSquared.get().removePlotAreas(testWorld);
|
||||
String name = plugin.getDescription().getName();
|
||||
GeneratorWrapper<?> wrapped;
|
||||
if (generator instanceof GeneratorWrapper<?>) {
|
||||
wrapped = (GeneratorWrapper<?>) generator;
|
||||
} else {
|
||||
wrapped = new BukkitPlotGenerator(testWorld, generator);
|
||||
}
|
||||
SetupUtils.generators.put(name, wrapped);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) { // Recover from third party generator error
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void unload(String worldName, boolean save) {
|
||||
World world = Bukkit.getWorld(worldName);
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
World dw = Bukkit.getWorlds().get(0);
|
||||
for (Player player : world.getPlayers()) {
|
||||
PaperLib.teleportAsync(player,dw.getSpawnLocation());
|
||||
}
|
||||
if (save) {
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
chunk.unload(true);
|
||||
}
|
||||
} else {
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
chunk.unload(false);
|
||||
}
|
||||
}
|
||||
Bukkit.unloadWorld(world, false);
|
||||
}
|
||||
|
||||
@Override public String setupWorld(SetupObject object) {
|
||||
SetupUtils.manager.updateGenerators();
|
||||
ConfigurationNode[] steps = object.step == null ? new ConfigurationNode[0] : object.step;
|
||||
String world = object.world;
|
||||
PlotAreaType type = object.type;
|
||||
String worldPath = "worlds." + object.world;
|
||||
switch (type) {
|
||||
case PARTIAL: {
|
||||
if (object.id != null) {
|
||||
if (!PlotSquared.get().worlds.contains(worldPath)) {
|
||||
PlotSquared.get().worlds.createSection(worldPath);
|
||||
}
|
||||
ConfigurationSection worldSection =
|
||||
PlotSquared.get().worlds.getConfigurationSection(worldPath);
|
||||
String areaName = object.id + "-" + object.min + "-" + object.max;
|
||||
String areaPath = "areas." + areaName;
|
||||
if (!worldSection.contains(areaPath)) {
|
||||
worldSection.createSection(areaPath);
|
||||
}
|
||||
ConfigurationSection areaSection =
|
||||
worldSection.getConfigurationSection(areaPath);
|
||||
HashMap<String, Object> options = new HashMap<>();
|
||||
for (ConfigurationNode step : steps) {
|
||||
options.put(step.getConstant(), step.getValue());
|
||||
}
|
||||
options.put("generator.type", object.type.toString());
|
||||
options.put("generator.terrain", object.terrain.toString());
|
||||
options.put("generator.plugin", object.plotManager);
|
||||
if (object.setupGenerator != null && !object.setupGenerator
|
||||
.equals(object.plotManager)) {
|
||||
options.put("generator.init", object.setupGenerator);
|
||||
}
|
||||
for (Entry<String, Object> entry : options.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (worldSection.contains(key)) {
|
||||
Object current = worldSection.get(key);
|
||||
if (!Objects.equals(value, current)) {
|
||||
areaSection.set(key, value);
|
||||
}
|
||||
} else {
|
||||
worldSection.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
GeneratorWrapper<?> gen = SetupUtils.generators.get(object.setupGenerator);
|
||||
if (gen != null && gen.isFull()) {
|
||||
object.setupGenerator = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUGMENTED: {
|
||||
if (!object.plotManager.endsWith(":single")) {
|
||||
if (!PlotSquared.get().worlds.contains(worldPath)) {
|
||||
PlotSquared.get().worlds.createSection(worldPath);
|
||||
}
|
||||
if (steps.length != 0) {
|
||||
ConfigurationSection worldSection =
|
||||
PlotSquared.get().worlds.getConfigurationSection(worldPath);
|
||||
for (ConfigurationNode step : steps) {
|
||||
worldSection.set(step.getConstant(), step.getValue());
|
||||
}
|
||||
}
|
||||
PlotSquared.get().worlds
|
||||
.set("worlds." + world + ".generator.type", object.type.toString());
|
||||
PlotSquared.get().worlds
|
||||
.set("worlds." + world + ".generator.terrain", object.terrain.toString());
|
||||
PlotSquared.get().worlds
|
||||
.set("worlds." + world + ".generator.plugin", object.plotManager);
|
||||
if (object.setupGenerator != null && !object.setupGenerator
|
||||
.equals(object.plotManager)) {
|
||||
PlotSquared.get().worlds
|
||||
.set("worlds." + world + ".generator.init", object.setupGenerator);
|
||||
}
|
||||
}
|
||||
GeneratorWrapper<?> gen = SetupUtils.generators.get(object.setupGenerator);
|
||||
if (gen != null && gen.isFull()) {
|
||||
object.setupGenerator = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NORMAL: {
|
||||
if (steps.length != 0) {
|
||||
if (!PlotSquared.get().worlds.contains(worldPath)) {
|
||||
PlotSquared.get().worlds.createSection(worldPath);
|
||||
}
|
||||
ConfigurationSection worldSection =
|
||||
PlotSquared.get().worlds.getConfigurationSection(worldPath);
|
||||
for (ConfigurationNode step : steps) {
|
||||
worldSection.set(step.getConstant(), step.getValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
PlotSquared.get().worlds.save(PlotSquared.get().worldsFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin("Multiverse-Core");
|
||||
if (object.setupGenerator != null) {
|
||||
if (plugin != null && plugin.isEnabled()) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(),
|
||||
"mv create " + world + " normal -g " + object.setupGenerator);
|
||||
setGenerator(world, object.setupGenerator);
|
||||
if (Bukkit.getWorld(world) != null) {
|
||||
return world;
|
||||
}
|
||||
}
|
||||
WorldCreator wc = new WorldCreator(object.world);
|
||||
wc.generator(object.setupGenerator);
|
||||
wc.environment(Environment.NORMAL);
|
||||
wc.type(WorldType.FLAT);
|
||||
Bukkit.createWorld(wc);
|
||||
setGenerator(world, object.setupGenerator);
|
||||
} else {
|
||||
if (plugin != null && plugin.isEnabled()) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(),
|
||||
"mv create " + world + " normal");
|
||||
if (Bukkit.getWorld(world) != null) {
|
||||
return world;
|
||||
}
|
||||
}
|
||||
World bw =
|
||||
Bukkit.createWorld(new WorldCreator(object.world).environment(Environment.NORMAL));
|
||||
}
|
||||
return object.world;
|
||||
}
|
||||
|
||||
public void setGenerator(String world, String generator) {
|
||||
if (Bukkit.getWorlds().isEmpty() || !Bukkit.getWorlds().get(0).getName().equals(world)) {
|
||||
return;
|
||||
}
|
||||
File file = new File("bukkit.yml").getAbsoluteFile();
|
||||
YamlConfiguration yml = YamlConfiguration.loadConfiguration(file);
|
||||
yml.set("worlds." + world + ".generator", generator);
|
||||
try {
|
||||
yml.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String getGenerator(PlotArea plotArea) {
|
||||
if (SetupUtils.generators.isEmpty()) {
|
||||
updateGenerators();
|
||||
}
|
||||
World world = Bukkit.getWorld(plotArea.getWorldName());
|
||||
if (world == null) {
|
||||
return null;
|
||||
}
|
||||
ChunkGenerator generator = world.getGenerator();
|
||||
if (!(generator instanceof BukkitPlotGenerator)) {
|
||||
return null;
|
||||
}
|
||||
for (Entry<String, GeneratorWrapper<?>> entry : SetupUtils.generators.entrySet()) {
|
||||
GeneratorWrapper<?> current = entry.getValue();
|
||||
if (current.equals(generator)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.BukkitMain;
|
||||
import com.plotsquared.util.tasks.TaskManager;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class BukkitTaskManager extends TaskManager {
|
||||
|
||||
private final BukkitMain bukkitMain;
|
||||
|
||||
public BukkitTaskManager(BukkitMain bukkitMain) {
|
||||
this.bukkitMain = bukkitMain;
|
||||
}
|
||||
|
||||
@Override public int taskRepeat(Runnable runnable, int interval) {
|
||||
return this.bukkitMain.getServer().getScheduler()
|
||||
.scheduleSyncRepeatingTask(this.bukkitMain, runnable, interval, interval);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") @Override
|
||||
public int taskRepeatAsync(Runnable runnable, int interval) {
|
||||
return this.bukkitMain.getServer().getScheduler()
|
||||
.scheduleAsyncRepeatingTask(this.bukkitMain, runnable, interval, interval);
|
||||
}
|
||||
|
||||
@Override public void taskAsync(Runnable runnable) {
|
||||
if (this.bukkitMain.isEnabled()) {
|
||||
this.bukkitMain.getServer().getScheduler()
|
||||
.runTaskAsynchronously(this.bukkitMain, runnable);
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void task(Runnable runnable) {
|
||||
this.bukkitMain.getServer().getScheduler().runTask(this.bukkitMain, runnable).getTaskId();
|
||||
}
|
||||
|
||||
@Override public void taskLater(Runnable runnable, int delay) {
|
||||
this.bukkitMain.getServer().getScheduler().runTaskLater(this.bukkitMain, runnable, delay)
|
||||
.getTaskId();
|
||||
}
|
||||
|
||||
@Override public void taskLaterAsync(Runnable runnable, int delay) {
|
||||
this.bukkitMain.getServer().getScheduler()
|
||||
.runTaskLaterAsynchronously(this.bukkitMain, runnable, delay);
|
||||
}
|
||||
|
||||
@Override public void cancelTask(int task) {
|
||||
if (task != -1) {
|
||||
Bukkit.getScheduler().cancelTask(task);
|
||||
}
|
||||
}
|
||||
}
|
540
Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java
Normal file
540
Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java
Normal file
@ -0,0 +1,540 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.BukkitMain;
|
||||
import com.plotsquared.bukkit.player.BukkitPlayer;
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.config.Captions;
|
||||
import com.plotsquared.location.Location;
|
||||
import com.plotsquared.plot.Plot;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.plotsquared.util.tasks.RunnableVal;
|
||||
import com.plotsquared.util.MainUtil;
|
||||
import com.plotsquared.util.MathMan;
|
||||
import com.plotsquared.util.StringComparison;
|
||||
import com.plotsquared.util.tasks.TaskManager;
|
||||
import com.plotsquared.util.uuid.UUIDHandler;
|
||||
import com.plotsquared.util.WorldUtil;
|
||||
import com.plotsquared.util.BlockUtil;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import lombok.NonNull;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.data.Directional;
|
||||
import org.bukkit.block.data.type.WallSign;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public class BukkitUtil extends WorldUtil {
|
||||
|
||||
private static Method biomeSetter;
|
||||
static {
|
||||
try {
|
||||
biomeSetter = World.class.getMethod("setBiome", Integer.class, Integer.class, Biome.class);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private static String lastString = null;
|
||||
private static World lastWorld = null;
|
||||
|
||||
private static Player lastPlayer = null;
|
||||
private static PlotPlayer lastPlotPlayer = null;
|
||||
|
||||
public static void removePlayer(String player) {
|
||||
lastPlayer = null;
|
||||
lastPlotPlayer = null;
|
||||
}
|
||||
|
||||
public static PlotPlayer getPlayer(@NonNull final OfflinePlayer op) {
|
||||
if (op.isOnline()) {
|
||||
return getPlayer(op.getPlayer());
|
||||
}
|
||||
final Player player = OfflinePlayerUtil.loadPlayer(op);
|
||||
player.loadData();
|
||||
return new BukkitPlayer(player, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a plot based on the location.
|
||||
*
|
||||
* @param location the location to check
|
||||
* @return plot if found, otherwise it creates a temporary plot
|
||||
* @see Plot
|
||||
*/
|
||||
public static Plot getPlot(org.bukkit.Location location) {
|
||||
if (location == null) {
|
||||
return null;
|
||||
}
|
||||
return getLocation(location).getPlot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a plot based on the player location.
|
||||
*
|
||||
* @param player the player to check
|
||||
* @return plot if found, otherwise it creates a temporary plot
|
||||
* @see #getPlot(org.bukkit.Location)
|
||||
* @see Plot
|
||||
*/
|
||||
public static Plot getPlot(Player player) {
|
||||
return getPlot(player.getLocation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PlotPlayer for an offline player.
|
||||
*
|
||||
* <p>Note that this will work if the player is offline, however not all
|
||||
* functionality will work.
|
||||
*
|
||||
* @param player the player to wrap
|
||||
* @return a {@code PlotPlayer}
|
||||
* @see PlotPlayer#wrap(Object)
|
||||
*/
|
||||
public static PlotPlayer wrapPlayer(OfflinePlayer player) {
|
||||
return PlotPlayer.wrap(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PlotPlayer for a player. The PlotPlayer is usually cached and
|
||||
* will provide useful functions relating to players.
|
||||
*
|
||||
* @param player the player to wrap
|
||||
* @return a {@code PlotPlayer}
|
||||
* @see PlotPlayer#wrap(Object)
|
||||
*/
|
||||
public static PlotPlayer wrapPlayer(Player player) {
|
||||
return PlotPlayer.wrap(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PlotPlayer for a UUID. The PlotPlayer is usually cached and
|
||||
* will provide useful functions relating to players.
|
||||
*
|
||||
* @param uuid the uuid to wrap
|
||||
* @return a {@code PlotPlayer}
|
||||
* @see PlotPlayer#wrap(Object)
|
||||
*/
|
||||
@Override public PlotPlayer wrapPlayer(UUID uuid) {
|
||||
return PlotPlayer.wrap(Bukkit.getOfflinePlayer(uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of plots, which the player is able to build in.
|
||||
*
|
||||
* @param player player, for whom we're getting the plots
|
||||
* @return the number of allowed plots
|
||||
*/
|
||||
public static int getAllowedPlots(Player player) {
|
||||
PlotPlayer plotPlayer = PlotPlayer.wrap(player);
|
||||
return plotPlayer.getAllowedPlots();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not a player is in a plot.
|
||||
*
|
||||
* @param player who we're checking for
|
||||
* @return true if the player is in a plot, false if not-
|
||||
*/
|
||||
public static boolean isInPlot(Player player) {
|
||||
return getPlot(player) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a collection containing the players plots.
|
||||
*
|
||||
* @param world Specify the world we want to select the plots from
|
||||
* @param player Player, for whom we're getting the plots
|
||||
* @return a set containing the players plots
|
||||
* @see Plot
|
||||
*/
|
||||
public static Set<Plot> getPlayerPlots(String world, Player player) {
|
||||
if (world == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return PlotPlayer.wrap(player).getPlots(world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a player. The message supports color codes.
|
||||
*
|
||||
* @param player the recipient of the message
|
||||
* @param string the message
|
||||
* @see MainUtil#sendMessage(PlotPlayer, String)
|
||||
*/
|
||||
public static void sendMessage(Player player, String string) {
|
||||
MainUtil.sendMessage(BukkitUtil.getPlayer(player), string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player plot count.
|
||||
*
|
||||
* @param world Specify the world we want to select the plots from
|
||||
* @param player Player, for whom we're getting the plot count
|
||||
* @return the number of plots the player has
|
||||
*/
|
||||
public static int getPlayerPlotCount(String world, Player player) {
|
||||
if (world == null) {
|
||||
return 0;
|
||||
}
|
||||
return BukkitUtil.getPlayer(player).getPlotCount(world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a player.
|
||||
*
|
||||
* @param player the recipient of the message
|
||||
* @param caption the message
|
||||
*/
|
||||
public static void sendMessage(Player player, Captions caption) {
|
||||
MainUtil.sendMessage(BukkitUtil.getPlayer(player), caption);
|
||||
}
|
||||
|
||||
public static PlotPlayer getPlayer(@NonNull final Player player) {
|
||||
if (player == lastPlayer) {
|
||||
return lastPlotPlayer;
|
||||
}
|
||||
final String name = player.getName();
|
||||
final PlotPlayer plotPlayer = UUIDHandler.getPlayer(name);
|
||||
if (plotPlayer != null) {
|
||||
return plotPlayer;
|
||||
}
|
||||
lastPlotPlayer = new BukkitPlayer(player);
|
||||
UUIDHandler.getPlayers().put(name, lastPlotPlayer);
|
||||
lastPlayer = player;
|
||||
return lastPlotPlayer;
|
||||
}
|
||||
|
||||
public static Location getLocation(@NonNull final org.bukkit.Location location) {
|
||||
return new Location(location.getWorld().getName(), MathMan.roundInt(location.getX()),
|
||||
MathMan.roundInt(location.getY()), MathMan.roundInt(location.getZ()));
|
||||
}
|
||||
|
||||
public static Location getLocationFull(@NonNull final org.bukkit.Location location) {
|
||||
return new Location(location.getWorld().getName(), MathMan.roundInt(location.getX()),
|
||||
MathMan.roundInt(location.getY()), MathMan.roundInt(location.getZ()), location.getYaw(),
|
||||
location.getPitch());
|
||||
}
|
||||
|
||||
public static org.bukkit.Location getLocation(@NonNull final Location location) {
|
||||
return new org.bukkit.Location(getWorld(location.getWorld()), location.getX(),
|
||||
location.getY(), location.getZ());
|
||||
}
|
||||
|
||||
public static World getWorld(@NonNull final String string) {
|
||||
return Bukkit.getWorld(string);
|
||||
}
|
||||
|
||||
public static String getWorld(@NonNull final Entity entity) {
|
||||
return entity.getWorld().getName();
|
||||
}
|
||||
|
||||
public static List<Entity> getEntities(@NonNull final String worldName) {
|
||||
World world = getWorld(worldName);
|
||||
if (world != null) {
|
||||
return world.getEntities();
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public static Location getLocation(@NonNull final Entity entity) {
|
||||
final org.bukkit.Location location = entity.getLocation();
|
||||
String world = location.getWorld().getName();
|
||||
return new Location(world, location.getBlockX(), location.getBlockY(),
|
||||
location.getBlockZ());
|
||||
}
|
||||
|
||||
@NotNull public static Location getLocationFull(@NonNull final Entity entity) {
|
||||
final org.bukkit.Location location = entity.getLocation();
|
||||
return new Location(location.getWorld().getName(), MathMan.roundInt(location.getX()),
|
||||
MathMan.roundInt(location.getY()), MathMan.roundInt(location.getZ()), location.getYaw(),
|
||||
location.getPitch());
|
||||
}
|
||||
|
||||
public static Material getMaterial(@NonNull final BlockState plotBlock) {
|
||||
return BukkitAdapter.adapt(plotBlock.getBlockType());
|
||||
}
|
||||
|
||||
@Override public boolean isBlockSame(BlockState block1, BlockState block2) {
|
||||
if (block1.equals(block2)) {
|
||||
return true;
|
||||
}
|
||||
Material mat1 = getMaterial(block1), mat2 = getMaterial(block2);
|
||||
return mat1 == mat2;
|
||||
}
|
||||
|
||||
@Override public boolean isWorld(@NonNull final String worldName) {
|
||||
return getWorld(worldName) != null;
|
||||
}
|
||||
|
||||
@Override public void getBiome(String world, int x, int z, final Consumer<BiomeType> result) {
|
||||
ensureLoaded(world, x, z, chunk ->
|
||||
result.accept(BukkitAdapter.adapt(getWorld(world).getBiome(x, z))));
|
||||
}
|
||||
|
||||
@Override public BiomeType getBiomeSynchronous(String world, int x, int z) {
|
||||
return BukkitAdapter.adapt(getWorld(world).getBiome(x, z));
|
||||
}
|
||||
|
||||
@Override public void getHighestBlock(@NonNull final String world, final int x, final int z,
|
||||
final IntConsumer result) {
|
||||
ensureLoaded(world, x, z, chunk -> {
|
||||
final World bukkitWorld = getWorld(world);
|
||||
// Skip top and bottom block
|
||||
int air = 1;
|
||||
for (int y = bukkitWorld.getMaxHeight() - 1; y >= 0; y--) {
|
||||
Block block = bukkitWorld.getBlockAt(x, y, z);
|
||||
Material type = block.getType();
|
||||
if (type.isSolid()) {
|
||||
if (air > 1) {
|
||||
result.accept(y);
|
||||
return;
|
||||
}
|
||||
air = 0;
|
||||
} else {
|
||||
if (block.isLiquid()) {
|
||||
result.accept(y);
|
||||
return;
|
||||
}
|
||||
air++;
|
||||
}
|
||||
}
|
||||
result.accept(bukkitWorld.getMaxHeight() - 1);
|
||||
});
|
||||
}
|
||||
|
||||
@Override public int getHighestBlockSynchronous(String world, int x, int z) {
|
||||
final World bukkitWorld = getWorld(world);
|
||||
// Skip top and bottom block
|
||||
int air = 1;
|
||||
for (int y = bukkitWorld.getMaxHeight() - 1; y >= 0; y--) {
|
||||
Block block = bukkitWorld.getBlockAt(x, y, z);
|
||||
Material type = block.getType();
|
||||
if (type.isSolid()) {
|
||||
if (air > 1) {
|
||||
return y;
|
||||
}
|
||||
air = 0;
|
||||
} else {
|
||||
if (block.isLiquid()) {
|
||||
return y;
|
||||
}
|
||||
air++;
|
||||
}
|
||||
}
|
||||
return bukkitWorld.getMaxHeight() - 1;
|
||||
}
|
||||
|
||||
@Override public void getSign(@NonNull final Location location, final Consumer<String[]> result) {
|
||||
ensureLoaded(location, chunk -> {
|
||||
final Block block = chunk.getWorld().getBlockAt(getLocation(location));
|
||||
if (block.getState() instanceof Sign) {
|
||||
Sign sign = (Sign) block.getState();
|
||||
result.accept(sign.getLines());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override @Nullable public String[] getSignSynchronous(@NonNull final Location location) {
|
||||
Block block = getWorld(location.getWorld())
|
||||
.getBlockAt(location.getX(), location.getY(), location.getZ());
|
||||
return TaskManager.IMP.sync(new RunnableVal<String[]>() {
|
||||
@Override public void run(String[] value) {
|
||||
if (block.getState() instanceof Sign) {
|
||||
Sign sign = (Sign) block.getState();
|
||||
this.value = sign.getLines();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public Location getSpawn(@NonNull final String world) {
|
||||
final org.bukkit.Location temp = getWorld(world).getSpawnLocation();
|
||||
return new Location(world, temp.getBlockX(), temp.getBlockY(), temp.getBlockZ(),
|
||||
temp.getYaw(), temp.getPitch());
|
||||
}
|
||||
|
||||
@Override public void setSpawn(@NonNull final Location location) {
|
||||
final World world = getWorld(location.getWorld());
|
||||
if (world != null) {
|
||||
world.setSpawnLocation(location.getX(), location.getY(), location.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void saveWorld(@NonNull final String worldName) {
|
||||
final World world = getWorld(worldName);
|
||||
if (world != null) {
|
||||
world.save();
|
||||
}
|
||||
}
|
||||
|
||||
@Override @SuppressWarnings("deprecation")
|
||||
public void setSign(@NonNull final String worldName, final int x, final int y, final int z,
|
||||
@NonNull final String[] lines) {
|
||||
ensureLoaded(worldName, x, z, chunk -> {
|
||||
final World world = getWorld(worldName);
|
||||
final Block block = world.getBlockAt(x, y, z);
|
||||
// block.setType(Material.AIR);
|
||||
final Material type = block.getType();
|
||||
if (type != Material.LEGACY_SIGN && type != Material.LEGACY_WALL_SIGN) {
|
||||
BlockFace facing = BlockFace.EAST;
|
||||
if (world.getBlockAt(x, y, z + 1).getType().isSolid()) {
|
||||
facing = BlockFace.NORTH;
|
||||
} else if (world.getBlockAt(x + 1, y, z).getType().isSolid()) {
|
||||
facing = BlockFace.WEST;
|
||||
} else if (world.getBlockAt(x, y, z - 1).getType().isSolid()) {
|
||||
facing = BlockFace.SOUTH;
|
||||
}
|
||||
if (PlotSquared.get().IMP.getServerVersion()[1] == 13) {
|
||||
block.setType(Material.valueOf("WALL_SIGN"), false);
|
||||
} else {
|
||||
block.setType(Material.valueOf("OAK_WALL_SIGN"), false);
|
||||
}
|
||||
if (!(block.getBlockData() instanceof WallSign)) {
|
||||
PlotSquared.debug(block.getBlockData().getAsString());
|
||||
throw new RuntimeException("Something went wrong generating a sign");
|
||||
}
|
||||
final Directional sign = (Directional) block.getBlockData();
|
||||
sign.setFacing(facing);
|
||||
block.setBlockData(sign, false);
|
||||
}
|
||||
final org.bukkit.block.BlockState blockstate = block.getState();
|
||||
if (blockstate instanceof Sign) {
|
||||
final Sign sign = (Sign) blockstate;
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
sign.setLine(i, lines[i]);
|
||||
}
|
||||
sign.update(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public boolean isBlockSolid(@NonNull final BlockState block) {
|
||||
return block.getBlockType().getMaterial().isSolid();
|
||||
}
|
||||
|
||||
@Override public String getClosestMatchingName(@NonNull final BlockState block) {
|
||||
try {
|
||||
return getMaterial(block).name();
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
public StringComparison<BlockState>.ComparisonResult getClosestBlock(String name) {
|
||||
BlockState state = BlockUtil.get(name);
|
||||
return new StringComparison<BlockState>().new ComparisonResult(1, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiomes(@NonNull final String worldName, @NonNull final CuboidRegion region,
|
||||
@NonNull final BiomeType biomeType) {
|
||||
final World world = getWorld(worldName);
|
||||
if (world == null) {
|
||||
PlotSquared.log("An error occurred setting the biome because the world was null.");
|
||||
return;
|
||||
}
|
||||
final Biome biome = BukkitAdapter.adapt(biomeType);
|
||||
for (int x = region.getMinimumPoint().getX(); x <= region.getMaximumPoint().getX(); x++) {
|
||||
for (int z = region.getMinimumPoint().getZ();
|
||||
z <= region.getMaximumPoint().getZ(); z++) {
|
||||
for (int y = 0; y < world.getMaxHeight(); y++) {
|
||||
try {
|
||||
if (biomeSetter != null) {
|
||||
biomeSetter.invoke(world, x, z, biome);
|
||||
} else {
|
||||
world.setBiome(x, y, z, biome);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
PlotSquared.log("An error occurred setting the biome:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public com.sk89q.worldedit.world.World getWeWorld(String world) {
|
||||
return new BukkitWorld(Bukkit.getWorld(world));
|
||||
}
|
||||
|
||||
@Override public void getBlock(@NonNull final Location location, final Consumer<BlockState> result) {
|
||||
ensureLoaded(location, chunk -> {
|
||||
final World world = getWorld(location.getWorld());
|
||||
final Block block = world.getBlockAt(location.getX(), location.getY(), location.getZ());
|
||||
result.accept(BukkitAdapter.asBlockType(block.getType()).getDefaultState());
|
||||
});
|
||||
}
|
||||
|
||||
@Override public BlockState getBlockSynchronous(@NonNull final Location location) {
|
||||
final World world = getWorld(location.getWorld());
|
||||
final Block block = world.getBlockAt(location.getX(), location.getY(), location.getZ());
|
||||
return BukkitAdapter.asBlockType(block.getType()).getDefaultState();
|
||||
}
|
||||
|
||||
@Override public String getMainWorld() {
|
||||
return Bukkit.getWorlds().get(0).getName();
|
||||
}
|
||||
|
||||
@Override public double getHealth(PlotPlayer player) {
|
||||
return Bukkit.getPlayer(player.getUUID()).getHealth();
|
||||
}
|
||||
|
||||
@Override public int getFoodLevel(PlotPlayer player) {
|
||||
return Bukkit.getPlayer(player.getUUID()).getFoodLevel();
|
||||
}
|
||||
|
||||
@Override public void setHealth(PlotPlayer player, double health) {
|
||||
Bukkit.getPlayer(player.getUUID()).setHealth(health);
|
||||
}
|
||||
|
||||
@Override public void setFoodLevel(PlotPlayer player, int foodLevel) {
|
||||
Bukkit.getPlayer(player.getUUID()).setFoodLevel(foodLevel);
|
||||
}
|
||||
|
||||
private static void ensureLoaded(final String world, final int x, final int z, final Consumer<Chunk> chunkConsumer) {
|
||||
PaperLib.getChunkAtAsync(getWorld(world), x >> 4, z >> 4, true).thenAccept(chunk ->
|
||||
ensureMainThread(chunkConsumer, chunk));
|
||||
}
|
||||
|
||||
private static void ensureLoaded(final Location location, final Consumer<Chunk> chunkConsumer) {
|
||||
PaperLib.getChunkAtAsync(getLocation(location), true).thenAccept(chunk ->
|
||||
ensureMainThread(chunkConsumer, chunk));
|
||||
}
|
||||
|
||||
private static <T> void ensureMainThread(final Consumer<T> consumer, final T value) {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
consumer.accept(value);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(BukkitMain.getPlugin(BukkitMain.class), () ->
|
||||
consumer.accept(value));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
714
Bukkit/src/main/java/com/plotsquared/bukkit/util/Metrics.java
Normal file
714
Bukkit/src/main/java/com/plotsquared/bukkit/util/Metrics.java
Normal file
@ -0,0 +1,714 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class Metrics {
|
||||
|
||||
static {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
|
||||
final String defaultPackage = new String(
|
||||
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
|
||||
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure nobody just copy & pastes the example and use the wrong package names
|
||||
|
||||
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private boolean enabled;
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
// The plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
// The plugin id
|
||||
private final int bstatsId;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param plugin The plugin which stats should be submitted.
|
||||
* @param bstatsId The ID of the plugin. It can be found in the url when you open the plugin on bStats.
|
||||
*/
|
||||
public Metrics(Plugin plugin, int bstatsId) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
this.bstatsId = bstatsId;
|
||||
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
"To honor their work, you should not disable it.\n" +
|
||||
"This has nearly no effect on the server performance!\n" +
|
||||
"Check out https://bStats.org/ to learn more :)"
|
||||
).copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) { }
|
||||
}
|
||||
|
||||
// Load the data
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
|
||||
if (enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
// Register our service
|
||||
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
|
||||
if (!found) {
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
if (chart == null) {
|
||||
throw new IllegalArgumentException("Chart cannot be null!");
|
||||
}
|
||||
charts.add(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
* This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JSONObject getPluginData() {
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
String pluginName = plugin.getDescription().getName();
|
||||
String pluginVersion = plugin.getDescription().getVersion();
|
||||
|
||||
data.put("pluginName", pluginName); // Append the name of the plugin
|
||||
data.put("id", bstatsId); // Append the id of the plugin
|
||||
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||
JSONArray customCharts = new JSONArray();
|
||||
for (CustomChart customChart : charts) {
|
||||
// Add the data of the custom charts
|
||||
JSONObject chart = customChart.getRequestJsonObject();
|
||||
if (chart == null) { // If the chart is null, we skip it
|
||||
continue;
|
||||
}
|
||||
customCharts.add(chart);
|
||||
}
|
||||
data.put("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JSONObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount;
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed to a collection from an array,
|
||||
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
|
||||
}
|
||||
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
String bukkitVersion = Bukkit.getVersion();
|
||||
|
||||
// OS/Java specific data
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
data.put("serverUUID", serverUUID);
|
||||
|
||||
data.put("playerAmount", playerAmount);
|
||||
data.put("onlineMode", onlineMode);
|
||||
data.put("bukkitVersion", bukkitVersion);
|
||||
|
||||
data.put("javaVersion", javaVersion);
|
||||
data.put("osName", osName);
|
||||
data.put("osArch", osArch);
|
||||
data.put("osVersion", osVersion);
|
||||
data.put("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JSONObject data = getServerData();
|
||||
|
||||
JSONArray pluginData = new JSONArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
|
||||
try {
|
||||
pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
|
||||
data.put("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(Plugin plugin, JSONObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data.toString());
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
outputStream.write(compressedData);
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
bufferedReader.close();
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom chart.
|
||||
*/
|
||||
public static abstract class CustomChart {
|
||||
|
||||
// The id of the chart
|
||||
final String chartId;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
*/
|
||||
CustomChart(String chartId) {
|
||||
if (chartId == null || chartId.isEmpty()) {
|
||||
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
private JSONObject getRequestJsonObject() {
|
||||
JSONObject chart = new JSONObject();
|
||||
chart.put("chartId", chartId);
|
||||
try {
|
||||
JSONObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
chart.put("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logFailedRequests) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected abstract JSONObject getChartData() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple pie.
|
||||
*/
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced pie.
|
||||
*/
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom drilldown pie.
|
||||
*/
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JSONObject value = new JSONObject();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
value.put(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
values.put(entryValues.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom single line chart.
|
||||
*/
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom multi line chart.
|
||||
*/
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple bar chart.
|
||||
*/
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
categoryValues.add(entry.getValue());
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced bar chart.
|
||||
*/
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
for (int categoryValue : entry.getValue()) {
|
||||
categoryValues.add(categoryValue);
|
||||
}
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.plotsquared.util.ReflectionUtils.callConstructor;
|
||||
import static com.plotsquared.util.ReflectionUtils.callMethod;
|
||||
import static com.plotsquared.util.ReflectionUtils.getCbClass;
|
||||
import static com.plotsquared.util.ReflectionUtils.getField;
|
||||
import static com.plotsquared.util.ReflectionUtils.getNmsClass;
|
||||
import static com.plotsquared.util.ReflectionUtils.getUtilClass;
|
||||
import static com.plotsquared.util.ReflectionUtils.makeConstructor;
|
||||
import static com.plotsquared.util.ReflectionUtils.makeField;
|
||||
import static com.plotsquared.util.ReflectionUtils.makeMethod;
|
||||
|
||||
public class OfflinePlayerUtil {
|
||||
|
||||
public static Player loadPlayer(OfflinePlayer player) {
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
if (player instanceof Player) {
|
||||
return (Player) player;
|
||||
}
|
||||
return loadPlayer(player.getUniqueId(), player.getName());
|
||||
}
|
||||
|
||||
private static Player loadPlayer(UUID id, String name) {
|
||||
Object server = getMinecraftServer();
|
||||
Object interactManager = newPlayerInteractManager();
|
||||
Object worldServer = getWorldServer();
|
||||
Object profile = newGameProfile(id, name);
|
||||
Class<?> entityPlayerClass = getNmsClass("EntityPlayer");
|
||||
Constructor entityPlayerConstructor =
|
||||
makeConstructor(entityPlayerClass, getNmsClass("MinecraftServer"),
|
||||
getNmsClass("WorldServer"), getUtilClass("com.mojang.authlib.GameProfile"),
|
||||
getNmsClass("PlayerInteractManager"));
|
||||
Object entityPlayer =
|
||||
callConstructor(entityPlayerConstructor, server, worldServer, profile, interactManager);
|
||||
return (Player) getBukkitEntity(entityPlayer);
|
||||
}
|
||||
|
||||
private static Object newGameProfile(UUID id, String name) {
|
||||
Class<?> gameProfileClass = getUtilClass("com.mojang.authlib.GameProfile");
|
||||
if (gameProfileClass == null) { //Before uuids
|
||||
return name;
|
||||
}
|
||||
Constructor gameProfileConstructor =
|
||||
makeConstructor(gameProfileClass, UUID.class, String.class);
|
||||
if (gameProfileConstructor == null) { //Version has string constructor
|
||||
gameProfileConstructor = makeConstructor(gameProfileClass, String.class, String.class);
|
||||
return callConstructor(gameProfileConstructor, id.toString(), name);
|
||||
} else { //Version has uuid constructor
|
||||
return callConstructor(gameProfileConstructor, id, name);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object newPlayerInteractManager() {
|
||||
Object worldServer = getWorldServer();
|
||||
Class<?> playerInteractClass = getNmsClass("PlayerInteractManager");
|
||||
Class<?> worldClass = getNmsClass("World");
|
||||
Constructor c = makeConstructor(playerInteractClass, worldClass);
|
||||
return callConstructor(c, worldServer);
|
||||
}
|
||||
|
||||
public static Object getWorldServerNew() {
|
||||
Object server = getMinecraftServer();
|
||||
Class<?> minecraftServerClass = getNmsClass("MinecraftServer");
|
||||
Class<?> dimensionManager = getNmsClass("DimensionManager");
|
||||
Object overworld = getField(makeField(dimensionManager, "OVERWORLD"), null);
|
||||
Method getWorldServer = makeMethod(minecraftServerClass, "getWorldServer", dimensionManager);
|
||||
return callMethod(getWorldServer, server, overworld);
|
||||
}
|
||||
|
||||
private static Object getWorldServer() {
|
||||
Object server = getMinecraftServer();
|
||||
Class<?> minecraftServerClass = getNmsClass("MinecraftServer");
|
||||
Method getWorldServer = makeMethod(minecraftServerClass, "getWorldServer", int.class);
|
||||
Object o;
|
||||
try {
|
||||
o = callMethod(getWorldServer, server, 0);
|
||||
} catch (final RuntimeException e) {
|
||||
o = getWorldServerNew();
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
//NMS Utils
|
||||
|
||||
private static Object getMinecraftServer() {
|
||||
return callMethod(makeMethod(getCbClass("CraftServer"), "getServer"), Bukkit.getServer());
|
||||
}
|
||||
|
||||
private static Entity getBukkitEntity(Object o) {
|
||||
Method getBukkitEntity = makeMethod(o.getClass(), "getBukkitEntity");
|
||||
return callMethod(getBukkitEntity, o);
|
||||
}
|
||||
}
|
142
Bukkit/src/main/java/com/plotsquared/bukkit/util/SendChunk.java
Normal file
142
Bukkit/src/main/java/com/plotsquared/bukkit/util/SendChunk.java
Normal file
@ -0,0 +1,142 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.player.BukkitPlayer;
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.location.Location;
|
||||
import com.plotsquared.plot.Plot;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.plotsquared.util.ReflectionUtils.RefClass;
|
||||
import com.plotsquared.util.ReflectionUtils.RefConstructor;
|
||||
import com.plotsquared.util.ReflectionUtils.RefField;
|
||||
import com.plotsquared.util.ReflectionUtils.RefMethod;
|
||||
import com.plotsquared.util.tasks.TaskManager;
|
||||
import com.plotsquared.util.uuid.UUIDHandler;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static com.plotsquared.util.ReflectionUtils.getRefClass;
|
||||
|
||||
/**
|
||||
* An utility that can be used to send chunks, rather than using bukkit code
|
||||
* to do so (uses heavy NMS).
|
||||
*/
|
||||
public class SendChunk {
|
||||
|
||||
private final RefMethod methodGetHandlePlayer;
|
||||
private final RefMethod methodGetHandleChunk;
|
||||
private final RefConstructor mapChunk;
|
||||
private final RefField connection;
|
||||
private final RefMethod send;
|
||||
private final RefMethod methodInitLighting;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public SendChunk() throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException {
|
||||
RefClass classCraftPlayer = getRefClass("{cb}.entity.CraftPlayer");
|
||||
this.methodGetHandlePlayer = classCraftPlayer.getMethod("getHandle");
|
||||
RefClass classCraftChunk = getRefClass("{cb}.CraftChunk");
|
||||
this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle");
|
||||
RefClass classChunk = getRefClass("{nms}.Chunk");
|
||||
this.methodInitLighting = classChunk.getMethod("initLighting");
|
||||
RefClass classMapChunk = getRefClass("{nms}.PacketPlayOutMapChunk");
|
||||
this.mapChunk = classMapChunk.getConstructor(classChunk.getRealClass(), int.class);
|
||||
RefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer");
|
||||
this.connection = classEntityPlayer.getField("playerConnection");
|
||||
RefClass classPacket = getRefClass("{nms}.Packet");
|
||||
RefClass classConnection = getRefClass("{nms}.PlayerConnection");
|
||||
this.send = classConnection.getMethod("sendPacket", classPacket.getRealClass());
|
||||
}
|
||||
|
||||
public void sendChunk(Collection<Chunk> input) {
|
||||
HashSet<Chunk> chunks = new HashSet<>(input);
|
||||
HashMap<String, ArrayList<Chunk>> map = new HashMap<>();
|
||||
int view = Bukkit.getServer().getViewDistance();
|
||||
for (Chunk chunk : chunks) {
|
||||
String world = chunk.getWorld().getName();
|
||||
ArrayList<Chunk> list = map.computeIfAbsent(world, k -> new ArrayList<>());
|
||||
list.add(chunk);
|
||||
Object c = this.methodGetHandleChunk.of(chunk).call();
|
||||
this.methodInitLighting.of(c).call();
|
||||
}
|
||||
for (Entry<String, PlotPlayer> entry : UUIDHandler.getPlayers().entrySet()) {
|
||||
PlotPlayer pp = entry.getValue();
|
||||
Plot plot = pp.getCurrentPlot();
|
||||
Location location = null;
|
||||
String world;
|
||||
if (plot != null) {
|
||||
world = plot.getWorldName();
|
||||
} else {
|
||||
location = pp.getLocation();
|
||||
world = location.getWorld();
|
||||
}
|
||||
ArrayList<Chunk> list = map.get(world);
|
||||
if (list == null) {
|
||||
continue;
|
||||
}
|
||||
if (location == null) {
|
||||
location = pp.getLocation();
|
||||
}
|
||||
int chunkX = location.getX() >> 4;
|
||||
int chunkZ = location.getZ() >> 4;
|
||||
Player player = ((BukkitPlayer) pp).player;
|
||||
Object entity = this.methodGetHandlePlayer.of(player).call();
|
||||
|
||||
for (Chunk chunk : list) {
|
||||
int dx = Math.abs(chunkX - chunk.getX());
|
||||
int dz = Math.abs(chunkZ - chunk.getZ());
|
||||
if ((dx > view) || (dz > view)) {
|
||||
continue;
|
||||
}
|
||||
Object c = this.methodGetHandleChunk.of(chunk).call();
|
||||
chunks.remove(chunk);
|
||||
Object con = this.connection.of(entity).get();
|
||||
Object packet = null;
|
||||
try {
|
||||
packet = this.mapChunk.create(c, 65535);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
if (packet == null) {
|
||||
PlotSquared.debug("Error with PacketPlayOutMapChunk reflection.");
|
||||
}
|
||||
this.send.of(con).call(packet);
|
||||
}
|
||||
}
|
||||
for (final Chunk chunk : chunks) {
|
||||
TaskManager.runTask(() -> {
|
||||
try {
|
||||
chunk.unload(true);
|
||||
} catch (Throwable ignored) {
|
||||
String worldName = chunk.getWorld().getName();
|
||||
PlotSquared.debug(
|
||||
"$4Could not save chunk: " + worldName + ';' + chunk.getX() + ";" + chunk
|
||||
.getZ());
|
||||
PlotSquared.debug("$3 - $4File may be open in another process (e.g. MCEdit)");
|
||||
PlotSquared.debug("$3 - $4" + worldName + "/level.dat or " + worldName
|
||||
+ "/level_old.dat may be corrupt (try repairing or removing these)");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void sendChunk(String worldName, Collection<BlockVector2> chunkLocations) {
|
||||
World myWorld = Bukkit.getWorld(worldName);
|
||||
ArrayList<Chunk> chunks = new ArrayList<>();
|
||||
for (BlockVector2 loc : chunkLocations) {
|
||||
if (myWorld.isChunkLoaded(loc.getX(), loc.getZ())) {
|
||||
PaperLib.getChunkAtAsync(myWorld, loc.getX(), loc.getZ()).thenAccept(chunks::add);
|
||||
}
|
||||
}
|
||||
sendChunk(chunks);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.bukkit.generator.BukkitAugmentedGenerator;
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.generator.GeneratorWrapper;
|
||||
import com.plotsquared.util.SetupUtils;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class SetGenCB {
|
||||
|
||||
public static void setGenerator(World world) throws Exception {
|
||||
SetupUtils.manager.updateGenerators();
|
||||
PlotSquared.get().removePlotAreas(world.getName());
|
||||
ChunkGenerator gen = world.getGenerator();
|
||||
if (gen == null) {
|
||||
return;
|
||||
}
|
||||
String name = gen.getClass().getCanonicalName();
|
||||
boolean set = false;
|
||||
for (GeneratorWrapper<?> wrapper : SetupUtils.generators.values()) {
|
||||
ChunkGenerator newGen = (ChunkGenerator) wrapper.getPlatformGenerator();
|
||||
if (newGen == null) {
|
||||
newGen = (ChunkGenerator) wrapper;
|
||||
}
|
||||
if (newGen.getClass().getCanonicalName().equals(name)) {
|
||||
// set generator
|
||||
Field generator = world.getClass().getDeclaredField("generator");
|
||||
Field populators = world.getClass().getDeclaredField("populators");
|
||||
generator.setAccessible(true);
|
||||
populators.setAccessible(true);
|
||||
// Set populators (just in case)
|
||||
populators.set(world, new ArrayList<>());
|
||||
// Set generator
|
||||
generator.set(world, newGen);
|
||||
populators.set(world, newGen.getDefaultPopulators(world));
|
||||
// end
|
||||
set = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!set) {
|
||||
world.getPopulators()
|
||||
.removeIf(blockPopulator -> blockPopulator instanceof BukkitAugmentedGenerator);
|
||||
}
|
||||
PlotSquared.get()
|
||||
.loadWorld(world.getName(), PlotSquared.get().IMP.getGenerator(world.getName(), null));
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.plotsquared.bukkit.util;
|
||||
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.config.Captions;
|
||||
import com.plotsquared.config.Settings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
|
||||
public class UpdateUtility implements Listener {
|
||||
|
||||
public static String internalVersion;
|
||||
public static String spigotVersion;
|
||||
public final JavaPlugin javaPlugin;
|
||||
|
||||
public UpdateUtility(final JavaPlugin javaPlugin) {
|
||||
this.javaPlugin = javaPlugin;
|
||||
internalVersion = javaPlugin.getDescription().getVersion();
|
||||
}
|
||||
|
||||
public void updateChecker() {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(UpdateUtility.this.javaPlugin, () -> {
|
||||
if (Settings.Enabled_Components.UPDATE_NOTIFICATIONS) {
|
||||
try {
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://api.spigotmc.org/legacy/update.php?resource=1177").openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
spigotVersion = (new BufferedReader(new InputStreamReader(connection.getInputStream()))).readLine();
|
||||
} catch (IOException e) {
|
||||
PlotSquared.log(
|
||||
Captions.PREFIX + "&cUnable to check for updates because: " + e);
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!internalVersion.equals(spigotVersion)) {
|
||||
PlotSquared.log(Captions.PREFIX + "&6There appears to be a PlotSquared update available!");
|
||||
PlotSquared.log(Captions.PREFIX + "&6You are running version " + internalVersion + ", &6latest version is " + spigotVersion);
|
||||
PlotSquared.log(Captions.PREFIX + "&6https://www.spigotmc.org/resources/1177/updates");
|
||||
} else {
|
||||
PlotSquared.log(Captions.PREFIX + "Congratulations! You are running the latest PlotSquared version.");
|
||||
}
|
||||
}
|
||||
this.cancel();
|
||||
});
|
||||
}
|
||||
}.runTaskTimer(this.javaPlugin, 0L, 12000L);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.plotsquared.bukkit.util.block;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BukkitBlockUtil {
|
||||
public static Supplier<ItemType> supplyItem(Block block) {
|
||||
return new Supplier<ItemType>() {
|
||||
@Override public ItemType get() {
|
||||
return BukkitAdapter.asItemType(block.getType());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Supplier<ItemType> supplyItem(Material type) {
|
||||
return () -> BukkitAdapter.asItemType(type);
|
||||
}
|
||||
|
||||
public static BlockState get(Block block) {
|
||||
return get(block.getType());
|
||||
}
|
||||
|
||||
public static BlockState get(Material material) {
|
||||
return BukkitAdapter.asBlockType(material).getDefaultState();
|
||||
}
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
package com.plotsquared.bukkit.util.block;
|
||||
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.util.MainUtil;
|
||||
import com.plotsquared.queue.BasicLocalBlockQueue;
|
||||
import com.plotsquared.util.BlockUtil;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import lombok.NonNull;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BukkitLocalQueue extends BasicLocalBlockQueue {
|
||||
|
||||
public BukkitLocalQueue(String world) {
|
||||
super(world);
|
||||
}
|
||||
|
||||
@Override public LocalChunk getLocalChunk(int x, int z) {
|
||||
return new BasicLocalChunk(this, x, z) {
|
||||
// Custom stuff?
|
||||
};
|
||||
}
|
||||
|
||||
@Override public void optimize() {
|
||||
|
||||
}
|
||||
|
||||
@Override public BlockState getBlock(int x, int y, int z) {
|
||||
World worldObj = Bukkit.getWorld(getWorld());
|
||||
if (worldObj != null) {
|
||||
Block block = worldObj.getBlockAt(x, y, z);
|
||||
return BukkitBlockUtil.get(block);
|
||||
} else {
|
||||
return BlockUtil.get(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void refreshChunk(int x, int z) {
|
||||
World worldObj = Bukkit.getWorld(getWorld());
|
||||
if (worldObj != null) {
|
||||
worldObj.refreshChunk(x, z);
|
||||
} else {
|
||||
PlotSquared.debug("Error Refreshing Chunk");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void fixChunkLighting(int x, int z) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override public final void regenChunk(int x, int z) {
|
||||
World worldObj = Bukkit.getWorld(getWorld());
|
||||
if (worldObj != null) {
|
||||
try {
|
||||
worldObj.regenerateChunk(x, z);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
com.sk89q.worldedit.world.World world = BukkitAdapter.adapt(worldObj);
|
||||
try (EditSession editSession = WorldEdit.getInstance().getEditSessionFactory()
|
||||
.getEditSession(world, -1);) {
|
||||
CuboidRegion region =
|
||||
new CuboidRegion(world, BlockVector3.at((x << 4), 0, (z << 4)),
|
||||
BlockVector3.at((x << 4) + 15, 255, (z << 4) + 15));
|
||||
world.regenerate(region, editSession);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PlotSquared.debug("Error Regenerating Chunk");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public final void setComponents(LocalChunk lc)
|
||||
throws ExecutionException, InterruptedException {
|
||||
setBaseBlocks(lc);
|
||||
if (setBiome() && lc.biomes != null) {
|
||||
setBiomes(lc);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBaseBlocks(LocalChunk localChunk) {
|
||||
World worldObj = Bukkit.getWorld(getWorld());
|
||||
if (worldObj == null) {
|
||||
throw new NullPointerException("World cannot be null.");
|
||||
}
|
||||
final Consumer<Chunk> chunkConsumer = chunk -> {
|
||||
for (int layer = 0; layer < localChunk.baseblocks.length; layer++) {
|
||||
BaseBlock[] blocksLayer = localChunk.baseblocks[layer];
|
||||
if (blocksLayer != null) {
|
||||
for (int j = 0; j < blocksLayer.length; j++) {
|
||||
if (blocksLayer[j] != null) {
|
||||
BaseBlock block = blocksLayer[j];
|
||||
int x = MainUtil.x_loc[layer][j];
|
||||
int y = MainUtil.y_loc[layer][j];
|
||||
int z = MainUtil.z_loc[layer][j];
|
||||
|
||||
BlockData blockData = BukkitAdapter.adapt(block);
|
||||
|
||||
Block existing = chunk.getBlock(x, y, z);
|
||||
final BlockState existingBaseBlock = BukkitAdapter.adapt(existing.getBlockData());
|
||||
if (BukkitBlockUtil.get(existing).equals(existingBaseBlock) && existing
|
||||
.getBlockData().matches(blockData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
existing.setType(BukkitAdapter.adapt(block.getBlockType()), false);
|
||||
existing.setBlockData(blockData, false);
|
||||
if (block.hasNbtData()) {
|
||||
CompoundTag tag = block.getNbtData();
|
||||
StateWrapper sw = new StateWrapper(tag);
|
||||
|
||||
sw.restoreTag(worldObj.getName(), existing.getX(),
|
||||
existing.getY(), existing.getZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (isForceSync()) {
|
||||
chunkConsumer.accept(getChunk(worldObj, localChunk));
|
||||
} else {
|
||||
PaperLib.getChunkAtAsync(worldObj, localChunk.getX(), localChunk.getZ(), true).thenAccept(chunkConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
private Chunk getChunk(final World world, final LocalChunk localChunk) {
|
||||
Chunk chunk = null;
|
||||
if (this.getChunkObject() != null && this.getChunkObject() instanceof Chunk) {
|
||||
chunk = (Chunk) this.getChunkObject();
|
||||
}
|
||||
if (chunk == null) {
|
||||
chunk = world.getChunkAt(localChunk.getX(), localChunk.getZ());
|
||||
}
|
||||
if (!chunk.isLoaded()) {
|
||||
chunk.load(true);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private void setMaterial(@NonNull final BlockState plotBlock, @NonNull final Block block) {
|
||||
Material material = BukkitAdapter.adapt(plotBlock.getBlockType());
|
||||
block.setType(material, false);
|
||||
}
|
||||
|
||||
private boolean equals(@NonNull final BlockState plotBlock, @NonNull final Block block) {
|
||||
return plotBlock.equals(BukkitBlockUtil.get(block));
|
||||
}
|
||||
|
||||
public void setBiomes(LocalChunk lc) {
|
||||
World worldObj = Bukkit.getWorld(getWorld());
|
||||
if (worldObj == null) {
|
||||
throw new NullPointerException("World cannot be null.");
|
||||
}
|
||||
if (lc.biomes == null) {
|
||||
throw new NullPointerException("Biomes cannot be null.");
|
||||
}
|
||||
final Consumer<Chunk> chunkConsumer = chunk -> {
|
||||
for (int x = 0; x < lc.biomes.length; x++) {
|
||||
BiomeType[] biomeZ = lc.biomes[x];
|
||||
if (biomeZ != null) {
|
||||
for (int z = 0; z < biomeZ.length; z++) {
|
||||
if (biomeZ[z] != null) {
|
||||
BiomeType biomeType = biomeZ[z];
|
||||
|
||||
Biome biome = BukkitAdapter.adapt(biomeType);
|
||||
worldObj.setBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z,
|
||||
biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (this.isForceSync()) {
|
||||
chunkConsumer.accept(getChunk(worldObj, lc));
|
||||
} else {
|
||||
PaperLib.getChunkAtAsync(worldObj, lc.getX(), lc.getZ(), true).thenAccept(chunkConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
package com.plotsquared.bukkit.util.block;
|
||||
|
||||
import com.plotsquared.bukkit.util.BukkitUtil;
|
||||
import com.plotsquared.location.ChunkWrapper;
|
||||
import com.plotsquared.location.Location;
|
||||
import com.plotsquared.util.MainUtil;
|
||||
import com.plotsquared.queue.ScopedLocalBlockQueue;
|
||||
import com.plotsquared.util.PatternUtil;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
|
||||
import org.bukkit.generator.ChunkGenerator.ChunkData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class GenChunk extends ScopedLocalBlockQueue {
|
||||
|
||||
public final Biome[] biomes;
|
||||
public BlockState[][] result;
|
||||
public BiomeGrid biomeGrid;
|
||||
public Chunk chunk;
|
||||
public String world;
|
||||
public int chunkX;
|
||||
public int chunkZ;
|
||||
@Getter @Setter private ChunkData chunkData = null;
|
||||
|
||||
public GenChunk() {
|
||||
super(null, new Location(null, 0, 0, 0), new Location(null, 15, 255, 15));
|
||||
this.biomes = Biome.values();
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
if (chunk == null) {
|
||||
World worldObj = BukkitUtil.getWorld(world);
|
||||
if (worldObj != null) {
|
||||
this.chunk = worldObj.getChunkAt(chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public void setChunk(Chunk chunk) {
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
public void setChunk(ChunkWrapper wrap) {
|
||||
chunk = null;
|
||||
world = wrap.world;
|
||||
chunkX = wrap.x;
|
||||
chunkZ = wrap.z;
|
||||
}
|
||||
|
||||
@Override public void fillBiome(BiomeType biomeType) {
|
||||
if (biomeGrid == null) {
|
||||
return;
|
||||
}
|
||||
Biome biome = BukkitAdapter.adapt(biomeType);
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
this.biomeGrid.setBiome(x, z, biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setCuboid(Location pos1, Location pos2, BlockState block) {
|
||||
if (result != null && pos1.getX() == 0 && pos1.getZ() == 0 && pos2.getX() == 15
|
||||
&& pos2.getZ() == 15) {
|
||||
for (int y = pos1.getY(); y <= pos2.getY(); y++) {
|
||||
int layer = y >> 4;
|
||||
BlockState[] data = result[layer];
|
||||
if (data == null) {
|
||||
result[layer] = data = new BlockState[4096];
|
||||
}
|
||||
int start = y << 8;
|
||||
int end = start + 256;
|
||||
Arrays.fill(data, start, end, block);
|
||||
}
|
||||
}
|
||||
int minX = Math.min(pos1.getX(), pos2.getX());
|
||||
int minY = Math.min(pos1.getY(), pos2.getY());
|
||||
int minZ = Math.min(pos1.getZ(), pos2.getZ());
|
||||
int maxX = Math.max(pos1.getX(), pos2.getX());
|
||||
int maxY = Math.max(pos1.getY(), pos2.getY());
|
||||
int maxZ = Math.max(pos1.getZ(), pos2.getZ());
|
||||
chunkData.setRegion(minX, minY, minZ, maxX + 1, maxY + 1, maxZ + 1, BukkitAdapter.adapt(block));
|
||||
}
|
||||
|
||||
@Override public boolean setBiome(int x, int z, BiomeType biomeType) {
|
||||
return setBiome(x, z, BukkitAdapter.adapt(biomeType));
|
||||
}
|
||||
|
||||
public boolean setBiome(int x, int z, Biome biome) {
|
||||
if (this.biomeGrid != null) {
|
||||
this.biomeGrid.setBiome(x, z, biome);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, @NotNull Pattern pattern) {
|
||||
return setBlock(x, y, z, PatternUtil.apply(Preconditions.checkNotNull(pattern, "Pattern may not be null"), x, y, z));
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, BlockState id) {
|
||||
if (this.result == null) {
|
||||
this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id));
|
||||
return true;
|
||||
}
|
||||
this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id));
|
||||
this.storeCache(x, y, z, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void storeCache(final int x, final int y, final int z, final BlockState id) {
|
||||
int i = MainUtil.CACHE_I[y][x][z];
|
||||
BlockState[] v = this.result[i];
|
||||
if (v == null) {
|
||||
this.result[i] = v = new BlockState[4096];
|
||||
}
|
||||
int j = MainUtil.CACHE_J[y][x][z];
|
||||
v[j] = id;
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, BaseBlock id) {
|
||||
if (this.result == null) {
|
||||
this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id));
|
||||
return true;
|
||||
}
|
||||
this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id));
|
||||
this.storeCache(x, y, z, id.toImmutableState());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public BlockState getBlock(int x, int y, int z) {
|
||||
int i = MainUtil.CACHE_I[y][x][z];
|
||||
if (result == null) {
|
||||
return BukkitBlockUtil.get(chunkData.getType(x, y, z));
|
||||
}
|
||||
BlockState[] array = result[i];
|
||||
if (array == null) {
|
||||
return BlockTypes.AIR.getDefaultState();
|
||||
}
|
||||
int j = MainUtil.CACHE_J[y][x][z];
|
||||
return array[j];
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return chunk == null ? chunkX : chunk.getX();
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return chunk == null ? chunkZ : chunk.getZ();
|
||||
}
|
||||
|
||||
@Override public String getWorld() {
|
||||
return chunk == null ? world : chunk.getWorld().getName();
|
||||
}
|
||||
|
||||
@Override public Location getMax() {
|
||||
return new Location(getWorld(), 15 + (getX() << 4), 255, 15 + (getZ() << 4));
|
||||
}
|
||||
|
||||
@Override public Location getMin() {
|
||||
return new Location(getWorld(), getX() << 4, 0, getZ() << 4);
|
||||
}
|
||||
|
||||
public GenChunk clone() {
|
||||
GenChunk toReturn = new GenChunk();
|
||||
if (this.result != null) {
|
||||
for (int i = 0; i < this.result.length; i++) {
|
||||
BlockState[] matrix = this.result[i];
|
||||
if (matrix != null) {
|
||||
toReturn.result[i] = new BlockState[matrix.length];
|
||||
System.arraycopy(matrix, 0, toReturn.result[i], 0, matrix.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
toReturn.chunkData = this.chunkData;
|
||||
return toReturn;
|
||||
}
|
||||
}
|
@ -0,0 +1,265 @@
|
||||
package com.plotsquared.bukkit.util.block;
|
||||
|
||||
import com.plotsquared.bukkit.util.BukkitUtil;
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.config.Captions;
|
||||
import com.sk89q.jnbt.ByteTag;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.ShortTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.Container;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class StateWrapper {
|
||||
|
||||
public org.bukkit.block.BlockState state = null;
|
||||
public CompoundTag tag = null;
|
||||
|
||||
public StateWrapper(org.bukkit.block.BlockState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public StateWrapper(CompoundTag tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public static String jsonToColourCode(String str) {
|
||||
str = str.replace("{\"extra\":", "").replace("],\"text\":\"\"}", "]")
|
||||
.replace("[{\"color\":\"black\",\"text\":\"", "&0")
|
||||
.replace("[{\"color\":\"dark_blue\",\"text\":\"", "&1")
|
||||
.replace("[{\"color\":\"dark_green\",\"text\":\"", "&2")
|
||||
.replace("[{\"color\":\"dark_aqua\",\"text\":\"", "&3")
|
||||
.replace("[{\"color\":\"dark_red\",\"text\":\"", "&4")
|
||||
.replace("[{\"color\":\"dark_purple\",\"text\":\"", "&5")
|
||||
.replace("[{\"color\":\"gold\",\"text\":\"", "&6")
|
||||
.replace("[{\"color\":\"gray\",\"text\":\"", "&7")
|
||||
.replace("[{\"color\":\"dark_gray\",\"text\":\"", "&8")
|
||||
.replace("[{\"color\":\"blue\",\"text\":\"", "&9")
|
||||
.replace("[{\"color\":\"green\",\"text\":\"", "&a")
|
||||
.replace("[{\"color\":\"aqua\",\"text\":\"", "&b")
|
||||
.replace("[{\"color\":\"red\",\"text\":\"", "&c")
|
||||
.replace("[{\"color\":\"light_purple\",\"text\":\"", "&d")
|
||||
.replace("[{\"color\":\"yellow\",\"text\":\"", "&e")
|
||||
.replace("[{\"color\":\"white\",\"text\":\"", "&f")
|
||||
.replace("[{\"obfuscated\":true,\"text\":\"", "&k")
|
||||
.replace("[{\"bold\":true,\"text\":\"", "&l")
|
||||
.replace("[{\"strikethrough\":true,\"text\":\"", "&m")
|
||||
.replace("[{\"underlined\":true,\"text\":\"", "&n")
|
||||
.replace("[{\"italic\":true,\"text\":\"", "&o").replace("[{\"color\":\"black\",", "&0")
|
||||
.replace("[{\"color\":\"dark_blue\",", "&1")
|
||||
.replace("[{\"color\":\"dark_green\",", "&2")
|
||||
.replace("[{\"color\":\"dark_aqua\",", "&3").replace("[{\"color\":\"dark_red\",", "&4")
|
||||
.replace("[{\"color\":\"dark_purple\",", "&5").replace("[{\"color\":\"gold\",", "&6")
|
||||
.replace("[{\"color\":\"gray\",", "&7").replace("[{\"color\":\"dark_gray\",", "&8")
|
||||
.replace("[{\"color\":\"blue\",", "&9").replace("[{\"color\":\"green\",", "&a")
|
||||
.replace("[{\"color\":\"aqua\",", "&b").replace("[{\"color\":\"red\",", "&c")
|
||||
.replace("[{\"color\":\"light_purple\",", "&d").replace("[{\"color\":\"yellow\",", "&e")
|
||||
.replace("[{\"color\":\"white\",", "&f").replace("[{\"obfuscated\":true,", "&k")
|
||||
.replace("[{\"bold\":true,", "&l").replace("[{\"strikethrough\":true,", "&m")
|
||||
.replace("[{\"underlined\":true,", "&n").replace("[{\"italic\":true,", "&o")
|
||||
.replace("{\"color\":\"black\",\"text\":\"", "&0")
|
||||
.replace("{\"color\":\"dark_blue\",\"text\":\"", "&1")
|
||||
.replace("{\"color\":\"dark_green\",\"text\":\"", "&2")
|
||||
.replace("{\"color\":\"dark_aqua\",\"text\":\"", "&3")
|
||||
.replace("{\"color\":\"dark_red\",\"text\":\"", "&4")
|
||||
.replace("{\"color\":\"dark_purple\",\"text\":\"", "&5")
|
||||
.replace("{\"color\":\"gold\",\"text\":\"", "&6")
|
||||
.replace("{\"color\":\"gray\",\"text\":\"", "&7")
|
||||
.replace("{\"color\":\"dark_gray\",\"text\":\"", "&8")
|
||||
.replace("{\"color\":\"blue\",\"text\":\"", "&9")
|
||||
.replace("{\"color\":\"green\",\"text\":\"", "&a")
|
||||
.replace("{\"color\":\"aqua\",\"text\":\"", "&b")
|
||||
.replace("{\"color\":\"red\",\"text\":\"", "&c")
|
||||
.replace("{\"color\":\"light_purple\",\"text\":\"", "&d")
|
||||
.replace("{\"color\":\"yellow\",\"text\":\"", "&e")
|
||||
.replace("{\"color\":\"white\",\"text\":\"", "&f")
|
||||
.replace("{\"obfuscated\":true,\"text\":\"", "&k")
|
||||
.replace("{\"bold\":true,\"text\":\"", "&l")
|
||||
.replace("{\"strikethrough\":true,\"text\":\"", "&m")
|
||||
.replace("{\"underlined\":true,\"text\":\"", "&n")
|
||||
.replace("{\"italic\":true,\"text\":\"", "&o").replace("{\"color\":\"black\",", "&0")
|
||||
.replace("{\"color\":\"dark_blue\",", "&1").replace("{\"color\":\"dark_green\",", "&2")
|
||||
.replace("{\"color\":\"dark_aqua\",", "&3").replace("{\"color\":\"dark_red\",", "&4")
|
||||
.replace("{\"color\":\"dark_purple\",", "&5").replace("{\"color\":\"gold\",", "&6")
|
||||
.replace("{\"color\":\"gray\",", "&7").replace("{\"color\":\"dark_gray\",", "&8")
|
||||
.replace("{\"color\":\"blue\",", "&9").replace("{\"color\":\"green\",", "&a")
|
||||
.replace("{\"color\":\"aqua\",", "&b").replace("{\"color\":\"red\",", "&c")
|
||||
.replace("{\"color\":\"light_purple\",", "&d").replace("{\"color\":\"yellow\",", "&e")
|
||||
.replace("{\"color\":\"white\",", "&f").replace("{\"obfuscated\":true,", "&k")
|
||||
.replace("{\"bold\":true,", "&l").replace("{\"strikethrough\":true,", "&m")
|
||||
.replace("{\"underlined\":true,", "&n").replace("{\"italic\":true,", "&o")
|
||||
.replace("\"color\":\"black\",\"text\":\"", "&0")
|
||||
.replace("\"color\":\"dark_blue\",\"text\":\"", "&1")
|
||||
.replace("\"color\":\"dark_green\",\"text\":\"", "&2")
|
||||
.replace("\"color\":\"dark_aqua\",\"text\":\"", "&3")
|
||||
.replace("\"color\":\"dark_red\",\"text\":\"", "&4")
|
||||
.replace("\"color\":\"dark_purple\",\"text\":\"", "&5")
|
||||
.replace("\"color\":\"gold\",\"text\":\"", "&6")
|
||||
.replace("\"color\":\"gray\",\"text\":\"", "&7")
|
||||
.replace("\"color\":\"dark_gray\",\"text\":\"", "&8")
|
||||
.replace("\"color\":\"blue\",\"text\":\"", "&9")
|
||||
.replace("\"color\":\"green\",\"text\":\"", "&a")
|
||||
.replace("\"color\":\"aqua\",\"text\":\"", "&b")
|
||||
.replace("\"color\":\"red\",\"text\":\"", "&c")
|
||||
.replace("\"color\":\"light_purple\",\"text\":\"", "&d")
|
||||
.replace("\"color\":\"yellow\",\"text\":\"", "&e")
|
||||
.replace("\"color\":\"white\",\"text\":\"", "&f")
|
||||
.replace("\"obfuscated\":true,\"text\":\"", "&k")
|
||||
.replace("\"bold\":true,\"text\":\"", "&l")
|
||||
.replace("\"strikethrough\":true,\"text\":\"", "&m")
|
||||
.replace("\"underlined\":true,\"text\":\"", "&n")
|
||||
.replace("\"italic\":true,\"text\":\"", "&o").replace("\"color\":\"black\",", "&0")
|
||||
.replace("\"color\":\"dark_blue\",", "&1").replace("\"color\":\"dark_green\",", "&2")
|
||||
.replace("\"color\":\"dark_aqua\",", "&3").replace("\"color\":\"dark_red\",", "&4")
|
||||
.replace("\"color\":\"dark_purple\",", "&5").replace("\"color\":\"gold\",", "&6")
|
||||
.replace("\"color\":\"gray\",", "&7").replace("\"color\":\"dark_gray\",", "&8")
|
||||
.replace("\"color\":\"blue\",", "&9").replace("\"color\":\"green\",", "&a")
|
||||
.replace("\"color\":\"aqua\",", "&b").replace("\"color\":\"red\",", "&c")
|
||||
.replace("\"color\":\"light_purple\",", "&d").replace("\"color\":\"yellow\",", "&e")
|
||||
.replace("\"color\":\"white\",", "&f").replace("\"obfuscated\":true,", "&k")
|
||||
.replace("\"bold\":true,", "&l").replace("\"strikethrough\":true,", "&m")
|
||||
.replace("\"underlined\":true,", "&n").replace("\"italic\":true,", "&o")
|
||||
.replace("[{\"text\":\"", "&0").replace("{\"text\":\"", "&0").replace("\"},", "")
|
||||
.replace("\"}]", "").replace("\"}", "");
|
||||
for (Entry<String, String> entry : Captions.replacements.entrySet()) {
|
||||
str = str.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public boolean restoreTag(String worldName, int x, int y, int z) {
|
||||
if (this.tag == null) {
|
||||
return false;
|
||||
}
|
||||
String tileid = this.tag.getString("id").toLowerCase();
|
||||
if (tileid.startsWith("minecraft:")) {
|
||||
tileid = tileid.replace("minecraft:", "");
|
||||
}
|
||||
World world = BukkitUtil.getWorld(worldName);
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
if (block == null) {
|
||||
return false;
|
||||
}
|
||||
org.bukkit.block.BlockState state = block.getState();
|
||||
switch (tileid) {
|
||||
case "chest":
|
||||
case "beacon":
|
||||
case "brewingstand":
|
||||
case "dispenser":
|
||||
case "dropper":
|
||||
case "furnace":
|
||||
case "hopper":
|
||||
case "shulkerbox":
|
||||
List<Tag> itemsTag = this.tag.getListTag("Items").getValue();
|
||||
int length = itemsTag.size();
|
||||
String[] ids = new String[length];
|
||||
byte[] amounts = new byte[length];
|
||||
byte[] slots = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
Tag itemTag = itemsTag.get(i);
|
||||
CompoundTag itemComp = (CompoundTag) itemTag;
|
||||
String id = itemComp.getString("id");
|
||||
if (id.startsWith("minecraft:")) {
|
||||
id = id.replace("minecraft:", "");
|
||||
}
|
||||
ids[i] = id;
|
||||
amounts[i] = itemComp.getByte("Count");
|
||||
slots[i] = itemComp.getByte("Slot");
|
||||
}
|
||||
if (state instanceof Container) {
|
||||
Container container = (Container) state;
|
||||
Inventory inv = container.getSnapshotInventory();
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
Material mat = Material.getMaterial(ids[i].toUpperCase());
|
||||
if (mat != null) {
|
||||
ItemStack item = new ItemStack(mat, (int) amounts[i]);
|
||||
inv.setItem(slots[i], item);
|
||||
PlotSquared.log(mat.name() + " " + slots[i]);
|
||||
}
|
||||
}
|
||||
PlotSquared.log(inv.getStorageContents());
|
||||
container.update(true, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case "sign":
|
||||
if (state instanceof Sign) {
|
||||
Sign sign = (Sign) state;
|
||||
sign.setLine(0, jsonToColourCode(tag.getString("Text1")));
|
||||
sign.setLine(1, jsonToColourCode(tag.getString("Text2")));
|
||||
sign.setLine(2, jsonToColourCode(tag.getString("Text3")));
|
||||
sign.setLine(3, jsonToColourCode(tag.getString("Text4")));
|
||||
state.update(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public CompoundTag getTag() {
|
||||
if (this.tag != null) {
|
||||
return this.tag;
|
||||
}
|
||||
if (this.state instanceof InventoryHolder) {
|
||||
InventoryHolder inv = (InventoryHolder) this.state;
|
||||
ItemStack[] contents = inv.getInventory().getContents();
|
||||
Map<String, Tag> values = new HashMap<>();
|
||||
values.put("Items", new ListTag(CompoundTag.class, serializeInventory(contents)));
|
||||
return new CompoundTag(values);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return "Chest";
|
||||
}
|
||||
|
||||
public List<CompoundTag> serializeInventory(ItemStack[] items) {
|
||||
List<CompoundTag> tags = new ArrayList<>();
|
||||
for (int i = 0; i < items.length; ++i) {
|
||||
if (items[i] != null) {
|
||||
Map<String, Tag> tagData = serializeItem(items[i]);
|
||||
tagData.put("Slot", new ByteTag((byte) i));
|
||||
tags.add(new CompoundTag(tagData));
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
public Map<String, Tag> serializeItem(ItemStack item) {
|
||||
Map<String, Tag> data = new HashMap<>();
|
||||
data.put("id", new StringTag(item.getType().name()));
|
||||
data.put("Damage", new ShortTag(item.getDurability()));
|
||||
data.put("Count", new ByteTag((byte) item.getAmount()));
|
||||
if (!item.getEnchantments().isEmpty()) {
|
||||
List<CompoundTag> enchantmentList = new ArrayList<>();
|
||||
for (Entry<Enchantment, Integer> entry : item.getEnchantments().entrySet()) {
|
||||
Map<String, Tag> enchantment = new HashMap<>();
|
||||
enchantment.put("id", new StringTag(entry.getKey().toString()));
|
||||
enchantment.put("lvl", new ShortTag(entry.getValue().shortValue()));
|
||||
enchantmentList.add(new CompoundTag(enchantment));
|
||||
}
|
||||
Map<String, Tag> auxData = new HashMap<>();
|
||||
auxData.put("ench", new ListTag(CompoundTag.class, enchantmentList));
|
||||
data.put("tag", new CompoundTag(auxData));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.plotsquared.bukkit.util.uuid;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
|
||||
public class DatFileFilter implements FilenameFilter {
|
||||
|
||||
@Override public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".dat");
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.plotsquared.bukkit.util.uuid;
|
||||
|
||||
import com.plotsquared.bukkit.player.BukkitOfflinePlayer;
|
||||
import com.plotsquared.bukkit.player.BukkitPlayer;
|
||||
import com.plotsquared.player.OfflinePlotPlayer;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.plotsquared.util.uuid.UUIDWrapper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DefaultUUIDWrapper extends UUIDWrapper {
|
||||
|
||||
@NotNull @Override public UUID getUUID(PlotPlayer player) {
|
||||
return ((BukkitPlayer) player).player.getUniqueId();
|
||||
}
|
||||
|
||||
@Override public UUID getUUID(OfflinePlotPlayer player) {
|
||||
return player.getUUID();
|
||||
}
|
||||
|
||||
@Override public OfflinePlotPlayer getOfflinePlayer(UUID uuid) {
|
||||
return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(uuid));
|
||||
}
|
||||
|
||||
@Override public UUID getUUID(String name) {
|
||||
return Bukkit.getOfflinePlayer(name).getUniqueId();
|
||||
}
|
||||
|
||||
@Override public OfflinePlotPlayer[] getOfflinePlayers() {
|
||||
OfflinePlayer[] ops = Bukkit.getOfflinePlayers();
|
||||
return Arrays.stream(ops).map(BukkitOfflinePlayer::new).toArray(BukkitOfflinePlayer[]::new);
|
||||
}
|
||||
|
||||
@Override public OfflinePlotPlayer getOfflinePlayer(String name) {
|
||||
return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(name));
|
||||
}
|
||||
}
|
@ -0,0 +1,264 @@
|
||||
package com.plotsquared.bukkit.util.uuid;
|
||||
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.config.Captions;
|
||||
import com.plotsquared.config.Settings;
|
||||
import com.plotsquared.player.OfflinePlotPlayer;
|
||||
import com.plotsquared.util.tasks.RunnableVal;
|
||||
import com.plotsquared.util.StringWrapper;
|
||||
import com.plotsquared.util.StringMan;
|
||||
import com.plotsquared.util.tasks.TaskManager;
|
||||
import com.plotsquared.util.uuid.UUIDHandler;
|
||||
import com.plotsquared.util.uuid.UUIDHandlerImplementation;
|
||||
import com.plotsquared.plot.expiration.ExpireManager;
|
||||
import com.plotsquared.util.uuid.UUIDWrapper;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FileUUIDHandler extends UUIDHandlerImplementation {
|
||||
|
||||
public FileUUIDHandler(UUIDWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
@Override public boolean startCaching(Runnable whenDone) {
|
||||
return super.startCaching(whenDone) && cache(whenDone);
|
||||
}
|
||||
|
||||
private Tag readTag(File file) throws IOException {
|
||||
// Don't chain the creation of the GZIP stream and the NBT stream, because their
|
||||
// constructors may throw an IOException.
|
||||
try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
|
||||
NBTInputStream nbtInputStream = new NBTInputStream(gzipInputStream)) {
|
||||
return nbtInputStream.readNamedTag().getTag();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean cache(final Runnable whenDone) {
|
||||
final File container = Bukkit.getWorldContainer();
|
||||
List<World> worlds = Bukkit.getWorlds();
|
||||
final String world;
|
||||
if (worlds.isEmpty()) {
|
||||
world = "world";
|
||||
} else {
|
||||
world = worlds.get(0).getName();
|
||||
}
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
PlotSquared.debug(Captions.PREFIX + "Starting player data caching for: " + world);
|
||||
File uuidFile = new File(PlotSquared.get().IMP.getDirectory(), "uuids.txt");
|
||||
if (uuidFile.exists()) {
|
||||
try {
|
||||
List<String> lines =
|
||||
Files.readAllLines(uuidFile.toPath(), StandardCharsets.UTF_8);
|
||||
for (String line : lines) {
|
||||
try {
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
line = line.replaceAll("[\\|][0-9]+[\\|][0-9]+[\\|]", "");
|
||||
String[] split = line.split("\\|");
|
||||
String name = split[0];
|
||||
if (name.isEmpty() || (name.length() > 16) || !StringMan
|
||||
.isAlphanumericUnd(name)) {
|
||||
continue;
|
||||
}
|
||||
UUID uuid = FileUUIDHandler.this.uuidWrapper.getUUID(name);
|
||||
if (uuid == null) {
|
||||
continue;
|
||||
}
|
||||
UUIDHandler.add(new StringWrapper(name), uuid);
|
||||
} catch (Exception e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
HashBiMap<StringWrapper, UUID> toAdd = HashBiMap.create(new HashMap<>());
|
||||
if (Settings.UUID.NATIVE_UUID_PROVIDER) {
|
||||
HashSet<UUID> all = UUIDHandler.getAllUUIDS();
|
||||
PlotSquared.debug("Fast mode UUID caching enabled!");
|
||||
File playerDataFolder = new File(container, world + File.separator + "playerdata");
|
||||
String[] dat = playerDataFolder.list(new DatFileFilter());
|
||||
boolean check = all.isEmpty();
|
||||
if (dat != null) {
|
||||
for (String current : dat) {
|
||||
String s = current.replaceAll(".dat$", "");
|
||||
try {
|
||||
UUID uuid = UUID.fromString(s);
|
||||
if (check || all.remove(uuid)) {
|
||||
File file = new File(playerDataFolder, current);
|
||||
CompoundTag compound = (CompoundTag) readTag(file);
|
||||
if (!compound.containsKey("bukkit")) {
|
||||
PlotSquared.debug("ERROR: Player data (" + uuid.toString()
|
||||
+ ".dat) does not contain the the key \"bukkit\"");
|
||||
} else {
|
||||
Map<String, Tag> compoundMap = compound.getValue();
|
||||
CompoundTag bukkit = (CompoundTag) compoundMap.get("bukkit");
|
||||
Map<String, Tag> bukkitMap = bukkit.getValue();
|
||||
String name =
|
||||
(String) bukkitMap.get("lastKnownName").getValue();
|
||||
long last = (long) bukkitMap.get("lastPlayed").getValue();
|
||||
long first = (long) bukkitMap.get("firstPlayed").getValue();
|
||||
if (ExpireManager.IMP != null) {
|
||||
ExpireManager.IMP.storeDate(uuid, last);
|
||||
ExpireManager.IMP.storeAccountAge(uuid, last - first);
|
||||
}
|
||||
toAdd.put(new StringWrapper(name), uuid);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
PlotSquared.debug(Captions.PREFIX + "Invalid playerdata: " + current);
|
||||
}
|
||||
}
|
||||
}
|
||||
add(toAdd);
|
||||
if (all.isEmpty()) {
|
||||
if (whenDone != null) {
|
||||
whenDone.run();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
PlotSquared.debug(
|
||||
"Failed to cache: " + all.size() + " uuids - slowly processing all files");
|
||||
}
|
||||
}
|
||||
HashSet<String> worlds1 = Sets.newHashSet(world, "world");
|
||||
HashSet<UUID> uuids = new HashSet<>();
|
||||
HashSet<String> names = new HashSet<>();
|
||||
File playerDataFolder = null;
|
||||
for (String worldName : worlds1) {
|
||||
// Getting UUIDs
|
||||
playerDataFolder = new File(container, worldName + File.separator + "playerdata");
|
||||
String[] dat = playerDataFolder.list(new DatFileFilter());
|
||||
if ((dat != null) && (dat.length != 0)) {
|
||||
for (String current : dat) {
|
||||
String s = current.replaceAll(".dat$", "");
|
||||
try {
|
||||
UUID uuid = UUID.fromString(s);
|
||||
uuids.add(uuid);
|
||||
} catch (Exception ignored) {
|
||||
PlotSquared.debug(Captions.PREFIX + "Invalid PlayerData: " + current);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Getting names
|
||||
File playersFolder = new File(worldName + File.separator + "players");
|
||||
dat = playersFolder.list(new DatFileFilter());
|
||||
if ((dat != null) && (dat.length != 0)) {
|
||||
for (String current : dat) {
|
||||
names.add(current.replaceAll(".dat$", ""));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (UUID uuid : uuids) {
|
||||
try {
|
||||
File file =
|
||||
new File(playerDataFolder + File.separator + uuid.toString() + ".dat");
|
||||
if (!file.exists()) {
|
||||
continue;
|
||||
}
|
||||
CompoundTag compound = (CompoundTag) readTag(file);
|
||||
if (!compound.containsKey("bukkit")) {
|
||||
PlotSquared.debug("ERROR: Player data (" + uuid.toString()
|
||||
+ ".dat) does not contain the the key \"bukkit\"");
|
||||
} else {
|
||||
Map<String, Tag> compoundMap = compound.getValue();
|
||||
CompoundTag bukkit = (CompoundTag) compoundMap.get("bukkit");
|
||||
Map<String, Tag> bukkitMap = bukkit.getValue();
|
||||
String name = (String) bukkitMap.get("lastKnownName").getValue();
|
||||
StringWrapper wrap = new StringWrapper(name);
|
||||
if (!toAdd.containsKey(wrap)) {
|
||||
long last = (long) bukkitMap.get("lastPlayed").getValue();
|
||||
long first = (long) bukkitMap.get("firstPlayed").getValue();
|
||||
if (Settings.UUID.OFFLINE) {
|
||||
if (Settings.UUID.FORCE_LOWERCASE && !name.toLowerCase()
|
||||
.equals(name)) {
|
||||
uuid = FileUUIDHandler.this.uuidWrapper.getUUID(name);
|
||||
} else {
|
||||
long most = (long) compoundMap.get("UUIDMost").getValue();
|
||||
long least = (long) compoundMap.get("UUIDLeast").getValue();
|
||||
uuid = new UUID(most, least);
|
||||
}
|
||||
}
|
||||
if (ExpireManager.IMP != null) {
|
||||
ExpireManager.IMP.storeDate(uuid, last);
|
||||
ExpireManager.IMP.storeAccountAge(uuid, last - first);
|
||||
}
|
||||
toAdd.put(wrap, uuid);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
PlotSquared.debug(
|
||||
Captions.PREFIX + "&6Invalid PlayerData: " + uuid.toString() + ".dat");
|
||||
}
|
||||
}
|
||||
for (String name : names) {
|
||||
UUID uuid = FileUUIDHandler.this.uuidWrapper.getUUID(name);
|
||||
StringWrapper nameWrap = new StringWrapper(name);
|
||||
toAdd.put(nameWrap, uuid);
|
||||
}
|
||||
|
||||
if (getUUIDMap().isEmpty()) {
|
||||
for (OfflinePlotPlayer offlinePlotPlayer : FileUUIDHandler.this.uuidWrapper
|
||||
.getOfflinePlayers()) {
|
||||
long last = offlinePlotPlayer.getLastPlayed();
|
||||
if (last != 0) {
|
||||
String name = offlinePlotPlayer.getName();
|
||||
StringWrapper wrap = new StringWrapper(name);
|
||||
if (!toAdd.containsKey(wrap)) {
|
||||
UUID uuid = FileUUIDHandler.this.uuidWrapper.getUUID(offlinePlotPlayer);
|
||||
if (toAdd.containsValue(uuid)) {
|
||||
StringWrapper duplicate = toAdd.inverse().get(uuid);
|
||||
PlotSquared.debug(
|
||||
"The UUID: " + uuid.toString() + " is already mapped to "
|
||||
+ duplicate
|
||||
+ "\n It cannot be added to the Map with a key of " + wrap);
|
||||
}
|
||||
toAdd.putIfAbsent(wrap, uuid);
|
||||
if (ExpireManager.IMP != null) {
|
||||
ExpireManager.IMP.storeDate(uuid, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add(toAdd);
|
||||
if (whenDone != null) {
|
||||
whenDone.run();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void fetchUUID(final String name, final RunnableVal<UUID> ifFetch) {
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
ifFetch.value = FileUUIDHandler.this.uuidWrapper.getUUID(name);
|
||||
TaskManager.runTask(ifFetch);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.plotsquared.bukkit.util.uuid;
|
||||
|
||||
import com.plotsquared.player.OfflinePlotPlayer;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LowerOfflineUUIDWrapper extends OfflineUUIDWrapper {
|
||||
|
||||
@NotNull @Override public UUID getUUID(PlotPlayer player) {
|
||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player.getName().toLowerCase()).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override public UUID getUUID(OfflinePlotPlayer player) {
|
||||
return UUID.nameUUIDFromBytes(
|
||||
("OfflinePlayer:" + player.getName().toLowerCase()).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override public UUID getUUID(OfflinePlayer player) {
|
||||
return UUID.nameUUIDFromBytes(
|
||||
("OfflinePlayer:" + Objects.requireNonNull(player.getName()).toLowerCase()).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override public UUID getUUID(String name) {
|
||||
return UUID
|
||||
.nameUUIDFromBytes(("OfflinePlayer:" + name.toLowerCase()).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package com.plotsquared.bukkit.util.uuid;
|
||||
|
||||
import com.plotsquared.bukkit.player.BukkitOfflinePlayer;
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.player.OfflinePlotPlayer;
|
||||
import com.plotsquared.player.PlotPlayer;
|
||||
import com.plotsquared.util.StringWrapper;
|
||||
import com.plotsquared.util.uuid.UUIDHandler;
|
||||
import com.plotsquared.util.uuid.UUIDWrapper;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.BiMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OfflineUUIDWrapper extends UUIDWrapper {
|
||||
|
||||
private final Object[] arg = new Object[0];
|
||||
private Method getOnline = null;
|
||||
|
||||
public OfflineUUIDWrapper() {
|
||||
try {
|
||||
this.getOnline = Server.class.getMethod("getOnlinePlayers");
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull @Override public UUID getUUID(PlotPlayer player) {
|
||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player.getName()).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override public UUID getUUID(OfflinePlotPlayer player) {
|
||||
return UUID
|
||||
.nameUUIDFromBytes(("OfflinePlayer:" + player.getName()).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
public UUID getUUID(OfflinePlayer player) {
|
||||
return UUID
|
||||
.nameUUIDFromBytes(("OfflinePlayer:" + player.getName()).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override public OfflinePlotPlayer getOfflinePlayer(UUID uuid) {
|
||||
BiMap<UUID, StringWrapper> map = UUIDHandler.getUuidMap().inverse();
|
||||
String name = null;
|
||||
if (map.containsKey(uuid)) {
|
||||
name = map.get(uuid).value;
|
||||
}
|
||||
if (name != null) {
|
||||
OfflinePlayer op = Bukkit.getOfflinePlayer(name);
|
||||
if (op.hasPlayedBefore()) {
|
||||
return new BukkitOfflinePlayer(op);
|
||||
}
|
||||
}
|
||||
for (OfflinePlayer player : Bukkit.getOfflinePlayers()) {
|
||||
if (getUUID(player).equals(uuid)) {
|
||||
return new BukkitOfflinePlayer(player);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Player[] getOnlinePlayers() {
|
||||
if (this.getOnline == null) {
|
||||
Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
|
||||
return onlinePlayers.toArray(new Player[0]);
|
||||
}
|
||||
try {
|
||||
Object players = this.getOnline.invoke(Bukkit.getServer(), this.arg);
|
||||
if (players instanceof Player[]) {
|
||||
return (Player[]) players;
|
||||
} else {
|
||||
@SuppressWarnings("unchecked") Collection<? extends Player> p =
|
||||
(Collection<? extends Player>) players;
|
||||
return p.toArray(new Player[0]);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ignored) {
|
||||
PlotSquared.debug("Failed to resolve online players");
|
||||
this.getOnline = null;
|
||||
Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
|
||||
return onlinePlayers.toArray(new Player[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public UUID getUUID(String name) {
|
||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override public OfflinePlotPlayer[] getOfflinePlayers() {
|
||||
OfflinePlayer[] ops = Bukkit.getOfflinePlayers();
|
||||
return Arrays.stream(ops).map(BukkitOfflinePlayer::new).toArray(BukkitOfflinePlayer[]::new);
|
||||
}
|
||||
|
||||
@Override public OfflinePlotPlayer getOfflinePlayer(String name) {
|
||||
return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(name));
|
||||
}
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
package com.plotsquared.bukkit.util.uuid;
|
||||
|
||||
import com.plotsquared.PlotSquared;
|
||||
import com.plotsquared.config.Captions;
|
||||
import com.plotsquared.config.Settings;
|
||||
import com.plotsquared.database.SQLite;
|
||||
import com.plotsquared.util.tasks.RunnableVal;
|
||||
import com.plotsquared.util.StringWrapper;
|
||||
import com.plotsquared.util.MainUtil;
|
||||
import com.plotsquared.util.tasks.TaskManager;
|
||||
import com.plotsquared.util.uuid.UUIDHandler;
|
||||
import com.plotsquared.util.uuid.UUIDHandlerImplementation;
|
||||
import com.plotsquared.util.uuid.UUIDWrapper;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SQLUUIDHandler extends UUIDHandlerImplementation {
|
||||
|
||||
final int MAX_REQUESTS = 500;
|
||||
private final String PROFILE_URL =
|
||||
"https://sessionserver.mojang.com/session/minecraft/profile/";
|
||||
private final JSONParser jsonParser = new JSONParser();
|
||||
private final SQLite sqlite;
|
||||
|
||||
public SQLUUIDHandler(UUIDWrapper wrapper) {
|
||||
super(wrapper);
|
||||
this.sqlite =
|
||||
new SQLite(MainUtil.getFile(PlotSquared.get().IMP.getDirectory(), "usercache.db"));
|
||||
try {
|
||||
this.sqlite.openConnection();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try (PreparedStatement stmt = getConnection().prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS `usercache` (uuid VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL, PRIMARY KEY (uuid, username))")) {
|
||||
stmt.execute();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
startCaching(null);
|
||||
}
|
||||
|
||||
private Connection getConnection() {
|
||||
synchronized (this.sqlite) {
|
||||
return this.sqlite.getConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean startCaching(final Runnable whenDone) {
|
||||
if (!super.startCaching(whenDone)) {
|
||||
return false;
|
||||
}
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
try {
|
||||
HashBiMap<StringWrapper, UUID> toAdd = HashBiMap.create(new HashMap<>());
|
||||
try (PreparedStatement statement = getConnection()
|
||||
.prepareStatement("SELECT `uuid`, `username` FROM `usercache`");
|
||||
ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
StringWrapper username = new StringWrapper(resultSet.getString("username"));
|
||||
UUID uuid = UUID.fromString(resultSet.getString("uuid"));
|
||||
toAdd.put(new StringWrapper(username.value), uuid);
|
||||
}
|
||||
}
|
||||
add(toAdd);
|
||||
// This should be called as long as there are some unknown plots
|
||||
final ArrayDeque<UUID> toFetch = new ArrayDeque<>();
|
||||
for (UUID u : UUIDHandler.getAllUUIDS()) {
|
||||
if (!uuidExists(u)) {
|
||||
toFetch.add(u);
|
||||
}
|
||||
}
|
||||
if (toFetch.isEmpty()) {
|
||||
if (whenDone != null) {
|
||||
whenDone.run();
|
||||
}
|
||||
return;
|
||||
}
|
||||
FileUUIDHandler fileHandler = new FileUUIDHandler(SQLUUIDHandler.this.uuidWrapper);
|
||||
fileHandler.startCaching(() -> {
|
||||
// If the file based UUID handler didn't cache it, then we can't cache offline mode
|
||||
// Also, trying to cache based on files again, is useless as that's what the file based uuid cacher does
|
||||
if (Settings.UUID.OFFLINE) {
|
||||
if (whenDone != null) {
|
||||
whenDone.run();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
while (!toFetch.isEmpty()) {
|
||||
try {
|
||||
for (int i = 0; i < Math.min(MAX_REQUESTS, toFetch.size()); i++) {
|
||||
UUID uuid = toFetch.pop();
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(
|
||||
SQLUUIDHandler.this.PROFILE_URL + uuid.toString()
|
||||
.replace("-", "")).openConnection();
|
||||
try (InputStream con = connection.getInputStream()) {
|
||||
InputStreamReader reader = new InputStreamReader(con);
|
||||
JSONObject response =
|
||||
(JSONObject) SQLUUIDHandler.this.jsonParser
|
||||
.parse(reader);
|
||||
String name = (String) response.get("name");
|
||||
if (name != null) {
|
||||
add(new StringWrapper(name), uuid);
|
||||
}
|
||||
}
|
||||
connection.disconnect();
|
||||
}
|
||||
} catch (IOException | ParseException e) {
|
||||
PlotSquared.debug(
|
||||
"Invalid response from Mojang: Some UUIDs will be cached later. (`unknown` until then or player joins)");
|
||||
}
|
||||
try {
|
||||
//Mojang allows requests every 10 minutes according to https://wiki.vg/Mojang_API
|
||||
//15 Minutes is chosen here since system timers are not always precise
|
||||
//and it should provide enough time where Mojang won't block requests.
|
||||
TimeUnit.MINUTES.sleep(15);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (whenDone != null) {
|
||||
whenDone.run();
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
throw new SQLUUIDHandlerException("Couldn't select :s", e);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void fetchUUID(final String name, final RunnableVal<UUID> ifFetch) {
|
||||
PlotSquared.debug(Captions.PREFIX + "UUID for '" + name
|
||||
+ "' was null. We'll cache this from the Mojang servers!");
|
||||
if (ifFetch == null) {
|
||||
return;
|
||||
}
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
try {
|
||||
URL url = new URL(SQLUUIDHandler.this.PROFILE_URL);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setUseCaches(false);
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
String body = JSONArray.toJSONString(Collections.singletonList(name));
|
||||
OutputStream stream = connection.getOutputStream();
|
||||
stream.write(body.getBytes());
|
||||
stream.flush();
|
||||
stream.close();
|
||||
JSONArray array = (JSONArray) SQLUUIDHandler.this.jsonParser
|
||||
.parse(new InputStreamReader(connection.getInputStream()));
|
||||
JSONObject jsonProfile = (JSONObject) array.get(0);
|
||||
String id = (String) jsonProfile.get("id");
|
||||
String name1 = (String) jsonProfile.get("name");
|
||||
ifFetch.value = UUID.fromString(
|
||||
id.substring(0, 8) + '-' + id.substring(8, 12) + '-' + id.substring(12, 16)
|
||||
+ '-' + id.substring(16, 20) + '-' + id.substring(20, 32));
|
||||
} catch (IOException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
TaskManager.runTask(ifFetch);
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void handleShutdown() {
|
||||
super.handleShutdown();
|
||||
try {
|
||||
getConnection().close();
|
||||
} catch (SQLException e) {
|
||||
throw new SQLUUIDHandlerException("Couldn't close database connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful for name changes
|
||||
*/
|
||||
@Override public void rename(final UUID uuid, final StringWrapper name) {
|
||||
super.rename(uuid, name);
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
try (PreparedStatement statement = getConnection()
|
||||
.prepareStatement("UPDATE usercache SET `username`=? WHERE `uuid`=?")) {
|
||||
statement.setString(1, name.value);
|
||||
statement.setString(2, uuid.toString());
|
||||
statement.execute();
|
||||
PlotSquared.debug(
|
||||
Captions.PREFIX + "Name change for '" + uuid + "' to '" + name.value + '\'');
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public boolean add(final StringWrapper name, final UUID uuid) {
|
||||
// Ignoring duplicates
|
||||
if (super.add(name, uuid)) {
|
||||
TaskManager.runTaskAsync(() -> {
|
||||
try (PreparedStatement statement = getConnection()
|
||||
.prepareStatement("REPLACE INTO usercache (`uuid`, `username`) VALUES(?, ?)")) {
|
||||
statement.setString(1, uuid.toString());
|
||||
statement.setString(2, name.toString());
|
||||
statement.execute();
|
||||
PlotSquared
|
||||
.debug(Captions.PREFIX + "&cAdded '&6" + uuid + "&c' - '&6" + name + "&c'");
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class SQLUUIDHandlerException extends RuntimeException {
|
||||
|
||||
SQLUUIDHandlerException(String s, Throwable c) {
|
||||
super("SQLUUIDHandler caused an exception: " + s, c);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user