Perform disguise validation before doing any of the disguise work

If a disguise is invalid, we won't notify redis or prevent players from joining
This commit is contained in:
Dan Mulloy 2017-07-17 13:28:50 -04:00
parent 2fd3c9cc2f
commit 9255c5a6ab
3 changed files with 250 additions and 137 deletions

View File

@ -37,11 +37,9 @@ public class DisguiseCommand extends CommandBase<PlayerDisguiseManager> implemen
return;
}
Plugin.runAsync(() ->
{
new PlayerDisguiseNotification(realName, currentUUID, args[0], args.length > 1 ? args[1] : args[0]).publish();
});
Plugin.disguise(caller, args[0], args.length > 1 ? args[1] : args[0]);
String skin = args.length > 1 ? args[1] : args[0];
Plugin.tryDisguise(caller, args[0], skin, () -> // onComplete
Plugin.runAsync(() -> // task
new PlayerDisguiseNotification(realName, currentUUID, args[0], skin).publish()));
}
}

View File

@ -0,0 +1,96 @@
package mineplex.core.disguise.playerdisguise;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* Set with contents that expire after X amount of time units. Essentially HashSet reimplemented with a Cache instead of HashMap.
* @author Dan
*/
public class ExpiringSet<E> extends AbstractSet<E> implements Set<E>
{
private transient Cache<E, Object> cache;
private static final Object DUMMY = new Object();
public ExpiringSet(long duration, TimeUnit unit)
{
this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit).build();
}
@Override
public Iterator<E> iterator()
{
return cache.asMap().keySet().iterator();
}
@Override
public void forEach(Consumer<? super E> action)
{
cache.asMap().keySet().forEach(action);
}
@Override
public boolean removeIf(Predicate<? super E> filter)
{
return cache.asMap().keySet().removeIf(filter);
}
@Override
public Spliterator<E> spliterator()
{
return cache.asMap().keySet().spliterator();
}
@Override
public Stream<E> stream()
{
return cache.asMap().keySet().stream();
}
@Override
public Stream<E> parallelStream()
{
return cache.asMap().keySet().parallelStream();
}
@Override
public int size()
{
return (int) cache.size();
}
@Override
public boolean contains(Object o)
{
return cache.getIfPresent(o) != null;
}
@Override
public boolean add(E e)
{
boolean contained = contains(e);
cache.put(e, DUMMY);
return contained;
}
@Override
public boolean remove(Object o)
{
boolean contained = contains(o);
cache.invalidate(o);
return contained;
}
@Override
public void clear()
{
cache.invalidateAll();
}
}

View File

@ -1,14 +1,8 @@
package mineplex.core.disguise.playerdisguise;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import net.minecraft.server.v1_8_R3.MinecraftServer;
@ -86,7 +80,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
ILLEGAL_USERNAMES = ImmutableSet.copyOf(Arrays.asList("hypixel", "chiss", "dctr", "blondebug", "dooskee",
"tomcallister", "jessiemarcia", "spu_", "sp614x", "deadmau5", "gwen", "mineplex", "samczsun", "sethbling",
"xisuma", "cubehamster", "natet_bird", "qwertyuiopthepie"
"xisuma", "cubehamster", "natet_bird", "qwertyuiopthepie", "hitler", "adolfhitler"
));
VERY_SPECIAL_PEOPLE = ImmutableSet.copyOf(Arrays.asList(
@ -121,14 +115,15 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
private CoreClientManager _clients = require(CoreClientManager.class);
private DisguiseManager _disguise = require(DisguiseManager.class);
private Punish _punish = require(Punish.class);
private CosmeticManager _cosmetics = require(CosmeticManager.class);
// private CosmeticManager _cosmetics = require(CosmeticManager.class);
private PreferencesManager _prefs = require(PreferencesManager.class);
private RedisDataRepository<DisguisePlayerBean> _redis;
// The list of usernames which cannot join because someone else is joining
private Set<String> _cannotJoin = Collections.synchronizedSet(new HashSet<>());
private Set<String> _loggingIn = Collections.synchronizedSet(new HashSet<>());
// Values expire in 30 seconds if they haven't been properly cleaned up
private Set<String> _cannotJoin = Collections.synchronizedSet(new ExpiringSet<>(1, TimeUnit.MINUTES));
private Set<String> _loggingIn = Collections.synchronizedSet(new ExpiringSet<>(1, TimeUnit.MINUTES));
private Set<UUID> _pendingDisguise1 = new HashSet<>();
@ -164,7 +159,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
if (disguisePlayer.getProfile().getName().equalsIgnoreCase(event.getPlayer().getName()))
{
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
event.setKickMessage("Failed to login: The authentication servers are currently down for maintainence");
event.setKickMessage("Failed to login: The authentication servers are currently down for maintenance");
return;
}
}
@ -172,7 +167,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
if (_cannotJoin.contains(event.getPlayer().getName().toLowerCase()))
{
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
event.setKickMessage("Failed to login: The authentication servers are currently down for maintainence");
event.setKickMessage("Failed to login: The authentication servers are currently down for maintenance");
return;
}
@ -231,7 +226,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
runSyncLater(() ->
{
UtilPlayer.message(event.getPlayer(), F.main(getName(), "Attempting to disguise you as " + bean.getGameProfile().getName()));
disguise(event.getPlayer(), bean.getGameProfile());
tryDisguise(event.getPlayer(), bean.getGameProfile(), () -> { });
}, 1);
}
}
@ -480,15 +475,15 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
}
}
public void disguise(Player caller, GameProfile requestedProfile)
public void tryDisguise(Player caller, GameProfile requestedProfile, Runnable onComplete)
{
if (getDisguiseManager().isDisguised(caller))
{
if (isDisguised(caller))
{
UtilPlayer.message(caller, F.main("Disguise", "You are already disguised. Please undisguise by using /disguise"));
}
else
UtilPlayer.message(caller,
F.main("Disguise", "You are already disguised. Please undisguise by using /disguise"));
} else
{
UtilPlayer.message(caller, F.main("Disguise", "You are already disguised. Perhaps you are morphed?"));
}
@ -497,134 +492,75 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
if (isDisguised(caller))
{
UtilPlayer.message(caller, F.main("Disguise", "You are already disguised. Please undisguise by using /disguise"));
return;
}
String requestedUsername = requestedProfile.getName();
if (!requestedUsername.equalsIgnoreCase(caller.getName()))
{
_cannotJoin.add(requestedUsername.toLowerCase());
for (Player other : UtilServer.getPlayersCollection())
{
if (other.getName().equalsIgnoreCase(requestedUsername))
{
UtilPlayer.message(caller, C.cRed + F.main("Disguise", "This name is already in use!"));
_cannotJoin.remove(requestedUsername.toLowerCase());
return;
}
}
}
if (!_pendingDisguise.add(requestedUsername.toLowerCase()))
{
UtilPlayer.message(caller, F.main("Disguise", "Someone is already disguising as that user"));
return;
}
PlayerPreDisguiseEvent playerPreDisguiseEvent = new PlayerPreDisguiseEvent(caller, requestedUsername);
UtilServer.CallEvent(playerPreDisguiseEvent);
if (playerPreDisguiseEvent.isCancelled())
{
UtilPlayer.message(caller, F.main(getName(), "Your disguise was cancelled by something"));
_pendingDisguise.remove(requestedUsername.toLowerCase());
UtilPlayer.message(caller,
F.main("Disguise", "You are already disguised. Please undisguise by using /disguise"));
return;
}
CoreClient callerClient = getClientManager().Get(caller);
if (!requestedUsername.equalsIgnoreCase(caller.getName()))
String requestedUsername = requestedProfile.getName();
if (requestedUsername.equalsIgnoreCase(caller.getName()))
{
getClientManager().getOrLoadClient(requestedUsername, other ->
if (doDisguise(caller, requestedProfile, callerClient, callerClient))
{
Rank otherRank = other != null ? other.GetRank() : Rank.ALL;
onComplete.run();
}
return;
}
for (Player other : UtilServer.getPlayersCollection())
{
if (other.getName().equalsIgnoreCase(requestedUsername))
{
UtilPlayer.message(caller, C.cRed + F.main("Disguise", "This name is already in use!"));
return;
}
}
if (_pendingDisguise.contains(requestedUsername.toLowerCase()))
{
UtilPlayer.message(caller, F.main("Disguise", "Someone is already disguising as that user"));
return;
}
getClientManager().getOrLoadClient(requestedUsername, other ->
{
if (other != null)
{
Rank otherRank = other.GetRank();
if (otherRank.has(Rank.TWITCH))
{
UtilPlayer.message(caller, F.main("Disguise", "You can't disguise as staff, YouTubers or Twitchers!"));
UtilPlayer.message(caller,
F.main("Disguise", "You can't disguise as staff, YouTubers or Twitchers!"));
return;
}
if (other != null)
PunishClient pclient = getPunishManager().GetClient(requestedUsername);
if (pclient != null && (pclient.IsBanned() || pclient.IsMuted()))
{
PunishClient pclient = getPunishManager().GetClient(requestedUsername);
if (pclient != null && (pclient.IsBanned() || pclient.IsMuted()))
{
UtilPlayer.message(caller, F.main("Disguise", "You can't disguise as players who are banned/muted!"));
return;
}
UtilPlayer.message(caller,
F.main("Disguise", "You can't disguise as players who are banned/muted!"));
return;
}
}
callerClient.disguise(requestedUsername, requestedProfile.getId(), otherRank);
if (doDisguise(caller, requestedProfile, callerClient, other))
{
onComplete.run();
}
});
}
_mapping.put(callerClient.getDisguisedAs().toLowerCase(), callerClient.getName());
public boolean doDisguise(Player caller, GameProfile requestedProfile, CoreClient callerClient, CoreClient otherClient)
{
String requestedUsername = requestedProfile.getName();
_pendingDisguise.add(requestedUsername.toLowerCase());
System.out.println("=================");
System.out.println("Disguising " + caller.getName() + " as:");
System.out.println(requestedProfile.getName() + " id " + requestedProfile.getId());
System.out.println("Properties:");
for (Map.Entry<String, Property> p : requestedProfile.getProperties().entries())
{
System.out.println("\t" + p.getKey() + " " + p.getValue().getName());
System.out.println("\t" + p.getValue().getValue());
System.out.println("\t" + p.getValue().getSignature());
}
System.out.println("=================");
DisguisePlayer disguisePlayer = new DisguisePlayer(caller, requestedProfile);
disguisePlayer.showInTabList(true, 0);
allow(caller);
getDisguiseManager().disguise(disguisePlayer, () ->
{
GameProfile callerProfile = ((CraftPlayer) caller).getProfile();
require(ScoreboardManager.class).handlePlayerQuit(disguisePlayer.getOriginalProfile().getName());
try
{
UtilGameProfile.changeName(callerProfile, disguisePlayer.getProfile().getName());
UtilGameProfile.changeId(callerProfile, disguisePlayer.getProfile().getId());
Field playersByName = PlayerList.class.getDeclaredField("playersByName");
playersByName.setAccessible(true);
Map map = (Map) playersByName.get(MinecraftServer.getServer().getPlayerList());
map.remove(disguisePlayer.getOriginalProfile().getName());
map.put(disguisePlayer.getProfile().getName(), disguisePlayer.getEntity());
}
catch (Throwable t)
{
t.printStackTrace();
}
require(ScoreboardManager.class).handlePlayerJoin(disguisePlayer.getName());
callerProfile.getProperties().clear();
callerProfile.getProperties().putAll(disguisePlayer.getProfile().getProperties());
callerProfile.getProperties().removeAll(ORIGINAL_UUID_KEY);
callerProfile.getProperties().put(ORIGINAL_UUID_KEY, new Property(ORIGINAL_UUID_KEY, caller.getUniqueId().toString()));
require(FriendManager.class).updatePlayerStatus(disguisePlayer.getOriginalProfile().getId(), null);
require(FriendManager.class).updatePlayerStatus(disguisePlayer.getProfile().getId(), new PlayerStatus(disguisePlayer.getProfile().getId(), requestedUsername, _serverName));
getPreferencesManager().handlePlayerJoin(caller, true);
_disguises.put(caller.getUniqueId(), disguisePlayer);
UtilPlayer.message(caller, F.main("Disguise", "Disguise Active: " + ChatColor.RESET + requestedUsername));
UtilServer.CallEvent(new PlayerDisguisedEvent(caller));
storeDisguiseData(caller, requestedUsername, requestedProfile);
_pendingDisguise.remove(requestedUsername.toLowerCase());
_cannotJoin.remove(requestedUsername.toLowerCase());
});
});
}
else
if (!requestedUsername.equalsIgnoreCase(caller.getName()))
{
_cannotJoin.add(requestedUsername.toLowerCase());
} else
{
DisguisePlayer disguisePlayer = new DisguisePlayer(caller, requestedProfile);
disguisePlayer.showInTabList(true, 0);
@ -641,10 +577,93 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
_pendingDisguise.remove(requestedUsername.toLowerCase());
});
return true;
}
PlayerPreDisguiseEvent playerPreDisguiseEvent = new PlayerPreDisguiseEvent(caller, requestedUsername);
UtilServer.CallEvent(playerPreDisguiseEvent);
if (playerPreDisguiseEvent.isCancelled())
{
UtilPlayer.message(caller, F.main(getName(), "Your disguise was cancelled by something"));
_pendingDisguise.remove(requestedUsername.toLowerCase());
_cannotJoin.remove(requestedUsername.toLowerCase());
return false;
}
Rank otherRank = otherClient != null ? otherClient.GetRank() : Rank.ALL;
callerClient.disguise(requestedUsername, requestedProfile.getId(), otherRank);
_mapping.put(callerClient.getDisguisedAs().toLowerCase(), callerClient.getName());
System.out.println("=================");
System.out.println("Disguising " + caller.getName() + " as:");
System.out.println(requestedProfile.getName() + " id " + requestedProfile.getId());
System.out.println("Properties:");
for (Map.Entry<String, Property> p : requestedProfile.getProperties().entries())
{
System.out.println("\t" + p.getKey() + " " + p.getValue().getName());
System.out.println("\t" + p.getValue().getValue());
System.out.println("\t" + p.getValue().getSignature());
}
System.out.println("=================");
DisguisePlayer disguisePlayer = new DisguisePlayer(caller, requestedProfile);
disguisePlayer.showInTabList(true, 0);
allow(caller);
getDisguiseManager().disguise(disguisePlayer, () ->
{
GameProfile callerProfile = ((CraftPlayer) caller).getProfile();
require(ScoreboardManager.class).handlePlayerQuit(disguisePlayer.getOriginalProfile().getName());
try
{
UtilGameProfile.changeName(callerProfile, disguisePlayer.getProfile().getName());
UtilGameProfile.changeId(callerProfile, disguisePlayer.getProfile().getId());
Field playersByName = PlayerList.class.getDeclaredField("playersByName");
playersByName.setAccessible(true);
Map map = (Map) playersByName.get(MinecraftServer.getServer().getPlayerList());
map.remove(disguisePlayer.getOriginalProfile().getName());
map.put(disguisePlayer.getProfile().getName(), disguisePlayer.getEntity());
} catch (Throwable t)
{
t.printStackTrace();
}
require(ScoreboardManager.class).handlePlayerJoin(disguisePlayer.getName());
callerProfile.getProperties().clear();
callerProfile.getProperties().putAll(disguisePlayer.getProfile().getProperties());
callerProfile.getProperties().removeAll(ORIGINAL_UUID_KEY);
callerProfile.getProperties()
.put(ORIGINAL_UUID_KEY, new Property(ORIGINAL_UUID_KEY, caller.getUniqueId().toString()));
require(FriendManager.class).updatePlayerStatus(disguisePlayer.getOriginalProfile().getId(), null);
require(FriendManager.class).updatePlayerStatus(disguisePlayer.getProfile().getId(),
new PlayerStatus(disguisePlayer.getProfile().getId(), requestedUsername, _serverName));
getPreferencesManager().handlePlayerJoin(caller, true);
_disguises.put(caller.getUniqueId(), disguisePlayer);
UtilPlayer.message(caller, F.main("Disguise", "Disguise Active: " + ChatColor.RESET + requestedUsername));
UtilServer.CallEvent(new PlayerDisguisedEvent(caller));
storeDisguiseData(caller, requestedUsername, requestedProfile);
_pendingDisguise.remove(requestedUsername.toLowerCase());
_cannotJoin.remove(requestedUsername.toLowerCase());
});
return true;
}
public void disguise(Player caller, String requestedUsername, String requestedSkin)
public void tryDisguise(Player caller, String requestedUsername, String requestedSkin, Runnable onComplete)
{
if (!validateUsername(caller, requestedUsername, true)) return;
if (!validateUsername(caller, requestedSkin, false)) return;
@ -667,7 +686,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
requestedProfile.getProperties().clear();
requestedProfile.getProperties().put("textures", skinData.getProperty());
disguise(caller, requestedProfile);
tryDisguise(caller, requestedProfile, onComplete);
};
if (!requestedUsername.equalsIgnoreCase(requestedSkin))