UUID Migration Rework, Bug Fixes

Using multi-faceted strategy to better resolve UUIDs, and do it faster.
Fixed dispensers putting fluids in a neighboring claim.
Automatically deleting claims for worlds which no longer exist.
Streamlined visualization code, hopefully will reduce or eliminate weird
visualizations for VERY big land claims.
Removed option to disallow un-claiming land in creative mode.
Better default for last login date for new players or players who've had
their data deleted or lost.
This commit is contained in:
ryanhamshire 2014-10-08 19:32:43 -07:00
parent bf4d5ee633
commit 29a2b8e17b
9 changed files with 309 additions and 238 deletions

View File

@ -51,6 +51,7 @@ import org.bukkit.event.world.StructureGrowEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.material.Dispenser;
import org.bukkit.util.Vector;
//event handlers related to blocks
@ -763,24 +764,10 @@ public class BlockEventHandler implements Listener
{
//from where?
Block fromBlock = dispenseEvent.getBlock();
Dispenser dispenser = new Dispenser(fromBlock.getType(), fromBlock.getData());
//to where?
Vector velocity = dispenseEvent.getVelocity();
int xChange = 0;
int zChange = 0;
if(Math.abs(velocity.getX()) > Math.abs(velocity.getZ()))
{
if(velocity.getX() > 0) xChange = 1;
else xChange = -1;
}
else
{
if(velocity.getZ() > 0) zChange = 1;
else zChange = -1;
}
Block toBlock = fromBlock.getRelative(xChange, 0, zChange);
Block toBlock = fromBlock.getRelative(dispenser.getFacing());
Claim fromClaim = this.dataStore.getClaimAt(fromBlock.getLocation(), false, null);
Claim toClaim = this.dataStore.getClaimAt(toBlock.getLocation(), false, fromClaim);

View File

@ -95,6 +95,7 @@ public class DatabaseDataStore extends DataStore
{
GriefPrevention.AddLogEntry("ERROR: Unable to create the necessary database table. Details:");
GriefPrevention.AddLogEntry(e3.getMessage());
e3.printStackTrace();
throw e3;
}
@ -133,6 +134,85 @@ public class DatabaseDataStore extends DataStore
this.nextClaimID = results.getLong("nextid");
}
if(this.getSchemaVersion() == 0)
{
try
{
this.refreshDataConnection();
//pull ALL player data from the database
statement = this.databaseConnection.createStatement();
results = statement.executeQuery("SELECT * FROM griefprevention_playerdata;");
//make a list of changes to be made
HashMap<String, UUID> changes = new HashMap<String, UUID>();
ArrayList<String> namesToConvert = new ArrayList<String>();
while(results.next())
{
//get the id
String playerName = results.getString("name");
//ignore groups
if(playerName.startsWith("$")) continue;
//add to list of names to convert to UUID
namesToConvert.add(playerName);
}
//resolve and cache as many as possible through various means
try
{
UUIDFetcher fetcher = new UUIDFetcher(namesToConvert);
fetcher.call();
}
catch(Exception e)
{
GriefPrevention.AddLogEntry("Failed to resolve a batch of names to UUIDs. Details:" + e.getMessage());
e.printStackTrace();
}
//reset results cursor
results.beforeFirst();
//for each result
while(results.next())
{
//get the id
String playerName = results.getString("name");
//ignore groups
if(playerName.startsWith("$")) continue;
//try to convert player name to UUID
try
{
UUID playerID = UUIDFetcher.getUUIDOf(playerName);
//if successful, update the playerdata row by replacing the player's name with the player's UUID
if(playerID != null)
{
changes.put(playerName, playerID);
}
}
//otherwise leave it as-is. no harm done - it won't be requested by name, and this update only happens once.
catch(Exception ex){ }
}
for(String name : changes.keySet())
{
statement = this.databaseConnection.createStatement();
statement.execute("UPDATE griefprevention_playerdata SET name = '" + changes.get(name).toString() + "' WHERE name = '" + name + "';");
}
}
catch(SQLException e)
{
GriefPrevention.AddLogEntry("Unable to convert player data. Details:");
GriefPrevention.AddLogEntry(e.getMessage());
e.printStackTrace();
}
}
//load claims data into memory
results = statement.executeQuery("SELECT * FROM griefprevention_claimdata;");
@ -148,11 +228,30 @@ public class DatabaseDataStore extends DataStore
long claimID = results.getLong("id");
String lesserCornerString = results.getString("lessercorner");
Location lesserBoundaryCorner = this.locationFromString(lesserCornerString);
String greaterCornerString = results.getString("greatercorner");
Location greaterBoundaryCorner = this.locationFromString(greaterCornerString);
Location lesserBoundaryCorner = null;
Location greaterBoundaryCorner = null;
try
{
String lesserCornerString = results.getString("lessercorner");
lesserBoundaryCorner = this.locationFromString(lesserCornerString);
String greaterCornerString = results.getString("greatercorner");
greaterBoundaryCorner = this.locationFromString(greaterCornerString);
}
catch(Exception e)
{
if(e.getMessage().contains("World not found"))
{
Claim claim = new Claim();
claim.id = claimID;
claimsToRemove.add(claim);
continue;
}
else
{
throw e;
}
}
String ownerName = results.getString("owner");
UUID ownerID = null;
@ -166,7 +265,11 @@ public class DatabaseDataStore extends DataStore
{
ownerID = UUIDFetcher.getUUIDOf(ownerName);
}
catch(Exception ex){ } //if UUID not found, use NULL
catch(Exception ex)
{
GriefPrevention.AddLogEntry("This owner name did not convert to aUUID: " + ownerName + ".");
GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString());
}
}
else
{
@ -221,10 +324,10 @@ public class DatabaseDataStore extends DataStore
while(childResults.next())
{
lesserCornerString = childResults.getString("lessercorner");
String lesserCornerString = childResults.getString("lessercorner");
lesserBoundaryCorner = this.locationFromString(lesserCornerString);
greaterCornerString = childResults.getString("greatercorner");
String greaterCornerString = childResults.getString("greatercorner");
greaterBoundaryCorner = this.locationFromString(greaterCornerString);
buildersString = childResults.getString("builders");
@ -248,7 +351,7 @@ public class DatabaseDataStore extends DataStore
//add this claim to the list of children of the current top level claim
childClaim.parent = topLevelClaim;
topLevelClaim.children.add(childClaim);
childClaim.inDataStore = true;
childClaim.inDataStore = true;
}
}
catch(SQLException e)
@ -263,57 +366,6 @@ public class DatabaseDataStore extends DataStore
this.deleteClaimFromSecondaryStorage(claimsToRemove.get(i));
}
if(this.getSchemaVersion() == 0)
{
try
{
this.refreshDataConnection();
//pull ALL player data from the database
statement = this.databaseConnection.createStatement();
results = statement.executeQuery("SELECT * FROM griefprevention_playerdata;");
//make a list of changes to be made
HashMap<String, UUID> changes = new HashMap<String, UUID>();
//for each result
while(results.next())
{
//get the id
String playerName = results.getString("name");
//ignore groups
if(playerName.startsWith("$")) continue;
//try to convert player name to UUID
try
{
UUID playerID = UUIDFetcher.getUUIDOf(playerName);
//if successful, update the playerdata row by replacing the player's name with the player's UUID
if(playerID != null)
{
changes.put(playerName, playerID);
}
}
//otherwise leave it as-is. no harm done - it won't be requested by name, and this update only happens once.
catch(Exception ex){ }
}
for(String name : changes.keySet())
{
statement = this.databaseConnection.createStatement();
statement.execute("UPDATE griefprevention_playerdata SET name = '" + changes.get(name).toString() + "' WHERE name = '" + name + "';");
}
}
catch(SQLException e)
{
GriefPrevention.AddLogEntry("Unable to convert player data. Details:");
GriefPrevention.AddLogEntry(e.getMessage());
e.printStackTrace();
}
}
super.initialize();
}
@ -441,8 +493,9 @@ public class DatabaseDataStore extends DataStore
}
catch(SQLException e)
{
GriefPrevention.AddLogEntry("Unable to delete data for claim at " + this.locationToString(claim.lesserBoundaryCorner) + ". Details:");
GriefPrevention.AddLogEntry("Unable to delete data for claim " + claim.id + ". Details:");
GriefPrevention.AddLogEntry(e.getMessage());
e.printStackTrace();
}
}

View File

@ -127,6 +127,65 @@ public class FlatFileDataStore extends DataStore
catch(IOException exception) {}
}
//if converting up from schema version 0, rename player data files using UUIDs instead of player names
//get a list of all the files in the claims data folder
if(this.getSchemaVersion() == 0)
{
files = playerDataFolder.listFiles();
ArrayList<String> namesToConvert = new ArrayList<String>();
for(File playerFile : files)
{
//anything starting with an underscore or dollar sign isn't a player, ignore those
String currentFilename = playerFile.getName();
if(currentFilename.startsWith("$") || currentFilename.startsWith("_")) continue;
namesToConvert.add(currentFilename);
}
//resolve and cache as many as possible through various means
try
{
UUIDFetcher fetcher = new UUIDFetcher(namesToConvert);
fetcher.call();
}
catch(Exception e)
{
GriefPrevention.AddLogEntry("Failed to resolve a batch of names to UUIDs. Details:" + e.getMessage());
e.printStackTrace();
}
//rename files
for(File playerFile : files)
{
//anything starting with an underscore or dollar sign isn't a player, ignore those
String currentFilename = playerFile.getName();
if(currentFilename.startsWith("$") || currentFilename.startsWith("_")) continue;
//try to convert player name to UUID
UUID playerID = null;
try
{
playerID = UUIDFetcher.getUUIDOf(currentFilename);
//if successful, rename the file using the UUID
if(playerID != null)
{
playerFile.renameTo(new File(playerDataFolder, playerID.toString()));
}
//otherwise hide it from the data store for the future
else
{
playerFile.renameTo(new File(playerDataFolder, "__" + currentFilename));
}
}
catch(Exception ex)
{
playerFile.renameTo(new File(playerDataFolder, "__" + currentFilename));
}
}
}
//load claims data into memory
//get a list of all the files in the claims data folder
files = claimDataFolder.listFiles();
@ -201,7 +260,11 @@ public class FlatFileDataStore extends DataStore
{
ownerID = UUIDFetcher.getUUIDOf(ownerName);
}
catch(Exception ex){ } //if UUID not found, use NULL
catch(Exception ex)
{
GriefPrevention.AddLogEntry("Couldn't resolve this name to a UUID: " + ownerName + ".");
GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString());
}
}
else
{
@ -290,7 +353,14 @@ public class FlatFileDataStore extends DataStore
//if there's any problem with the file's content, log an error message and skip it
catch(Exception e)
{
GriefPrevention.AddLogEntry("Unable to load data for claim \"" + files[i].getName() + "\": " + e.toString());
if(e.getMessage().contains("World not found"))
{
files[i].delete();
}
else
{
GriefPrevention.AddLogEntry("Unable to load data for claim \"" + files[i].getName() + "\": " + e.toString());
}
}
try
@ -301,42 +371,6 @@ public class FlatFileDataStore extends DataStore
}
}
//if converting up from schema version 0, rename files using UUIDs instead of player names
//get a list of all the files in the claims data folder
if(this.getSchemaVersion() == 0)
{
files = playerDataFolder.listFiles();
for(File playerFile : files)
{
//anything starting with an underscore or dollar sign isn't a player, ignore those
String currentFilename = playerFile.getName();
if(currentFilename.startsWith("$") || currentFilename.startsWith("_")) continue;
//try to convert player name to UUID
UUID playerID = null;
try
{
playerID = UUIDFetcher.getUUIDOf(currentFilename);
//if successful, rename the file using the UUID
if(playerID != null)
{
playerFile.renameTo(new File(playerDataFolder, playerID.toString()));
}
//otherwise hide it from the data store for the future
else
{
playerFile.renameTo(new File(playerDataFolder, "__" + currentFilename));
}
}
catch(Exception ex)
{
playerFile.renameTo(new File(playerDataFolder, "__" + currentFilename));
}
}
}
super.initialize();
}

View File

@ -83,7 +83,6 @@ public class GriefPrevention extends JavaPlugin
public boolean config_claims_creationRequiresPermission; //whether creating claims with the shovel requires a permission
public int config_claims_claimsExtendIntoGroundDistance; //how far below the shoveled block a new claim will reach
public int config_claims_minSize; //minimum width and height for non-admin claims
public boolean config_claims_allowUnclaimInCreative; //whether players may unclaim land (resize or abandon) in creative mode
public boolean config_claims_autoRestoreUnclaimedCreativeLand; //whether unclaimed land in creative worlds is automatically /restorenature-d
public boolean config_claims_noBuildOutsideClaims; //whether players can build in survival worlds outside their claimed areas
@ -311,7 +310,6 @@ public class GriefPrevention extends JavaPlugin
this.config_claims_trappedCooldownHours = config.getInt("GriefPrevention.Claims.TrappedCommandCooldownHours", 8);
this.config_claims_noBuildOutsideClaims = config.getBoolean("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", false);
this.config_claims_warnOnBuildOutside = config.getBoolean("GriefPrevention.Claims.WarnWhenBuildingOutsideClaims", true);
this.config_claims_allowUnclaimInCreative = config.getBoolean("GriefPrevention.Claims.AllowUnclaimingCreativeModeLand", true);
this.config_claims_autoRestoreUnclaimedCreativeLand = config.getBoolean("GriefPrevention.Claims.AutoRestoreUnclaimedCreativeLand", true);
this.config_claims_chestClaimExpirationDays = config.getInt("GriefPrevention.Claims.Expiration.ChestClaimDays", 7);
@ -571,7 +569,6 @@ public class GriefPrevention extends JavaPlugin
outConfig.set("GriefPrevention.Claims.ModificationTool", this.config_claims_modificationTool.name());
outConfig.set("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", this.config_claims_noBuildOutsideClaims);
outConfig.set("GriefPrevention.Claims.WarnWhenBuildingOutsideClaims", this.config_claims_warnOnBuildOutside);
outConfig.set("GriefPrevention.Claims.AllowUnclaimingCreativeModeLand", this.config_claims_allowUnclaimInCreative);
outConfig.set("GriefPrevention.Claims.AutoRestoreUnclaimedCreativeLand", this.config_claims_autoRestoreUnclaimedCreativeLand);
outConfig.set("GriefPrevention.Spam.Enabled", this.config_spam_enabled);
@ -841,12 +838,6 @@ public class GriefPrevention extends JavaPlugin
{
if(args.length != 0) return false;
if(!GriefPrevention.instance.config_claims_allowUnclaimInCreative && creativeRulesApply(player.getLocation()))
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoCreativeUnClaim);
return true;
}
//count claims
PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId());
int originalClaimCount = playerData.claims.size();
@ -1936,12 +1927,6 @@ public class GriefPrevention extends JavaPlugin
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NotYourClaim);
}
//don't allow abandon of creative mode claims
else if(!GriefPrevention.instance.config_claims_allowUnclaimInCreative && this.creativeRulesApply(player.getLocation()))
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoCreativeUnClaim);
}
//warn if has children and we're not explicitly deleting a top level claim
else if(claim.children.size() > 0 && !deleteTopLevelClaim)
{
@ -2200,8 +2185,6 @@ public class GriefPrevention extends JavaPlugin
}
//if none found
GriefPrevention.AddLogEntry("Error: Failed to find a local player with UUID: " + playerID.toString());
new Exception().printStackTrace();
return "someone";
}
@ -2242,15 +2225,15 @@ public class GriefPrevention extends JavaPlugin
//called when a player spawns, applies protection for that player if necessary
public void checkPvpProtectionNeeded(Player player)
{
//if pvp is disabled, do nothing
//if anti spawn camping feature is not enabled, do nothing
if(!this.config_pvp_protectFreshSpawns) return;
//if pvp is disabled, do nothing
if(!this.config_pvp_enabledWorlds.contains(player.getWorld())) return;
//if player is in creative mode, do nothing
if(player.getGameMode() == GameMode.CREATIVE) return;
//if anti spawn camping feature is not enabled, do nothing
if(!this.config_pvp_protectFreshSpawns) return;
//if the player has the damage any player permission enabled, do nothing
if(player.hasPermission("griefprevention.nopvpimmunity")) return;

View File

@ -117,13 +117,16 @@ public class PlayerData
PlayerData()
{
//default last login date value to a year ago to ensure a brand new player can log in
//default last login date value to 5 minutes ago to ensure a brand new player can log in
//see login cooldown feature, PlayerEventHandler.onPlayerLogin()
//if the player successfully logs in, this value will be overwritten with the current date and time
Calendar lastYear = Calendar.getInstance();
lastYear.add(Calendar.YEAR, -1);
this.lastLogin = lastYear.getTime();
this.lastTrappedUsage = lastYear.getTime();
Calendar fiveMinutesBack = Calendar.getInstance();
fiveMinutesBack.add(Calendar.MINUTE, -5);
this.lastLogin = fiveMinutesBack.getTime();
//default last trapped usage to 5 hours ago, so the command is available right away
fiveMinutesBack.add(Calendar.HOUR, -5);
this.lastTrappedUsage = fiveMinutesBack.getTime();
}
//whether or not this player is "in" pvp combat

View File

@ -481,7 +481,6 @@ class PlayerEventHandler implements Listener
PlayerData playerData = this.dataStore.getPlayerData(playerID);
playerData.lastSpawn = now;
playerData.lastLogin = new Date();
this.dataStore.savePlayerData(playerID, playerData);
//if player has never played on the server before, may need pvp protection
if(!player.hasPlayedBefore())
@ -1558,9 +1557,8 @@ class PlayerEventHandler implements Listener
}
}
//special rules for making a top-level claim smaller. to check this, verifying the old claim's corners are inside the new claim's boundaries.
//rule1: in creative mode, top-level claims can't be moved or resized smaller.
//rule2: in any mode, shrinking a claim removes any surface fluids
//special rule for making a top-level claim smaller. to check this, verifying the old claim's corners are inside the new claim's boundaries.
//rule: in any mode, shrinking a claim removes any surface fluids
Claim oldClaim = playerData.claimResizing;
boolean smaller = false;
if(oldClaim.parent == null)
@ -1576,13 +1574,6 @@ class PlayerEventHandler implements Listener
{
smaller = true;
//enforce creative mode rule
if(!GriefPrevention.instance.config_claims_allowUnclaimInCreative && !player.hasPermission("griefprevention.deleteclaims") && GriefPrevention.instance.creativeRulesApply(player.getLocation()))
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoCreativeUnClaim);
return;
}
//remove surface fluids about to be unclaimed
oldClaim.removeSurfaceFluids(newClaim);
}

View File

@ -18,7 +18,7 @@ import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.Callable;
class UUIDFetcher implements Callable<Map<String, UUID>> {
class UUIDFetcher {
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();
@ -29,7 +29,7 @@ class UUIDFetcher implements Callable<Map<String, UUID>> {
private static HashMap<String, UUID> lookupCache;
public UUIDFetcher(List<String> names, boolean rateLimiting) {
this.names = ImmutableList.copyOf(names);
this.names = names;
this.rateLimiting = rateLimiting;
}
@ -37,26 +37,98 @@ class UUIDFetcher implements Callable<Map<String, UUID>> {
this(names, true);
}
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, uuid);
}
if (rateLimiting && i != requests - 1) {
Thread.sleep(100L);
public void call() throws Exception
{
if(lookupCache == null)
{
lookupCache = new HashMap<String, UUID>();
}
GriefPrevention.AddLogEntry("UUID conversion process started. Please be patient - this may take a while.");
//try to get correct casing from local data
OfflinePlayer [] players = GriefPrevention.instance.getServer().getOfflinePlayers();
GriefPrevention.AddLogEntry("Checking local server data to get correct casing for player names...");
for(int i = 0; i < names.size(); i++)
{
String name = names.get(i);
for(OfflinePlayer player : players)
{
if(player.getName().equalsIgnoreCase(name))
{
if(!player.getName().equals(name))
{
GriefPrevention.AddLogEntry(name + " --> " + player.getName());
names.set(i, player.getName());
continue;
}
}
}
}
//look for local data first
GriefPrevention.AddLogEntry("Checking local server data for UUIDs already seen...");
for(int i = 0; i < names.size(); i++)
{
String name = names.get(i);
for(OfflinePlayer player : players)
{
if(player.getName().equalsIgnoreCase(name))
{
UUID uuid = player.getUniqueId();
if(uuid != null)
{
GriefPrevention.AddLogEntry(name + " --> " + uuid.toString());
lookupCache.put(name, uuid);
lookupCache.put(name.toLowerCase(), uuid);
names.remove(i--);
continue;
}
}
}
}
//for online mode, call Mojang to resolve the rest
if(GriefPrevention.instance.getServer().getOnlineMode())
{
GriefPrevention.AddLogEntry("Calling Mojang to get UUIDs for remaining unresolved players (this is the slowest step)...");
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);
GriefPrevention.AddLogEntry(name + " --> " + uuid.toString());
lookupCache.put(name, uuid);
lookupCache.put(name.toLowerCase(), uuid);
}
if (rateLimiting && i != requests - 1) {
Thread.sleep(200L);
}
}
}
//for offline mode, generate UUIDs for the rest
else
{
GriefPrevention.AddLogEntry("Generating offline mode UUIDs for remaining unresolved players...");
for(int i = 0; i < names.size(); i++)
{
String name = names.get(i);
UUID uuid = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
GriefPrevention.AddLogEntry(name + " --> " + uuid.toString());
lookupCache.put(name, uuid);
lookupCache.put(name.toLowerCase(), uuid);
}
}
return uuidMap;
}
private static void writeBody(HttpURLConnection connection, String body) throws Exception {
@ -100,76 +172,14 @@ class UUIDFetcher implements Callable<Map<String, UUID>> {
public static UUID getUUIDOf(String name) throws Exception
{
if(lookupCache == null)
{
lookupCache = new HashMap<String, UUID>();
}
UUID result = lookupCache.get(name);
if(result == null)
{
if(lookupCache.containsKey(name)) return null;
//use local minecraft player data to try correcting a name to the correct casing
String correctCasingName = getNameWithCasing(name);
//online mode: look it up by calling Mojang's web service
if(GriefPrevention.instance.getServer().getOnlineMode() == true)
{
result = new UUIDFetcher(Arrays.asList(name)).call().get(correctCasingName);
}
//offline mode best guess
else
{
//search server's minecraft player data to find a UUID
OfflinePlayer [] players = GriefPrevention.instance.getServer().getOfflinePlayers();
for(OfflinePlayer player : players)
{
if(player.getName().equals(correctCasingName))
{
result = player.getUniqueId();
break;
}
}
//if that doesn't work, make a wild guess by imitating what Mojang reportedly does
if(result == null)
{
result = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + correctCasingName).getBytes(Charsets.UTF_8));
}
}
//if none of the above worked, throw up our hands and report the problem in the logs
//throw up our hands and report the problem in the logs
//this player will lose his land claim blocks, but claims will stay in place as admin claims
if(result == null)
{
GriefPrevention.AddLogEntry(correctCasingName + " --> ???");
lookupCache.put(name, null);
throw new IllegalArgumentException(name);
}
GriefPrevention.AddLogEntry(correctCasingName + " --> " + result.toString());
lookupCache.put(name, result);
throw new IllegalArgumentException(name);
}
return result;
}
private static String getNameWithCasing(String name)
{
OfflinePlayer [] players = GriefPrevention.instance.getServer().getOfflinePlayers();
for(OfflinePlayer player : players)
{
if(player.getName().equalsIgnoreCase(name))
{
if(!player.getName().equals(name))
{
GriefPrevention.AddLogEntry(name + " --> " + player.getName());
}
return player.getName();
}
}
return name;
return result;
}
}

View File

@ -66,6 +66,8 @@ public class Visualization
for(int i = 0; i < visualization.elements.size(); i++)
{
VisualizationElement element = visualization.elements.get(i);
if(!element.location.getChunk().isLoaded()) continue;
if(element.location.distanceSquared(player.getLocation()) > 10000) continue;
Block block = element.location.getBlock();
player.sendBlockChange(element.location, block.getType(), block.getData());
}
@ -198,7 +200,14 @@ public class Visualization
//finds a block the player can probably see. this is how visualizations "cling" to the ground or ceiling
private static Location getVisibleLocation(World world, int x, int y, int z)
{
Block block = world.getBlockAt(x, y, z);
//cheap distance check - also avoids loading chunks just for a big visualization
Location location = new Location(world, x, y, z);
if(!location.getChunk().isLoaded())
{
return location;
}
Block block = world.getBlockAt(x, y, z);
BlockFace direction = (isTransparent(block)) ? BlockFace.DOWN : BlockFace.UP;
while( block.getY() >= 1 &&

View File

@ -43,6 +43,7 @@ class VisualizationApplicationTask implements Runnable
VisualizationElement element = visualization.elements.get(i);
//send the player a fake block change event
if(!element.location.getChunk().isLoaded()) continue; //cheap distance check
player.sendBlockChange(element.location, element.visualizedMaterial, element.visualizedData);
}