mirror of
https://github.com/PryPurity/WorldBorder.git
synced 2024-11-22 11:35:14 +01:00
Misc 1.14 updates:
* using "force loaded" chunk flag instead of canceling ChunkUnloadEvents, which is no longer possible as of Spigot 1.14 * replaced UUID lookup code with newer and slightly improved implementation * added new 1.14 wooden sign variations to safe open blocks list * fix for error if Fill was canceled or finished without actually loading any chunks * added notice to end of Trim recommending server restart
This commit is contained in:
parent
179d1cfda3
commit
e14d526541
@ -327,8 +327,6 @@ public class BorderData
|
||||
safeOpenBlocks.add(Material.WALL_TORCH);
|
||||
safeOpenBlocks.add(Material.REDSTONE_WIRE);
|
||||
safeOpenBlocks.add(Material.WHEAT);
|
||||
safeOpenBlocks.add(Material.SIGN);
|
||||
safeOpenBlocks.add(Material.WALL_SIGN);
|
||||
safeOpenBlocks.add(Material.LADDER);
|
||||
safeOpenBlocks.add(Material.LEVER);
|
||||
safeOpenBlocks.add(Material.LIGHT_WEIGHTED_PRESSURE_PLATE);
|
||||
@ -376,6 +374,22 @@ public class BorderData
|
||||
safeOpenBlocks.add(Material.TALL_GRASS);
|
||||
safeOpenBlocks.add(Material.LARGE_FERN);
|
||||
safeOpenBlocks.add(Material.BEETROOTS);
|
||||
try
|
||||
{ // signs in 1.14 can be different wood types
|
||||
safeOpenBlocks.add(Material.ACACIA_SIGN);
|
||||
safeOpenBlocks.add(Material.ACACIA_WALL_SIGN);
|
||||
safeOpenBlocks.add(Material.BIRCH_SIGN);
|
||||
safeOpenBlocks.add(Material.BIRCH_WALL_SIGN);
|
||||
safeOpenBlocks.add(Material.DARK_OAK_SIGN);
|
||||
safeOpenBlocks.add(Material.DARK_OAK_WALL_SIGN);
|
||||
safeOpenBlocks.add(Material.JUNGLE_SIGN);
|
||||
safeOpenBlocks.add(Material.JUNGLE_WALL_SIGN);
|
||||
safeOpenBlocks.add(Material.OAK_SIGN);
|
||||
safeOpenBlocks.add(Material.OAK_WALL_SIGN);
|
||||
safeOpenBlocks.add(Material.SPRUCE_SIGN);
|
||||
safeOpenBlocks.add(Material.SPRUCE_WALL_SIGN);
|
||||
}
|
||||
catch (NoSuchFieldError ex) {}
|
||||
}
|
||||
|
||||
//these material IDs are ones we don't want to drop the player onto, like cactus or lava or fire or activated Ender portal
|
||||
|
@ -1,55 +0,0 @@
|
||||
package com.wimbli.WorldBorder.UUID;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
||||
/*
|
||||
* code by evilmidget38
|
||||
* from http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/
|
||||
*/
|
||||
|
||||
public class NameFetcher implements Callable<Map<UUID, String>>
|
||||
{
|
||||
private static final String PROFILE_URL = "https://sessionserver.mojang.com/session/minecraft/profile/";
|
||||
private final JSONParser jsonParser = new JSONParser();
|
||||
private final List<UUID> uuids;
|
||||
public NameFetcher(List<UUID> uuids)
|
||||
{
|
||||
this.uuids = ImmutableList.copyOf(uuids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<UUID, String> call() throws Exception
|
||||
{
|
||||
Map<UUID, String> uuidStringMap = new HashMap<UUID, String>();
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,116 +1,149 @@
|
||||
package com.wimbli.WorldBorder.UUID;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
||||
/*
|
||||
* code by evilmidget38
|
||||
* from http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/
|
||||
* slightly modified to fix name case mismatches for single name lookup
|
||||
* This code mostly taken from https://gist.github.com/Jofkos/d0c469528b032d820f42
|
||||
*/
|
||||
|
||||
public class UUIDFetcher implements Callable<Map<String, UUID>>
|
||||
{
|
||||
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<String> names;
|
||||
private final boolean rateLimiting;
|
||||
package com.wimbli.WorldBorder.UUID;
|
||||
|
||||
public UUIDFetcher(List<String> names, boolean rateLimiting)
|
||||
{
|
||||
this.names = ImmutableList.copyOf(names);
|
||||
this.rateLimiting = rateLimiting;
|
||||
}
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public UUIDFetcher(List<String> names)
|
||||
{
|
||||
this(names, true);
|
||||
}
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
public Map<String, UUID> call() throws Exception
|
||||
{
|
||||
Map<String, UUID> uuidMap = new HashMap<String, UUID>();
|
||||
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.toLowerCase(), 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();
|
||||
}
|
||||
public class UUIDFetcher {
|
||||
|
||||
/**
|
||||
* Date when name changes were introduced
|
||||
* @see UUIDFetcher#getUUIDAt(String, long)
|
||||
*/
|
||||
public static final long FEBRUARY_2015 = 1422748800000L;
|
||||
|
||||
|
||||
private static Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
|
||||
|
||||
private static final String UUID_URL = "https://api.mojang.com/users/profiles/minecraft/%s?at=%d";
|
||||
private static final String NAME_URL = "https://api.mojang.com/user/profiles/%s/names";
|
||||
|
||||
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 Map<String, UUID> uuidCache = new HashMap<String, UUID>();
|
||||
private static Map<UUID, String> nameCache = new HashMap<UUID, String>();
|
||||
|
||||
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));
|
||||
}
|
||||
private static ExecutorService pool = Executors.newCachedThreadPool();
|
||||
|
||||
private String name;
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* Fetches the uuid asynchronously and passes it to the consumer
|
||||
*
|
||||
* @param name The name
|
||||
* @param action Do what you want to do with the uuid her
|
||||
*/
|
||||
public static void getUUID(String name, Consumer<UUID> action) {
|
||||
pool.execute(() -> action.accept(getUUID(name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the uuid synchronously and returns it
|
||||
*
|
||||
* @param name The name
|
||||
* @return The uuid
|
||||
*/
|
||||
public static UUID getUUID(String name) {
|
||||
return getUUIDAt(name, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the uuid synchronously for a specified name and time and passes the result to the consumer
|
||||
*
|
||||
* @param name The name
|
||||
* @param timestamp Time when the player had this name in milliseconds
|
||||
* @param action Do what you want to do with the uuid her
|
||||
*/
|
||||
public static void getUUIDAt(String name, long timestamp, Consumer<UUID> action) {
|
||||
pool.execute(() -> action.accept(getUUIDAt(name, timestamp)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the uuid synchronously for a specified name and time
|
||||
*
|
||||
* @param name The name
|
||||
* @param timestamp Time when the player had this name in milliseconds
|
||||
* @see UUIDFetcher#FEBRUARY_2015
|
||||
*/
|
||||
public static UUID getUUIDAt(String name, long timestamp) {
|
||||
name = name.toLowerCase();
|
||||
if (uuidCache.containsKey(name)) {
|
||||
return uuidCache.get(name);
|
||||
}
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(String.format(UUID_URL, name, timestamp/1000)).openConnection();
|
||||
connection.setReadTimeout(5000);
|
||||
UUIDFetcher data = gson.fromJson(new BufferedReader(new InputStreamReader(connection.getInputStream())), UUIDFetcher.class);
|
||||
|
||||
uuidCache.put(name, data.id);
|
||||
nameCache.put(data.id, data.name);
|
||||
|
||||
public static byte[] toBytes(UUID uuid)
|
||||
{
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
|
||||
byteBuffer.putLong(uuid.getMostSignificantBits());
|
||||
byteBuffer.putLong(uuid.getLeastSignificantBits());
|
||||
return byteBuffer.array();
|
||||
}
|
||||
return data.id;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the name asynchronously and passes it to the consumer
|
||||
*
|
||||
* @param uuid The uuid
|
||||
* @param action Do what you want to do with the name her
|
||||
*/
|
||||
public static void getName(UUID uuid, Consumer<String> action) {
|
||||
pool.execute(() -> action.accept(getName(uuid)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* Fetches the name synchronously and returns it
|
||||
*
|
||||
* @param uuid The uuid
|
||||
* @return The name
|
||||
*/
|
||||
public static String getName(UUID uuid) {
|
||||
if (nameCache.containsKey(uuid)) {
|
||||
return nameCache.get(uuid);
|
||||
}
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(String.format(NAME_URL, UUIDTypeAdapter.fromUUID(uuid))).openConnection();
|
||||
connection.setReadTimeout(5000);
|
||||
UUIDFetcher[] nameHistory = gson.fromJson(new BufferedReader(new InputStreamReader(connection.getInputStream())), UUIDFetcher[].class);
|
||||
UUIDFetcher currentNameData = nameHistory[nameHistory.length - 1];
|
||||
|
||||
public static UUID getUUIDOf(String name) throws Exception
|
||||
{
|
||||
return new UUIDFetcher(Arrays.asList(name)).call().get(name.toLowerCase());
|
||||
}
|
||||
uuidCache.put(currentNameData.name.toLowerCase(), uuid);
|
||||
nameCache.put(uuid, currentNameData.name);
|
||||
|
||||
return currentNameData.name;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Map<UUID, String> getNameList(ArrayList<UUID> uuids) {
|
||||
Map<UUID, String> uuidStringMap = new HashMap<>();
|
||||
for (UUID uuid: uuids)
|
||||
{
|
||||
uuidStringMap.put(uuid, getName(uuid));
|
||||
}
|
||||
return uuidStringMap;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This code from: https://github.com/eitetu/minecraft-server/blob/master/src/main/java/com/eitetu/minecraft/server/util/UUIDTypeAdapter.java
|
||||
*/
|
||||
|
||||
package com.wimbli.WorldBorder.UUID;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
|
||||
public class UUIDTypeAdapter extends TypeAdapter<UUID> {
|
||||
public void write(JsonWriter out, UUID value) throws IOException {
|
||||
out.value(fromUUID(value));
|
||||
}
|
||||
|
||||
public UUID read(JsonReader in) throws IOException {
|
||||
return fromString(in.nextString());
|
||||
}
|
||||
|
||||
public static String fromUUID(UUID value) {
|
||||
return value.toString().replace("-", "");
|
||||
}
|
||||
|
||||
public static UUID fromString(String input) {
|
||||
return UUID.fromString(input.replaceFirst(
|
||||
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.event.player.PlayerPortalEvent;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.Location;
|
||||
|
||||
|
||||
public class WBListener implements Listener
|
||||
@ -73,21 +73,21 @@ public class WBListener implements Listener
|
||||
|
||||
/*
|
||||
* Check if there is a fill task running, and if yes, if it's for the
|
||||
* world that the unload event refers to and if the chunk should be
|
||||
* kept in memory because generation still needs it.
|
||||
* world that the unload event refers to, set "force loaded" flag off
|
||||
* and track if chunk was somehow on unload prevention list
|
||||
*/
|
||||
@EventHandler
|
||||
public void onChunkUnload(ChunkUnloadEvent e)
|
||||
{
|
||||
if (Config.fillTask!=null)
|
||||
{
|
||||
Chunk chunk=e.getChunk();
|
||||
if (e.getWorld() == Config.fillTask.getWorld()
|
||||
&& Config.fillTask.chunkOnUnloadPreventionList(chunk.getX(), chunk.getZ()))
|
||||
{
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
if (Config.fillTask == null)
|
||||
return;
|
||||
|
||||
Chunk chunk = e.getChunk();
|
||||
if (e.getWorld() != Config.fillTask.getWorld())
|
||||
return;
|
||||
|
||||
// just to be on the safe side, in case it's still set at this point somehow
|
||||
chunk.setForceLoaded(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -247,7 +247,10 @@ public class WorldFillTask implements Runnable
|
||||
for (CoordXZ unload: chunksToUnload)
|
||||
{
|
||||
if (!chunkOnUnloadPreventionList(unload.x, unload.z))
|
||||
{
|
||||
world.setChunkForceLoaded(unload.x, unload.z, false);
|
||||
world.unloadChunkRequest(unload.x, unload.z);
|
||||
}
|
||||
}
|
||||
|
||||
// Put some damper on chunksPerRun. We don't want the queue to be too
|
||||
@ -312,6 +315,7 @@ public class WorldFillTask implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
world.setChunkForceLoaded(x, z, true); // toggle "force loaded" flag on for chunk to prevent it from being unloaded while we need it
|
||||
pendingChunks.put(PaperLib.getChunkAtAsync(world, x, z, true), new CoordXZ(x, z));
|
||||
|
||||
// There need to be enough nearby chunks loaded to make the server populate a chunk with trees, snow, etc.
|
||||
@ -319,10 +323,12 @@ public class WorldFillTask implements Runnable
|
||||
int popX = !isZLeg ? x : (x + (isNeg ? -1 : 1));
|
||||
int popZ = isZLeg ? z : (z + (!isNeg ? -1 : 1));
|
||||
|
||||
world.setChunkForceLoaded(popX, popZ, true);
|
||||
pendingChunks.put(PaperLib.getChunkAtAsync(world, popX, popZ, false), new CoordXZ(popX, popZ));
|
||||
preventUnload.add(new UnloadDependency(popX, popZ, x, z));
|
||||
|
||||
// make sure the previous chunk in our spiral is loaded as well (might have already existed and been skipped over)
|
||||
world.setChunkForceLoaded(lastChunk.x, lastChunk.z, true);
|
||||
pendingChunks.put(PaperLib.getChunkAtAsync(world, lastChunk.x, lastChunk.z, false), new CoordXZ(lastChunk.x, lastChunk.z)); // <-- new CoordXZ as lastChunk isn't immutable
|
||||
preventUnload.add(new UnloadDependency(lastChunk.x, lastChunk.z, x, z));
|
||||
|
||||
@ -437,13 +443,17 @@ public class WorldFillTask implements Runnable
|
||||
server = null;
|
||||
|
||||
// go ahead and unload any chunks we still have loaded
|
||||
// Set preventUnload to emptry first so the ChunkUnloadEvent Listener
|
||||
// Set preventUnload to empty first so the ChunkUnloadEvent Listener
|
||||
// doesn't get in our way
|
||||
Set<UnloadDependency> tempPreventUnload = preventUnload;
|
||||
preventUnload = null;
|
||||
for (UnloadDependency entry: tempPreventUnload)
|
||||
if (preventUnload != null)
|
||||
{
|
||||
world.unloadChunkRequest(entry.neededX, entry.neededZ);
|
||||
Set<UnloadDependency> tempPreventUnload = preventUnload;
|
||||
preventUnload = null;
|
||||
for (UnloadDependency entry: tempPreventUnload)
|
||||
{
|
||||
world.setChunkForceLoaded(entry.neededX, entry.neededZ, false);
|
||||
world.unloadChunkRequest(entry.neededX, entry.neededZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,6 +363,10 @@ public class WorldTrimTask implements Runnable
|
||||
if (taskID != -1)
|
||||
server.getScheduler().cancelTask(taskID);
|
||||
server = null;
|
||||
|
||||
sendMessage("NOTICE: it is recommended that you restart your server after a Trim, to be on the safe side.");
|
||||
if (DynMapFeatures.renderEnabled())
|
||||
sendMessage("This especially true with DynMap. You should also run a fullrender in DynMap for the trimmed world after restarting, so trimmed chunks are updated on the map.");
|
||||
}
|
||||
|
||||
// is this task still valid/workable?
|
||||
|
@ -65,7 +65,7 @@ public class CmdBypass extends WBCmd
|
||||
// only do UUID lookup using Mojang server if specified player isn't online
|
||||
try
|
||||
{
|
||||
uPlayer = UUIDFetcher.getUUIDOf(sPlayer);
|
||||
uPlayer = UUIDFetcher.getUUID(sPlayer);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.wimbli.WorldBorder.*;
|
||||
import com.wimbli.WorldBorder.UUID.NameFetcher;
|
||||
import com.wimbli.WorldBorder.UUID.UUIDFetcher;
|
||||
|
||||
|
||||
public class CmdBypasslist extends WBCmd
|
||||
@ -42,8 +42,7 @@ public class CmdBypasslist extends WBCmd
|
||||
{
|
||||
try
|
||||
{
|
||||
NameFetcher fetcher = new NameFetcher(uuids);
|
||||
Map<UUID, String> names = fetcher.call();
|
||||
Map<UUID, String> names = UUIDFetcher.getNameList(uuids);
|
||||
String nameString = names.values().toString();
|
||||
|
||||
sender.sendMessage("Players with border bypass enabled: " + nameString.substring(1, nameString.length() - 1));
|
||||
|
Loading…
Reference in New Issue
Block a user