Improve extended names, normal player disguises now use teams instead of actual player names to better support names changing at will

Added setName to PlayerDisguise disguise options in command
This commit is contained in:
libraryaddict 2020-03-30 20:22:33 +13:00
parent ab5cb8a1f1
commit 901c6ef06f
No known key found for this signature in database
GPG Key ID: 052E4FBCD257AEA4
8 changed files with 292 additions and 197 deletions

@ -221,6 +221,9 @@ public class DisguiseConfig {
@Setter
private static BarColor bossBarColor = BarColor.GREEN;
private static PermissionDefault commandVisibility = PermissionDefault.TRUE;
@Getter
@Setter
private static boolean scoreboardDisguiseNames;
public static PermissionDefault getCommandVisibility() {
return commandVisibility;
@ -432,6 +435,7 @@ public class DisguiseConfig {
setWarnScoreboardConflict(config.getBoolean("Scoreboard.WarnConflict"));
setWitherSkullPacketsEnabled(config.getBoolean("PacketsEnabled.WitherSkull"));
setWolfDyeable(config.getBoolean("DyeableWolf"));
setScoreboardDisguiseNames(config.getBoolean("ScoreboardNames"));
if (!LibsPremium.isPremium() && (isSavePlayerDisguises() || isSaveEntityDisguises())) {
DisguiseUtilities.getLogger().warning("You must purchase the plugin to use saved disguises!");

@ -443,7 +443,7 @@ public class DisguiseListener implements Listener {
DisguiseUtilities.registerNoName(event.getPlayer().getScoreboard());
if (event.getPlayer().getScoreboard() != Bukkit.getScoreboardManager().getMainScoreboard()) {
DisguiseUtilities.registerExtendedNames(event.getPlayer().getScoreboard());
DisguiseUtilities.registerAllExtendedNames(event.getPlayer().getScoreboard());
}
}
}.runTaskLater(LibsDisguises.getInstance(), 20);

@ -29,13 +29,14 @@ import java.util.UUID;
public class PlayerDisguise extends TargetedDisguise {
private transient LibsProfileLookup currentLookup;
private WrappedGameProfile gameProfile;
private String playerName;
private String playerName = "Herobrine";
private String skinToUse;
private boolean nameVisible = true;
private UUID uuid = UUID.randomUUID();
@Getter
@Setter
private boolean dynamicName;
private DisguiseUtilities.DisguiseTeam scoreboardName;
private PlayerDisguise() {
super(DisguiseType.PLAYER);
@ -84,12 +85,25 @@ public class PlayerDisguise extends TargetedDisguise {
createDisguise();
}
public boolean hasExtendedName() {
return getName().length() > 16;
public DisguiseUtilities.DisguiseTeam getScoreboardName() {
if (getName().length() <= 16 ? !DisguiseConfig.isScoreboardDisguiseNames() :
!DisguiseConfig.isExtendedDisguiseNames()) {
throw new IllegalStateException("Cannot use this method when it's been disabled in config!");
}
if (scoreboardName == null) {
scoreboardName = DisguiseUtilities.createExtendedName(getName());
}
return scoreboardName;
}
public boolean hasScoreboardName() {
return scoreboardName != null || DisguiseConfig.isScoreboardDisguiseNames() || getName().length() > 16;
}
public String getProfileName() {
return hasExtendedName() ? DisguiseUtilities.getExtendedName(getName()).getSplit()[1] : getName();
return hasScoreboardName() ? getScoreboardName().getPlayer() : getName();
}
public UUID getUUID() {
@ -106,14 +120,19 @@ public class PlayerDisguise extends TargetedDisguise {
}
if (isDisguiseInUse()) {
if (stopDisguise()) {
this.nameVisible = nameVisible;
if (!DisguiseConfig.isScoreboardDisguiseNames()) {
if (stopDisguise()) {
this.nameVisible = nameVisible;
if (!startDisguise()) {
if (!startDisguise()) {
throw new IllegalStateException("Unable to restart disguise");
}
} else {
throw new IllegalStateException("Unable to restart disguise");
}
} else {
throw new IllegalStateException("Unable to restart disguise");
this.nameVisible = nameVisible;
DisguiseUtilities.updateExtendedName(this);
}
} else {
this.nameVisible = nameVisible;
@ -194,31 +213,60 @@ public class PlayerDisguise extends TargetedDisguise {
name = name.substring(0, 16);
}
if (isDisguiseInUse() && isNameVisible()) {
if (stopDisguise()) {
if (getName().isEmpty() && !name.isEmpty()) {
setNameVisible(true);
} else if (name.isEmpty()) {
setNameVisible(false);
}
if (isDisguiseInUse()) {
boolean resendDisguise = !DisguiseConfig.isScoreboardDisguiseNames();
playerName = name;
if (hasScoreboardName()) {
DisguiseUtilities.DisguiseTeam team = getScoreboardName();
String[] split = DisguiseUtilities.getExtendedNameSplit(team.getPlayer(), name);
if (gameProfile != null) {
gameProfile = ReflectionManager
.getGameProfileWithThisSkin(uuid, getProfileName(), getGameProfile());
}
resendDisguise = !split[1].equals(team.getPlayer());
team.setSplit(split);
}
if (!startDisguise()) {
if (resendDisguise) {
if (stopDisguise()) {
if (getName().isEmpty() && !name.isEmpty()) {
setNameVisible(true);
} else if (!getName().isEmpty() && name.isEmpty()) {
setNameVisible(false);
}
playerName = name;
if (gameProfile != null) {
gameProfile = ReflectionManager
.getGameProfileWithThisSkin(uuid, getProfileName(), getGameProfile());
}
if (!startDisguise()) {
throw new IllegalStateException("Unable to restart disguise");
}
} else {
throw new IllegalStateException("Unable to restart disguise");
}
} else {
throw new IllegalStateException("Unable to restart disguise");
if (getName().isEmpty() && !name.isEmpty() && !isNameVisible()) {
setNameVisible(true);
} else if (!getName().isEmpty() && name.isEmpty() && isNameVisible()) {
setNameVisible(false);
} else {
DisguiseUtilities.updateExtendedName(this);
}
playerName = name;
}
} else {
if (getName() != null&&!getName().isEmpty() && name.isEmpty()) {
if (scoreboardName != null) {
DisguiseUtilities.DisguiseTeam team = getScoreboardName();
String[] split = DisguiseUtilities.getExtendedNameSplit(team.getPlayer(), name);
team.setSplit(split);
}
if (name.isEmpty()) {
setNameVisible(false);
} else if (!name.isEmpty()) {
} else {
setNameVisible(true);
}
@ -388,53 +436,55 @@ public class PlayerDisguise extends TargetedDisguise {
@Override
public boolean startDisguise() {
if (!isDisguiseInUse()) {
if (skinToUse != null && gameProfile == null) {
currentLookup = new LibsProfileLookup() {
@Override
public void onLookup(WrappedGameProfile gameProfile) {
if (currentLookup != this || gameProfile == null || gameProfile.getProperties().isEmpty())
return;
if (isDisguiseInUse()) {
return false;
}
setSkin(gameProfile);
if (skinToUse != null && gameProfile == null) {
currentLookup = new LibsProfileLookup() {
@Override
public void onLookup(WrappedGameProfile gameProfile) {
if (currentLookup != this || gameProfile == null || gameProfile.getProperties().isEmpty())
return;
currentLookup = null;
}
};
WrappedGameProfile gameProfile = DisguiseUtilities.getProfileFromMojang(this.skinToUse, currentLookup,
LibsDisguises.getInstance().getConfig().getBoolean("ContactMojangServers", true));
if (gameProfile != null) {
setSkin(gameProfile);
currentLookup = null;
}
};
WrappedGameProfile gameProfile = DisguiseUtilities.getProfileFromMojang(this.skinToUse, currentLookup,
LibsDisguises.getInstance().getConfig().getBoolean("ContactMojangServers", true));
if (gameProfile != null) {
setSkin(gameProfile);
}
}
if (isDynamicName()) {
String name = getEntity().getCustomName();
if (name == null) {
name = "";
}
if (isDynamicName()) {
String name = getEntity().getCustomName();
if (name == null) {
name = "";
}
if (!getName().equals(name)) {
setName(name);
}
} else if (getName().equals("<Inherit>") && getEntity() != null) {
String name = getEntity().getCustomName();
if (name == null || name.isEmpty()) {
name = getEntity().getType().name();
}
if (!getName().equals(name)) {
setName(name);
}
} else if (getName().equals("<Inherit>") && getEntity() != null) {
String name = getEntity().getCustomName();
if (name == null || name.isEmpty()) {
name = getEntity().getType().name();
}
setName(name);
}
boolean result = super.startDisguise();
if (result && hasExtendedName()) {
DisguiseUtilities.registerExtendedName(getName());
if (result && hasScoreboardName()) {
DisguiseUtilities.registerExtendedName(this);
}
return result;
@ -464,16 +514,16 @@ public class PlayerDisguise extends TargetedDisguise {
public boolean removeDisguise(boolean disguiseBeingReplaced) {
boolean result = super.removeDisguise(disguiseBeingReplaced);
if (result && hasExtendedName()) {
if (result && hasScoreboardName()) {
if (disguiseBeingReplaced) {
new BukkitRunnable() {
@Override
public void run() {
DisguiseUtilities.unregisterAttemptExtendedName(PlayerDisguise.this);
DisguiseUtilities.unregisterExtendedName(PlayerDisguise.this);
}
}.runTaskLater(LibsDisguises.getInstance(), 5);
} else {
DisguiseUtilities.unregisterAttemptExtendedName(this);
DisguiseUtilities.unregisterExtendedName(this);
}
}

@ -13,6 +13,7 @@ import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.mojang.authlib.properties.PropertyMap;
import lombok.Getter;
import lombok.Setter;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.DisguiseConfig.DisguisePushing;
@ -49,47 +50,52 @@ import java.lang.reflect.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class DisguiseUtilities {
public static class ExtendedName {
public ExtendedName(String teamName, String[] name) {
this.teamName = teamName;
this.name = name;
@Setter
public static class DisguiseTeam {
public DisguiseTeam(String[] name) {
this.split = name;
}
@Getter
private String teamName;
private String[] name;
private int users;
private long lastUsed = System.currentTimeMillis();
private String[] split;
private PlayerDisguise disguise;
public String[] getSplit() {
return name;
public String getPlayer() {
return split[1];
}
public void addUser() {
lastUsed = 0;
users++;
public String getPrefix() {
return split[0];
}
public void removeUser() {
if (users > 0) {
users--;
public String getSuffix() {
return split[2];
}
public void handleTeam(Scoreboard board, boolean nameVisible) {
Team team = board.getTeam(getTeamName());
if (team == null) {
team = board.registerNewTeam(getTeamName());
team.addEntry(getPlayer());
if (!nameVisible) {
team.setOption(Option.NAME_TAG_VISIBILITY, OptionStatus.NEVER);
}
} else if (team.getOption(Option.NAME_TAG_VISIBILITY) !=
(nameVisible ? OptionStatus.ALWAYS : OptionStatus.NEVER)) {
team.setOption(Option.NAME_TAG_VISIBILITY, nameVisible ? OptionStatus.ALWAYS : OptionStatus.NEVER);
}
if (users != 0) {
return;
}
lastUsed = System.currentTimeMillis();
}
public boolean isRemoveable() {
return users <= 0 && lastUsed + TimeUnit.HOURS.toMillis(1) < System.currentTimeMillis();
team.setPrefix(getPrefix());
team.setSuffix(getSuffix());
}
}
@ -132,9 +138,10 @@ public class DisguiseUtilities {
private static boolean runningPaper;
@Getter
private static MineSkinAPI mineSkinAPI = new MineSkinAPI();
private static HashMap<String, ExtendedName> extendedNames = new HashMap<>();
@Getter
private static boolean invalidFile;
@Getter
private static char[] alphabet = "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray();
public static void setPlayerVelocity(Player player) {
if (player == null) {
@ -235,8 +242,6 @@ public class DisguiseUtilities {
disguise = disguise.clone();
}
char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray();
String reference = null;
int referenceLength = Math.max(2, (int) Math.ceil((0.1D + DisguiseConfig.getMaxClonedDisguises()) / 26D));
int attempts = 0;
@ -928,14 +933,14 @@ public class DisguiseUtilities {
// Clear the old scoreboard teams for extended names!
for (Scoreboard board : getAllScoreboards()) {
for (Team team : board.getTeams()) {
if (!team.getName().matches("LD_[0-9]{10,}")) {
if (!team.getName().startsWith("LD_")) {
continue;
}
team.unregister();
}
registerExtendedNames(board);
registerAllExtendedNames(board);
registerNoName(board);
}
}
@ -1264,107 +1269,84 @@ public class DisguiseUtilities {
return boards;
}
public static ExtendedName createExtendedName(String name) {
ExtendedName exName = extendedNames.get(name);
public static DisguiseTeam createExtendedName(String name) {
String[] split = getExtendedNameSplit(null, name);
if (exName == null) {
String[] split = getExtendedNameSplit(name);
Scoreboard mainBoard = Bukkit.getScoreboardManager().getMainScoreboard();
return new DisguiseTeam(split);
}
while (true) {
String teamName = System.nanoTime() + "";
public static String getUniqueTeam() {
Scoreboard mainBoard = Bukkit.getScoreboardManager().getMainScoreboard();
if (teamName.length() > 13) {
teamName = teamName.substring(teamName.length() - 13);
}
for (int i = 0; i < 1000; i++) {
String teamName = encode(System.nanoTime() / 100 % 100000) + "";
teamName = "LD_" + teamName;
if (teamName.length() > 13) {
teamName = teamName.substring(teamName.length() - 13);
}
if (mainBoard.getTeam(teamName) != null) {
teamName = "LD_" + teamName;
if (mainBoard.getTeam(teamName) != null) {
continue;
}
return teamName;
}
throw new IllegalStateException("Lib's Disguises unable to find a unique team name!");
}
public static void updateExtendedName(PlayerDisguise disguise) {
DisguiseTeam exName = disguise.getScoreboardName();
for (Scoreboard board : getAllScoreboards()) {
exName.handleTeam(board, disguise.isNameVisible());
}
}
public static void registerExtendedName(PlayerDisguise disguise) {
DisguiseTeam exName = disguise.getScoreboardName();
if (exName.getTeamName() == null) {
exName.setTeamName(getUniqueTeam());
}
for (Scoreboard board : getAllScoreboards()) {
exName.handleTeam(board, disguise.isNameVisible());
}
}
public static void registerAllExtendedNames(Scoreboard scoreboard) {
for (Set<TargetedDisguise> disguises : getDisguises().values()) {
for (Disguise disguise : disguises) {
if (!disguise.isPlayerDisguise() || !disguise.isDisguiseInUse()) {
continue;
}
exName = new ExtendedName(teamName, split);
break;
}
DisguiseTeam name = ((PlayerDisguise) disguise).getScoreboardName();
extendedNames.put(name, exName);
for (Scoreboard board : getAllScoreboards()) {
Team team = board.registerNewTeam(exName.teamName);
team.setPrefix(exName.getSplit()[0]);
team.setSuffix(exName.getSplit()[2]);
team.addEntry(exName.getSplit()[1]);
name.handleTeam(scoreboard, ((PlayerDisguise) disguise).isNameVisible());
}
}
return exName;
}
public static ExtendedName registerExtendedName(String name) {
ExtendedName exName = createExtendedName(name);
exName.addUser();
doExtendedNamesGarbageCollection();
return exName;
}
public static void registerExtendedNames(Scoreboard scoreboard) {
for (ExtendedName entry : extendedNames.values()) {
String teamName = entry.teamName;
String[] name = entry.getSplit();
if (scoreboard.getEntryTeam(name[1]) != null) {
continue;
}
if (scoreboard.getTeam(teamName) != null) {
continue;
}
Team team = scoreboard.registerNewTeam(teamName);
team.addEntry(name[1]);
team.setPrefix(name[0]);
team.setSuffix(name[2]);
}
}
public static void unregisterAttemptExtendedName(PlayerDisguise removed) {
ExtendedName name = extendedNames.get(removed.getName());
if (name == null) {
public static void unregisterExtendedName(PlayerDisguise removed) {
if (removed.getScoreboardName().getTeamName() == null) {
return;
}
name.removeUser();
}
for (Scoreboard board : getAllScoreboards()) {
Team t = board.getTeam(removed.getScoreboardName().getTeamName());
public static void doExtendedNamesGarbageCollection() {
Iterator<Map.Entry<String, ExtendedName>> itel = extendedNames.entrySet().iterator();
while (itel.hasNext()) {
Map.Entry<String, ExtendedName> entry = itel.next();
if (!entry.getValue().isRemoveable()) {
if (t == null) {
continue;
}
itel.remove();
for (Scoreboard board : getAllScoreboards()) {
Team team = board.getTeam(entry.getValue().teamName);
if (team == null) {
continue;
}
team.unregister();
}
t.unregister();
}
removed.getScoreboardName().setTeamName(null);
}
public static void registerNoName(Scoreboard scoreboard) {
@ -1379,18 +1361,8 @@ public class DisguiseUtilities {
}
}
public static ExtendedName getExtendedName(String name) {
ExtendedName extendedName = extendedNames.get(name);
if (extendedName != null) {
return extendedName;
}
return createExtendedName(name);
}
private static String[] getExtendedNameSplit(String name) {
if (name.length() <= 16) {
public static String[] getExtendedNameSplit(String playerName, String name) {
if (name.length() <= 16 && !DisguiseConfig.isScoreboardDisguiseNames()) {
throw new IllegalStateException("This can only be used for names longer than 16 characters!");
}
@ -1398,12 +1370,53 @@ public class DisguiseUtilities {
name = name.substring(0, 48);
}
if (extendedNames.containsKey(name)) {
return extendedNames.get(name).getSplit();
}
Scoreboard board = Bukkit.getScoreboardManager().getMainScoreboard();
// If name is short enough to be used outside of player name
if (DisguiseConfig.isScoreboardDisguiseNames() && name.length() <= 32) {
String[] newName = new String[]{name, playerName, ""};
if (name.length() > 16) {
if (name.charAt(16) == ChatColor.COLOR_CHAR) {
newName[0] = name.substring(0, 15);
} else {
newName[0] = name.substring(0, 16);
}
String suffix = ChatColor.getLastColors(newName[0]) + name.substring(newName[0].length());
if (suffix.length() > 16) {
suffix = suffix.substring(0, 16);
}
// Don't allow second name to hit 17 chars
newName[2] = suffix;
}
String namePrefix = colorize("LD");
if (playerName == null || !playerName.startsWith(namePrefix)) {
String nameSuffix = "" + ChatColor.RESET;
long time = System.nanoTime() / 100 % 10000;
for (int i = 0; i < 1000; i++) {
String testName = namePrefix + colorize(encode(time + i)) + nameSuffix;
if (testName.length() > 16) {
break;
}
if (!isValidPlayerName(board, testName)) {
continue;
}
newName[1] = testName;
break;
}
}
return newName;
}
for (int prefixLen = 16; prefixLen >= 0; prefixLen--) {
String prefix = name.substring(0, prefixLen);
@ -1430,7 +1443,7 @@ public class DisguiseUtilities {
String[] extended = new String[]{prefix, nName, suffix};
if (!isValidPlayerName(board, extended)) {
if ((playerName == null || !playerName.equals(extended[1])) && !isValidPlayerName(board, extended[1])) {
continue;
}
@ -1461,8 +1474,29 @@ public class DisguiseUtilities {
return new String[]{prefix, nName, suffix};
}
private static boolean isValidPlayerName(Scoreboard board, String[] name) {
return board.getEntryTeam(name[1]) == null && Bukkit.getPlayerExact(name[1]) == null;
private static String colorize(String s) {
StringBuilder builder = new StringBuilder(s.length() * 2);
for (char c : s.toCharArray()) {
builder.append(ChatColor.COLOR_CHAR).append(c);
}
return builder.toString();
}
private static String encode(long toConvert) {
StringBuilder builder = new StringBuilder();
while (toConvert != 0) {
builder.append(alphabet[(int) (toConvert % alphabet.length)]);
toConvert /= alphabet.length;
}
return builder.reverse().toString();
}
private static boolean isValidPlayerName(Scoreboard board, String name) {
return board.getEntryTeam(name) == null && Bukkit.getPlayerExact(name) == null;
}
public static void removeSelfDisguiseScoreboard(Player player) {
@ -1633,7 +1667,9 @@ public class DisguiseUtilities {
return string;
}
return "\"" + string.replaceAll("\\\\(?=\\\\*\"( |$))", "\\\\\\\\").replaceAll("((?<= )\")|(\"(?= ))", "\\\\\"") + "\"";
return "\"" +
string.replaceAll("\\\\(?=\\\\*\"( |$))", "\\\\\\\\").replaceAll("((?<= )\")|(\"(?= ))", "\\\\\"") +
"\"";
}
public static String[] split(String string) {

@ -155,10 +155,10 @@ public class PacketHandlerSpawn implements IPacketHandler {
mods.write(5, pitch);
} else if (disguise.getType().isPlayer()) {
PlayerDisguise playerDisguise = (PlayerDisguise) disguise;
boolean visibleOrNewCompat = playerDisguise.isNameVisible() || DisguiseConfig.isScoreboardDisguiseNames();
WrappedGameProfile spawnProfile = playerDisguise.isNameVisible() ? playerDisguise.getGameProfile() :
ReflectionManager.getGameProfileWithThisSkin(UUID.randomUUID(),
playerDisguise.isNameVisible() ? playerDisguise.getProfileName() : "",
WrappedGameProfile spawnProfile = visibleOrNewCompat ? playerDisguise.getGameProfile() : ReflectionManager
.getGameProfileWithThisSkin(UUID.randomUUID(), visibleOrNewCompat ? playerDisguise.getProfileName() : "",
playerDisguise.getGameProfile());
int entityId = disguisedEntity.getEntityId();

@ -146,6 +146,7 @@ public class ParamInfoManager {
try {
methods.add(PlayerDisguise.class.getMethod("setNameVisible", boolean.class));
methods.add(PlayerDisguise.class.getMethod("setDynamicName", boolean.class));
methods.add(PlayerDisguise.class.getMethod("setName", String.class));
}
catch (NoSuchMethodException e) {
e.printStackTrace();

@ -18,7 +18,6 @@ import org.apache.commons.lang.StringUtils;
import org.bukkit.*;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
import org.bukkit.entity.*;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;

@ -64,6 +64,11 @@ SaveDisguises:
# Do names go beyond the 16 char limit for player disguises?
ExtendedNames: true
# Do names resolve into scoreboard teams so changing the player name is flawless?
# This will only work properly for names 28 chars or less in length.
# Notch [DragonSlayer lvl: 28]
# It's recommended to leave this on unless you need it off
ScoreboardNames: true
# Does the player keep their disguise after they die?
KeepDisguises: