diff --git a/src/main/java/com/intellectualcrafters/plot/PS.java b/src/main/java/com/intellectualcrafters/plot/PS.java index ded0e19a7..2d2777b22 100644 --- a/src/main/java/com/intellectualcrafters/plot/PS.java +++ b/src/main/java/com/intellectualcrafters/plot/PS.java @@ -846,7 +846,7 @@ public class PS { */ @Deprecated public void sortPlotsByHash(final Plot[] input) { - final List[] bucket = new ArrayList[64]; + final List[] bucket = new ArrayList[32]; for (int i = 0; i < bucket.length; i++) { bucket[i] = new ArrayList(); } @@ -856,19 +856,19 @@ public class PS { maxLength = true; for (final Plot i : input) { tmp = MathMan.getPositiveId(i.hashCode()) / placement; - bucket[tmp & 63].add(i); + bucket[tmp & 31].add(i); if (maxLength && (tmp > 0)) { maxLength = false; } } int a = 0; - for (int b = 0; b < 64; b++) { + for (int b = 0; b < 32; b++) { for (final Plot i : bucket[b]) { input[a++] = i; } bucket[b].clear(); } - placement *= 64; + placement *= 32; } } @@ -1068,8 +1068,8 @@ public class PS { return strings.toArray(new String[strings.size()]); } - private String lastWorld; - private Map lastMap; + private volatile String lastWorld; + private volatile Map lastMap; /** * Get a map of the plots for a world @@ -1112,12 +1112,9 @@ public class PS { return null; } lastWorld = world; - if (plots.containsKey(world)) { - lastMap = plots.get(world); - if (lastMap != null) { - return lastMap.get(id); - } - return null; + lastMap = plots.get(world); + if (lastMap != null) { + return lastMap.get(id); } return null; } diff --git a/src/main/java/com/intellectualcrafters/plot/commands/Copy.java b/src/main/java/com/intellectualcrafters/plot/commands/Copy.java index efdb27a66..ffcfe4bd0 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/Copy.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/Copy.java @@ -67,7 +67,7 @@ public class Copy extends SubCommand { C.COMMAND_SYNTAX.send(plr, getUsage()); return false; } - if (!plot1.getWorld().equals(plot2.getWorld())) { + if (!plot1.getWorld().isCompatible(plot2.getWorld())) { C.PLOTWORLD_INCOMPATIBLE.send(plr); return false; } diff --git a/src/main/java/com/intellectualcrafters/plot/commands/Delete.java b/src/main/java/com/intellectualcrafters/plot/commands/Delete.java index 0ed9a050e..29f9559c2 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/Delete.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/Delete.java @@ -34,7 +34,6 @@ import com.intellectualcrafters.plot.util.EconHandler; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.Permissions; import com.intellectualcrafters.plot.util.TaskManager; -import com.intellectualcrafters.plot.util.UUIDHandler; import com.plotsquared.general.commands.CommandDeclaration; @CommandDeclaration( diff --git a/src/main/java/com/intellectualcrafters/plot/commands/Move.java b/src/main/java/com/intellectualcrafters/plot/commands/Move.java index 296d85f1e..8de95945c 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/Move.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/Move.java @@ -67,7 +67,7 @@ public class Move extends SubCommand { MainUtil.sendMessage(plr, C.COMMAND_SYNTAX, "/plot copy "); return false; } - if (!plot1.getWorld().equals(plot2.getWorld())) { + if (!plot1.getWorld().isCompatible(plot2.getWorld())) { C.PLOTWORLD_INCOMPATIBLE.send(plr); return false; } diff --git a/src/main/java/com/intellectualcrafters/plot/commands/Owner.java b/src/main/java/com/intellectualcrafters/plot/commands/Owner.java index 0d95e1b39..896cf06fc 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/Owner.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/Owner.java @@ -45,30 +45,32 @@ public class Owner extends SetCommand { @Override public boolean set(PlotPlayer plr, Plot plot, String value) { HashSet plots = MainUtil.getConnectedPlots(plot); - final PlotPlayer other = UUIDHandler.getPlayer(value); UUID uuid = null; - if (other == null) { - if (Permissions.hasPermission(plr, "plots.admin.command.setowner")) { - if ((uuid = UUIDHandler.getUUID(value, null)) == null) { - try { - uuid = UUID.fromString(value); - } catch (Exception e) {} - } - } - } - else { - other.getUUID(); + String name = null; + if (value.length() == 36) { + try { + uuid = UUID.fromString(value); + name = MainUtil.getName(uuid); + } catch (Exception e) {} + } else { + uuid = UUIDHandler.getUUID(value, null); + name = UUIDHandler.getName(uuid); + name = name == null ? value : name; } if (uuid == null) { - MainUtil.sendMessage(plr, C.INVALID_PLAYER, value); + C.INVALID_PLAYER.send(plr, value); return false; } - String name = other == null ? MainUtil.getName(uuid) : other.getName(); if (plot.isOwner(uuid)) { C.ALREADY_OWNER.send(plr); return false; } - if (other != null && !Permissions.hasPermission(plr, "plots.admin.command.setowner")) { + PlotPlayer other = UUIDHandler.getPlayer(uuid); + if (!Permissions.hasPermission(plr, "plots.admin.command.setowner")) { + if (other == null) { + C.INVALID_PLAYER_OFFLINE.send(plr, value); + return false; + } final int size = plots.size(); final int currentPlots = (Settings.GLOBAL_LIMIT ? MainUtil.getPlayerPlotCount(other) : MainUtil.getPlayerPlotCount(plot.world, other)) + size; if (currentPlots > MainUtil.getAllowedPlots(other)) { @@ -76,6 +78,7 @@ public class Owner extends SetCommand { return false; } } + plot.setOwner(uuid); MainUtil.setSign(name, plot); MainUtil.sendMessage(plr, C.SET_OWNER); diff --git a/src/main/java/com/intellectualcrafters/plot/commands/Swap.java b/src/main/java/com/intellectualcrafters/plot/commands/Swap.java index ec66d3b8d..473697f4f 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/Swap.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/Swap.java @@ -55,7 +55,7 @@ public class Swap extends SubCommand { MainUtil.sendMessage(plr, C.COMMAND_SYNTAX, "/plot copy "); return false; } - if (!plot1.getWorld().equals(plot2.getWorld())) { + if (!plot1.getWorld().isCompatible(plot2.getWorld())) { C.PLOTWORLD_INCOMPATIBLE.send(plr); return false; } diff --git a/src/main/java/com/intellectualcrafters/plot/commands/list.java b/src/main/java/com/intellectualcrafters/plot/commands/list.java index 5398b7499..0afbf75fc 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/list.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/list.java @@ -331,7 +331,7 @@ public class list extends SubCommand { } } if (sort) { - plots = PS.get().sortPlots(plots, SortType.DISTANCE_FROM_ORIGIN, world); + plots = PS.get().sortPlots(plots, SortType.CREATION_DATE_TIMESTAMP, world); } if (page < 0) { page = 0; diff --git a/src/main/java/com/intellectualcrafters/plot/config/C.java b/src/main/java/com/intellectualcrafters/plot/config/C.java index 2e8518734..73086c2c2 100644 --- a/src/main/java/com/intellectualcrafters/plot/config/C.java +++ b/src/main/java/com/intellectualcrafters/plot/config/C.java @@ -375,6 +375,7 @@ public enum C { */ INVALID_PLAYER_WAIT("$2Player not found: $1%s$2, fetching it. Try again soon.", "Errors"), INVALID_PLAYER("$2Player not found: $1%s.", "Errors"), + INVALID_PLAYER_OFFLINE("$2The player must be online: $1%s.", "Errors"), // SETTINGS_PASTE_UPLOADED("$2settings.yml was uploaded to: $1%url%", "Paste"), // LATEST_LOG_UPLOADED("$2latest.log was uploaded to: $1%url%", "Paste"), DEBUG_REPORT_CREATED("$1Uploaded a full debug to: $1%url%", "Paste"), diff --git a/src/main/java/com/intellectualcrafters/plot/database/SQLManager.java b/src/main/java/com/intellectualcrafters/plot/database/SQLManager.java index 486ddde06..675c09228 100644 --- a/src/main/java/com/intellectualcrafters/plot/database/SQLManager.java +++ b/src/main/java/com/intellectualcrafters/plot/database/SQLManager.java @@ -1193,6 +1193,9 @@ public class SQLManager implements AbstractDB { @Override public void deleteSettings(final Plot plot) { + if (plot.settings == null) { + return; + } addPlotTask(plot, new UniqueStatement("delete_plot_settings") { @Override public void set(final PreparedStatement stmt) throws SQLException { @@ -1301,7 +1304,6 @@ public class SQLManager implements AbstractDB { */ @Override public void delete(final Plot plot) { - PS.get().removePlot(plot.world, plot.id, false); deleteSettings(plot); deleteDenied(plot); deleteHelpers(plot); @@ -1736,7 +1738,7 @@ public class SQLManager implements AbstractDB { map = new ConcurrentHashMap(); newplots.put(plot.world, map); } - newplots.get(plot.world).put(plot.id, plot); + map.put(plot.id, plot); } } boolean invalidPlot = false; diff --git a/src/main/java/com/intellectualcrafters/plot/flag/FlagManager.java b/src/main/java/com/intellectualcrafters/plot/flag/FlagManager.java index 6fe60e347..67d5814c3 100644 --- a/src/main/java/com/intellectualcrafters/plot/flag/FlagManager.java +++ b/src/main/java/com/intellectualcrafters/plot/flag/FlagManager.java @@ -120,15 +120,19 @@ public class FlagManager { } public static Flag getSettingFlag(final String world, final PlotSettings settings, final String id) { + System.out.print("GETTIGN FLAG!! " + (settings.flags.size() == 0) + " | " + ((settings.flags.get(id)) == null)); Flag flag; if ((settings.flags.size() == 0) || ((flag = settings.flags.get(id)) == null)) { final PlotWorld plotworld = PS.get().getPlotWorld(world); if (plotworld == null) { + System.out.print("pw is nykl"); return null; } if (plotworld.DEFAULT_FLAGS.size() == 0) { + System.out.print("no default flags"); return null; } + System.out.print("DEFAULT_FLAGS: " + plotworld.DEFAULT_FLAGS + " | " + plotworld.DEFAULT_FLAGS.get(id)); return plotworld.DEFAULT_FLAGS.get(id); } return flag; diff --git a/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java b/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java index 475dd8aee..860116ff2 100644 --- a/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java +++ b/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java @@ -28,9 +28,21 @@ public class ClassicPlotManager extends SquarePlotManager { return true; } case "all": { + setAll(plotworld, plotid, blocks); + return true; + } + case "air": { setAir(plotworld, plotid, blocks); return true; } + case "main": { + setMain(plotworld, plotid, blocks); + return true; + } + case "middle": { + setMiddle(plotworld, plotid, blocks); + return true; + } case "outline": { setOutline(plotworld, plotid, blocks); return true; @@ -67,6 +79,20 @@ public class ClassicPlotManager extends SquarePlotManager { return true; } + public boolean setAll(final PlotWorld plotworld, final PlotId plotid, final PlotBlock[] blocks) { + Plot plot = MainUtil.getPlotAbs(plotworld.worldname, plotid); + if (!plot.isBasePlot()) { + return false; + } + final ClassicPlotWorld dpw = (ClassicPlotWorld) plotworld; + for (RegionWrapper region : MainUtil.getRegions(plot)) { + Location pos1 = new Location(plot.world, region.minX, 1, region.minZ); + Location pos2 = new Location(plot.world, region.maxX, 255, region.maxZ); + MainUtil.setCuboidAsync(plotworld.worldname, pos1, pos2, blocks); + } + return true; + } + public boolean setAir(final PlotWorld plotworld, final PlotId plotid, final PlotBlock[] blocks) { Plot plot = MainUtil.getPlotAbs(plotworld.worldname, plotid); if (!plot.isBasePlot()) { @@ -81,6 +107,31 @@ public class ClassicPlotManager extends SquarePlotManager { return true; } + public boolean setMain(final PlotWorld plotworld, final PlotId plotid, final PlotBlock[] blocks) { + Plot plot = MainUtil.getPlotAbs(plotworld.worldname, plotid); + if (!plot.isBasePlot()) { + return false; + } + final ClassicPlotWorld dpw = (ClassicPlotWorld) plotworld; + for (RegionWrapper region : MainUtil.getRegions(plot)) { + Location pos1 = new Location(plot.world, region.minX, 1, region.minZ); + Location pos2 = new Location(plot.world, region.maxX, dpw.PLOT_HEIGHT - 1, region.maxZ); + MainUtil.setCuboidAsync(plotworld.worldname, pos1, pos2, blocks); + } + return true; + } + + public boolean setMiddle(final PlotWorld plotworld, final PlotId plotid, final PlotBlock[] blocks) { + Plot plot = MainUtil.getPlotAbs(plotworld.worldname, plotid); + if (!plot.isBasePlot()) { + return false; + } + Location[] corners = plot.getCorners(); + final ClassicPlotWorld dpw = (ClassicPlotWorld) plotworld; + SetBlockQueue.setBlock(plotworld.worldname, (corners[0].getX() + corners[1].getX()) / 2, dpw.PLOT_HEIGHT, (corners[0].getZ() + corners[1].getZ()) / 2, blocks[0]); + return true; + } + public boolean setOutline(final PlotWorld plotworld, final PlotId plotid, final PlotBlock[] blocks) { final ClassicPlotWorld dpw = (ClassicPlotWorld) plotworld; if (dpw.ROAD_WIDTH == 0) { @@ -368,7 +419,7 @@ public class ClassicPlotManager extends SquarePlotManager { @Override public String[] getPlotComponents(final PlotWorld plotworld, final PlotId plotid) { - return new String[] { "floor", "wall", "border", "all", "outline" }; + return new String[] { "main", "floor", "air", "all", "border", "wall", "outline", "middle" }; } /** diff --git a/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java b/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java index dba8884d6..2cdda4176 100644 --- a/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java +++ b/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotWorld.java @@ -28,6 +28,7 @@ import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.C; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PlotLoc; +import com.intellectualcrafters.plot.object.PlotWorld; import com.intellectualcrafters.plot.object.schematic.PlotItem; import com.intellectualcrafters.plot.util.SchematicHandler; import com.intellectualcrafters.plot.util.SchematicHandler.Dimension; @@ -72,6 +73,14 @@ public class HybridPlotWorld extends ClassicPlotWorld { } } + @Override + public boolean isCompatible(PlotWorld plotworld) { + if (plotworld == null || !(plotworld instanceof SquarePlotWorld)) { + return false; + } + return ((ClassicPlotWorld) plotworld).PLOT_WIDTH == PLOT_WIDTH; + } + public void setupSchematics() { G_SCH_DATA = new HashMap<>(); G_SCH = new HashMap<>(); diff --git a/src/main/java/com/intellectualcrafters/plot/object/Location.java b/src/main/java/com/intellectualcrafters/plot/object/Location.java index 0fd71e17c..9a06d4c92 100644 --- a/src/main/java/com/intellectualcrafters/plot/object/Location.java +++ b/src/main/java/com/intellectualcrafters/plot/object/Location.java @@ -84,6 +84,10 @@ public class Location implements Cloneable, Comparable { public Plot getPlot() { return MainUtil.getPlot(this); } + + public ChunkLoc getChunkLoc() { + return new ChunkLoc(x >> 4, z >> 4); + } public void setWorld(final String world) { this.world = world; diff --git a/src/main/java/com/intellectualcrafters/plot/object/Plot.java b/src/main/java/com/intellectualcrafters/plot/object/Plot.java index 17e54da2a..865a68e5d 100644 --- a/src/main/java/com/intellectualcrafters/plot/object/Plot.java +++ b/src/main/java/com/intellectualcrafters/plot/object/Plot.java @@ -416,7 +416,6 @@ public class Plot { */ private Plot origin; - /** * The base plot is an arbitrary but specific connected plot. It is useful for the following:
* - Merged plots need to be treated as a single plot for most purposes
@@ -721,7 +720,7 @@ public class Plot { */ public double getAverageRating() { double sum = 0; - final Collection ratings = getBasePlot(false).getRatings().values(); + final Collection ratings = getRatings().values(); for (final Rating rating : ratings) { sum += rating.getAverageRating(); } diff --git a/src/main/java/com/intellectualcrafters/plot/object/PlotHandler.java b/src/main/java/com/intellectualcrafters/plot/object/PlotHandler.java index f0f13ea72..5996cb125 100644 --- a/src/main/java/com/intellectualcrafters/plot/object/PlotHandler.java +++ b/src/main/java/com/intellectualcrafters/plot/object/PlotHandler.java @@ -237,9 +237,9 @@ public class PlotHandler { return false; } for (Plot current : MainUtil.getConnectedPlots(plot)) { - current.settings = null; PS.get().removePlot(current.world, current.id, true); DBFunc.delete(current); + current.settings = null; } return true; } diff --git a/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java b/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java index fa51bca3a..5781a0c25 100644 --- a/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java +++ b/src/main/java/com/intellectualcrafters/plot/object/PlotWorld.java @@ -126,6 +126,10 @@ public abstract class PlotWorld { return true; } + public boolean isCompatible(PlotWorld plotworld) { + return equals(plotworld); + } + /** * When a world is created, the following method will be called for each * diff --git a/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java b/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java index 258603e46..28299effa 100644 --- a/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java +++ b/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java @@ -20,6 +20,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// package com.intellectualcrafters.plot.util; +import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -36,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; +import com.google.common.collect.BiMap; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.C; import com.intellectualcrafters.plot.config.Settings; @@ -56,6 +58,7 @@ import com.intellectualcrafters.plot.object.PlotWorld; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.object.RegionWrapper; import com.intellectualcrafters.plot.object.RunnableVal; +import com.intellectualcrafters.plot.object.StringWrapper; import com.plotsquared.listener.PlotListener; /** @@ -1427,7 +1430,9 @@ public class MainUtil { final HashSet regions = getRegions(plot); final HashSet plots = getConnectedPlots(plot); final ArrayDeque queue = new ArrayDeque<>(plots); - removeSign(plot); + if (isDelete) { + removeSign(plot); + } MainUtil.unlinkPlot(plot, true, !isDelete); final PlotManager manager = PS.get().getPlotManager(plot.world); final PlotWorld plotworld = PS.get().getPlotWorld(plot.world); @@ -1724,6 +1729,12 @@ public class MainUtil { return true; } + /** + * Check if a plot can be claimed + * @param player + * @param plot + * @return + */ public static boolean canClaim(final PlotPlayer player, final Plot plot) { if (plot == null) { return false; @@ -1736,7 +1747,63 @@ public class MainUtil { } } } - return plot.owner == null; + return guessOwner(plot) == null; + } + + /** + * Try to guess who the plot owner is: + * - Checks cache + * - Checks sign text + * @param plot + * @return + */ + public static UUID guessOwner(Plot plot) { + if (plot.owner != null) { + return plot.owner; + } + PlotWorld pw = plot.getWorld(); + if (!pw.ALLOW_SIGNS) { + return null; + } + Location loc = plot.getManager().getSignLoc(pw, plot); + ChunkManager.manager.loadChunk(loc.getWorld(), loc.getChunkLoc(), false); + String[] lines = BlockManager.manager.getSign(loc); + if (lines == null) { + return null; + } + loop: + for (int i = 4; i > 0; i--) { + String caption = C.valueOf("OWNER_SIGN_LINE_" + i).s(); + int index = caption.indexOf("%plr%"); + if (index == -1) { + continue; + } + String name = lines[i - 1].substring(index); + if (name.length() == 0) { + return null; + } + UUID owner = UUIDHandler.getUUID(name, null); + if (owner != null) { + plot.owner = owner; + break; + } + if (lines[i - 1].length() == 15) { + BiMap map = UUIDHandler.getUuidMap(); + for (Entry entry : map.entrySet()) { + String key = entry.getKey().value; + if (key.length() > name.length() && key.startsWith(name)) { + plot.owner = entry.getValue(); + break loop; + } + } + } + plot.owner = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)); + break; + } + if (plot.owner != null) { + plot.create(); + } + return plot.owner; } public static boolean isUnowned(final String world, final PlotId pos1, final PlotId pos2) { diff --git a/src/main/java/com/intellectualcrafters/plot/util/UUIDHandlerImplementation.java b/src/main/java/com/intellectualcrafters/plot/util/UUIDHandlerImplementation.java index 4eeeabb9e..de1010f1f 100644 --- a/src/main/java/com/intellectualcrafters/plot/util/UUIDHandlerImplementation.java +++ b/src/main/java/com/intellectualcrafters/plot/util/UUIDHandlerImplementation.java @@ -132,17 +132,21 @@ public abstract class UUIDHandlerImplementation { } try { final UUID offline = uuidMap.put(name, uuid); - if ((offline != null) && !offline.equals(uuid)) { - final Set plots = PS.get().getPlots(offline); - if (plots.size() > 0) { - for (final Plot plot : PS.get().getPlots(offline)) { - plot.owner = uuid; + if (offline != null) { + if (!offline.equals(uuid)) { + final Set plots = PS.get().getPlots(offline); + if (plots.size() > 0) { + for (final Plot plot : PS.get().getPlots(offline)) { + plot.owner = uuid; + } + DBFunc.replaceUUID(offline, uuid); + PS.debug("&cDetected invalid UUID stored for (1): " + name.value); + PS.debug("&7 - Did you recently switch to online-mode storage without running `uuidconvert`?"); + PS.debug("&6PlotSquared will update incorrect entries when the user logs in, or you can reconstruct your database."); } - DBFunc.replaceUUID(offline, uuid); - PS.debug("&cDetected invalid UUID stored for (1): " + name.value); - PS.debug("&7 - Did you recently switch to online-mode storage without running `uuidconvert`?"); - PS.debug("&6PlotSquared will update incorrect entries when the user logs in, or you can reconstruct your database."); + return true; } + return false; } } catch (final Exception e) { final BiMap inverse = uuidMap.inverse(); diff --git a/src/main/java/com/intellectualcrafters/plot/uuid/NameFetcher.java b/src/main/java/com/intellectualcrafters/plot/uuid/NameFetcher.java new file mode 100644 index 000000000..2e5e5da23 --- /dev/null +++ b/src/main/java/com/intellectualcrafters/plot/uuid/NameFetcher.java @@ -0,0 +1,44 @@ +package com.intellectualcrafters.plot.uuid; + +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Callable; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +public class NameFetcher implements Callable> { + private static final String PROFILE_URL = "https://sessionserver.mojang.com/session/minecraft/profile/"; + private final JSONParser jsonParser = new JSONParser(); + private final ArrayDeque uuids; + + public NameFetcher(List uuids) { + this.uuids = new ArrayDeque<>(uuids); + } + + @Override + public Map call() throws Exception { + Map uuidStringMap = new HashMap(); + for (UUID uuid : uuids) { + HttpURLConnection connection = (HttpURLConnection) new URL(PROFILE_URL + uuid.toString().replace("-", "")).openConnection(); + JSONObject response = (JSONObject) jsonParser.parse(new InputStreamReader(connection.getInputStream())); + String name = (String) response.get("name"); + if (name == null) { + continue; + } + String cause = (String) response.get("cause"); + String errorMessage = (String) response.get("errorMessage"); + if (cause != null && cause.length() > 0) { + throw new IllegalStateException(errorMessage); + } + uuidStringMap.put(uuid, name); + } + return uuidStringMap; + } +} diff --git a/src/main/java/com/intellectualcrafters/plot/uuid/UUIDFetcher.java b/src/main/java/com/intellectualcrafters/plot/uuid/UUIDFetcher.java new file mode 100644 index 000000000..a70e86635 --- /dev/null +++ b/src/main/java/com/intellectualcrafters/plot/uuid/UUIDFetcher.java @@ -0,0 +1,102 @@ +package com.intellectualcrafters.plot.uuid; + +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Callable; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +import com.google.common.collect.ImmutableList; + +public class UUIDFetcher implements Callable> { + private static final double PROFILES_PER_REQUEST = 100; + private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; + private final JSONParser jsonParser = new JSONParser(); + private final List names; + private final boolean rateLimiting; + + public UUIDFetcher(List names, boolean rateLimiting) { + this.names = ImmutableList.copyOf(names); + this.rateLimiting = rateLimiting; + } + + public UUIDFetcher(List names) { + this(names, true); + } + + @Override + public Map call() throws Exception { + Map uuidMap = new HashMap(); + int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); + for (int i = 0; i < requests; i++) { + HttpURLConnection connection = createConnection(); + String body = JSONArray.toJSONString(names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); + writeBody(connection, body); + JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream())); + for (Object profile : array) { + JSONObject jsonProfile = (JSONObject) profile; + String id = (String) jsonProfile.get("id"); + String name = (String) jsonProfile.get("name"); + UUID uuid = UUIDFetcher.getUUID(id); + uuidMap.put(name, uuid); + } + if (rateLimiting && i != requests - 1) { + Thread.sleep(100L); + } + } + return uuidMap; + } + + private static void writeBody(HttpURLConnection connection, String body) throws Exception { + OutputStream stream = connection.getOutputStream(); + stream.write(body.getBytes()); + stream.flush(); + stream.close(); + } + + private static HttpURLConnection createConnection() throws Exception { + URL url = new URL(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); + return connection; + } + + private static UUID getUUID(String id) { + return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32)); + } + + public static byte[] toBytes(UUID uuid) { + ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]); + byteBuffer.putLong(uuid.getMostSignificantBits()); + byteBuffer.putLong(uuid.getLeastSignificantBits()); + return byteBuffer.array(); + } + + public static UUID fromBytes(byte[] array) { + if (array.length != 16) { + throw new IllegalArgumentException("Illegal byte array length: " + array.length); + } + ByteBuffer byteBuffer = ByteBuffer.wrap(array); + long mostSignificant = byteBuffer.getLong(); + long leastSignificant = byteBuffer.getLong(); + return new UUID(mostSignificant, leastSignificant); + } + + public static UUID getUUIDOf(String name) throws Exception { + return new UUIDFetcher(Arrays.asList(name)).call().get(name); + } +} \ No newline at end of file diff --git a/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java b/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java index 3572f4d3c..c9e9cea93 100644 --- a/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java +++ b/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java @@ -1012,10 +1012,10 @@ public class BukkitChunkManager extends ChunkManager { } private void count(final int[] count, final Entity entity) { - count[0]++; switch (entity.getType()) { case PLAYER: { // not valid + return; } case SMALL_FIREBALL: case FIREBALL: @@ -1118,6 +1118,7 @@ public class BukkitChunkManager extends ChunkManager { } } } + count[0]++; } @Override diff --git a/src/main/java/com/plotsquared/bukkit/uuid/SQLUUIDHandler.java b/src/main/java/com/plotsquared/bukkit/uuid/SQLUUIDHandler.java index 0ce7e4616..8d7bafaff 100644 --- a/src/main/java/com/plotsquared/bukkit/uuid/SQLUUIDHandler.java +++ b/src/main/java/com/plotsquared/bukkit/uuid/SQLUUIDHandler.java @@ -1,22 +1,23 @@ package com.plotsquared.bukkit.uuid; -import java.io.BufferedReader; -import java.io.IOException; 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.ArrayList; +import java.util.ArrayDeque; +import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import java.util.UUID; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + import com.google.common.collect.HashBiMap; -import com.intellectualcrafters.json.JSONObject; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.C; import com.intellectualcrafters.plot.config.Settings; @@ -31,6 +32,11 @@ import com.intellectualcrafters.plot.uuid.UUIDWrapper; public class SQLUUIDHandler extends UUIDHandlerImplementation { + final String PROFILE_URL = "https://sessionserver.mojang.com/session/minecraft/profile/"; + final int MAX_REQUESTS = 500; + final int INTERVAL = 12000; + final JSONParser jsonParser = new JSONParser(); + public SQLUUIDHandler(final UUIDWrapper wrapper) { super(wrapper); _sqLite = new SQLite("./plugins/PlotSquared/usercache.db"); @@ -89,7 +95,7 @@ public class SQLUUIDHandler extends UUIDHandlerImplementation { add(new StringWrapper("*"), DBFunc.everyone); // This should be called as long as there are some unknown plots - final List toFetch = new ArrayList<>(); + final ArrayDeque toFetch = new ArrayDeque<>(); for (final UUID u : UUIDHandler.getAllUUIDS()) { if (!uuidExists(u)) { toFetch.add(u); @@ -113,49 +119,80 @@ public class SQLUUIDHandler extends UUIDHandlerImplementation { } return; } - if (!Settings.OFFLINE_MODE) { - PS.debug(C.PREFIX.s() + "&cWill fetch &6" + toFetch.size() + "&c from mojang!"); - int i = 0; - final Iterator iterator = toFetch.iterator(); - while (iterator.hasNext()) { - final StringBuilder url = new StringBuilder("http://api.intellectualsites.com/uuid/?user="); - final List currentIteration = new ArrayList<>(); - while ((i++ <= 15) && iterator.hasNext()) { - final UUID _uuid = iterator.next(); - url.append(_uuid.toString()); - if (iterator.hasNext()) { - url.append(","); - } - currentIteration.add(_uuid); - } - PS.debug(C.PREFIX.s() + "&cWill attempt to fetch &6" + currentIteration.size() + "&c uuids from: &6" + url.toString()); + + TaskManager.runTaskAsync(new Runnable() { + @Override + public void run() { try { - final HttpURLConnection connection = (HttpURLConnection) new URL(url.toString()).openConnection(); - connection.setRequestProperty("User-Agent", "Mozilla/5.0"); - final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String line; - final StringBuilder rawJSON = new StringBuilder(); - while ((line = reader.readLine()) != null) { - rawJSON.append(line); - } - reader.close(); - final JSONObject object = new JSONObject(rawJSON.toString()); - for (final UUID _u : currentIteration) { - final Object o = object.getJSONObject(_u.toString().replace("-", "")).get("username"); - if ((o == null) || !(o instanceof String)) { - continue; + if (toFetch.size() == 0) { + if (whenDone != null) { + whenDone.run(); } - add(new StringWrapper(o.toString()), _u); + return; } - } catch (final Exception e) { + for (int i = 0; i < Math.min(500, toFetch.size()); i++) { + UUID uuid = toFetch.pop(); + HttpURLConnection connection = (HttpURLConnection) new URL(PROFILE_URL + uuid.toString().replace("-", "")).openConnection(); + InputStreamReader reader = new InputStreamReader(connection.getInputStream()); + JSONObject response = (JSONObject) jsonParser.parse(reader); + String name = (String) response.get("name"); + if (name != null) { + add(new StringWrapper(name), uuid); + } + } + } catch (Exception e) { e.printStackTrace(); } - i = 0; + TaskManager.runTaskLaterAsync(this, INTERVAL); } - } - if (whenDone != null) { - whenDone.run(); - } + }); + /* + * This API is no longer accessible. + */ + // if (!Settings.OFFLINE_MODE) { + // PS.debug(C.PREFIX.s() + "&cWill fetch &6" + toFetch.size() + "&c from mojang!"); + // + // int i = 0; + // final Iterator iterator = toFetch.iterator(); + // while (iterator.hasNext()) { + // final StringBuilder url = new StringBuilder("http://api.intellectualsites.com/uuid/?user="); + // final List currentIteration = new ArrayList<>(); + // while ((i++ <= 15) && iterator.hasNext()) { + // final UUID _uuid = iterator.next(); + // url.append(_uuid.toString()); + // if (iterator.hasNext()) { + // url.append(","); + // } + // currentIteration.add(_uuid); + // } + // PS.debug(C.PREFIX.s() + "&cWill attempt to fetch &6" + currentIteration.size() + "&c uuids from: &6" + url.toString()); + // try { + // final HttpURLConnection connection = (HttpURLConnection) new URL(url.toString()).openConnection(); + // connection.setRequestProperty("User-Agent", "Mozilla/5.0"); + // final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + // String line; + // final StringBuilder rawJSON = new StringBuilder(); + // while ((line = reader.readLine()) != null) { + // rawJSON.append(line); + // } + // reader.close(); + // final JSONObject object = new JSONObject(rawJSON.toString()); + // for (final UUID _u : currentIteration) { + // final Object o = object.getJSONObject(_u.toString().replace("-", "")).get("username"); + // if ((o == null) || !(o instanceof String)) { + // continue; + // } + // add(new StringWrapper(o.toString()), _u); + // } + // } catch (final Exception e) { + // e.printStackTrace(); + // } + // i = 0; + // } + // } + // if (whenDone != null) { + // whenDone.run(); + // } } }); } catch (final SQLException e) { @@ -169,24 +206,31 @@ public class SQLUUIDHandler extends UUIDHandlerImplementation { @Override public void fetchUUID(final String name, final RunnableVal ifFetch) { PS.debug(C.PREFIX.s() + "UUID for '" + name + "' was null. We'll cache this from the mojang servers!"); + if (ifFetch == null) { + return; + } TaskManager.runTaskAsync(new Runnable() { @Override public void run() { - final String url = "http://api.intellectualsites.com/uuid/?user=" + name; try { - final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setRequestProperty("User-Agent", "Mozilla/5.0"); - final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String line; - final StringBuilder rawJSON = new StringBuilder(); - while ((line = reader.readLine()) != null) { - rawJSON.append(line); - } - reader.close(); - final JSONObject object = new JSONObject(rawJSON.toString()); - ifFetch.value = UUID.fromString(object.getJSONObject(name).getString("dashed")); - add(new StringWrapper(name), ifFetch.value); - } catch (final IOException e) { + URL url = new URL(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(Arrays.asList(name)); + OutputStream stream = connection.getOutputStream(); + stream.write(body.getBytes()); + stream.flush(); + stream.close(); + JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream())); + JSONObject jsonProfile = (JSONObject) array.get(0); + String id = (String) jsonProfile.get("id"); + String name = (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 (Exception e) { e.printStackTrace(); } TaskManager.runTask(ifFetch); @@ -212,7 +256,7 @@ public class SQLUUIDHandler extends UUIDHandlerImplementation { @Override public void run() { try { - final PreparedStatement statement = getConnection().prepareStatement("INSERT INTO usercache (`uuid`, `username`) VALUES(?, ?)"); + final PreparedStatement statement = getConnection().prepareStatement("REPLACE INTO usercache (`uuid`, `username`) VALUES(?, ?)"); statement.setString(1, uuid.toString()); statement.setString(2, name.toString()); statement.execute(); @@ -228,38 +272,8 @@ public class SQLUUIDHandler extends UUIDHandlerImplementation { } /** - * This isn't used as any UUID that is unknown is bulk cached (in lots of 16) - * @param uuid - * @return + * This is useful for name changes */ - @Deprecated - public String getName__unused__(final UUID uuid) { - PS.debug(C.PREFIX.s() + "Name for '" + uuid + "' was null. We'll cache this from the mojang servers!"); - TaskManager.runTaskAsync(new Runnable() { - @Override - public void run() { - final String url = "http://api.intellectualsites.com/uuid/?user=" + uuid; - try { - final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setRequestProperty("User-Agent", "Mozilla/5.0"); - final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String line; - final StringBuilder rawJSON = new StringBuilder(); - while ((line = reader.readLine()) != null) { - rawJSON.append(line); - } - reader.close(); - final JSONObject object = new JSONObject(rawJSON.toString()); - final String username = object.getJSONObject(uuid.toString().replace("-", "")).getString("username"); - add(new StringWrapper(username), uuid); - } catch (final IOException e) { - e.printStackTrace(); - } - } - }); - return null; - } - @Override public void rename(final UUID uuid, final StringWrapper name) { super.rename(uuid, name); diff --git a/target/PlotSquared-Bukkit.jar b/target/PlotSquared-Bukkit.jar index 6daf137ca..cd977fcaf 100644 Binary files a/target/PlotSquared-Bukkit.jar and b/target/PlotSquared-Bukkit.jar differ