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 committed by cnr
parent ac2df004f0
commit 8b39fa4e24
3 changed files with 250 additions and 137 deletions

View File

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

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; package mineplex.core.disguise.playerdisguise;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Arrays; import java.util.*;
import java.util.Collections; import java.util.concurrent.TimeUnit;
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.function.Consumer; import java.util.function.Consumer;
import net.minecraft.server.v1_8_R3.MinecraftServer; 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", ILLEGAL_USERNAMES = ImmutableSet.copyOf(Arrays.asList("hypixel", "chiss", "dctr", "blondebug", "dooskee",
"tomcallister", "jessiemarcia", "spu_", "sp614x", "deadmau5", "gwen", "mineplex", "samczsun", "sethbling", "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( 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 CoreClientManager _clients = require(CoreClientManager.class);
private DisguiseManager _disguise = require(DisguiseManager.class); private DisguiseManager _disguise = require(DisguiseManager.class);
private Punish _punish = require(Punish.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 PreferencesManager _prefs = require(PreferencesManager.class);
private RedisDataRepository<DisguisePlayerBean> _redis; private RedisDataRepository<DisguisePlayerBean> _redis;
// The list of usernames which cannot join because someone else is joining // The list of usernames which cannot join because someone else is joining
private Set<String> _cannotJoin = Collections.synchronizedSet(new HashSet<>()); // Values expire in 30 seconds if they haven't been properly cleaned up
private Set<String> _loggingIn = Collections.synchronizedSet(new HashSet<>()); 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<>(); 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())) if (disguisePlayer.getProfile().getName().equalsIgnoreCase(event.getPlayer().getName()))
{ {
event.setResult(PlayerLoginEvent.Result.KICK_OTHER); 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; return;
} }
} }
@ -172,7 +167,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
if (_cannotJoin.contains(event.getPlayer().getName().toLowerCase())) if (_cannotJoin.contains(event.getPlayer().getName().toLowerCase()))
{ {
event.setResult(PlayerLoginEvent.Result.KICK_OTHER); 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; return;
} }
@ -231,7 +226,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
runSyncLater(() -> runSyncLater(() ->
{ {
UtilPlayer.message(event.getPlayer(), F.main(getName(), "Attempting to disguise you as " + bean.getGameProfile().getName())); 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); }, 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 (getDisguiseManager().isDisguised(caller))
{ {
if (isDisguised(caller)) if (isDisguised(caller))
{ {
UtilPlayer.message(caller, F.main("Disguise", "You are already disguised. Please undisguise by using /disguise")); UtilPlayer.message(caller,
} F.main("Disguise", "You are already disguised. Please undisguise by using /disguise"));
else } else
{ {
UtilPlayer.message(caller, F.main("Disguise", "You are already disguised. Perhaps you are morphed?")); UtilPlayer.message(caller, F.main("Disguise", "You are already disguised. Perhaps you are morphed?"));
} }
@ -497,65 +492,106 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
if (isDisguised(caller)) if (isDisguised(caller))
{ {
UtilPlayer.message(caller, F.main("Disguise", "You are already disguised. Please undisguise by using /disguise")); UtilPlayer.message(caller,
F.main("Disguise", "You are already disguised. Please undisguise by using /disguise"));
return; return;
} }
String requestedUsername = requestedProfile.getName(); CoreClient callerClient = getClientManager().Get(caller);
if (!requestedUsername.equalsIgnoreCase(caller.getName())) String requestedUsername = requestedProfile.getName();
if (requestedUsername.equalsIgnoreCase(caller.getName()))
{ {
_cannotJoin.add(requestedUsername.toLowerCase()); if (doDisguise(caller, requestedProfile, callerClient, callerClient))
{
onComplete.run();
}
return;
}
for (Player other : UtilServer.getPlayersCollection()) for (Player other : UtilServer.getPlayersCollection())
{ {
if (other.getName().equalsIgnoreCase(requestedUsername)) if (other.getName().equalsIgnoreCase(requestedUsername))
{ {
UtilPlayer.message(caller, C.cRed + F.main("Disguise", "This name is already in use!")); UtilPlayer.message(caller, C.cRed + F.main("Disguise", "This name is already in use!"));
_cannotJoin.remove(requestedUsername.toLowerCase());
return; return;
} }
} }
}
if (!_pendingDisguise.add(requestedUsername.toLowerCase())) if (_pendingDisguise.contains(requestedUsername.toLowerCase()))
{ {
UtilPlayer.message(caller, F.main("Disguise", "Someone is already disguising as that user")); UtilPlayer.message(caller, F.main("Disguise", "Someone is already disguising as that user"));
return; 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!"));
return;
}
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;
}
}
if (doDisguise(caller, requestedProfile, callerClient, other))
{
onComplete.run();
}
});
}
public boolean doDisguise(Player caller, GameProfile requestedProfile, CoreClient callerClient, CoreClient otherClient)
{
String requestedUsername = requestedProfile.getName();
_pendingDisguise.add(requestedUsername.toLowerCase());
if (!requestedUsername.equalsIgnoreCase(caller.getName()))
{
_cannotJoin.add(requestedUsername.toLowerCase());
} else
{
DisguisePlayer disguisePlayer = new DisguisePlayer(caller, requestedProfile);
disguisePlayer.showInTabList(true, 0);
allow(caller);
getDisguiseManager().disguise(disguisePlayer, () ->
{
((CraftPlayer) caller).getProfile().getProperties().clear();
((CraftPlayer) caller).getProfile().getProperties().putAll(disguisePlayer.getProfile().getProperties());
storeDisguiseData(caller, caller.getName(), requestedProfile);
getPreferencesManager().handlePlayerJoin(caller, true);
_disguises.put(caller.getUniqueId(), disguisePlayer);
_pendingDisguise.remove(requestedUsername.toLowerCase());
});
return true;
}
PlayerPreDisguiseEvent playerPreDisguiseEvent = new PlayerPreDisguiseEvent(caller, requestedUsername); PlayerPreDisguiseEvent playerPreDisguiseEvent = new PlayerPreDisguiseEvent(caller, requestedUsername);
UtilServer.CallEvent(playerPreDisguiseEvent); UtilServer.CallEvent(playerPreDisguiseEvent);
if (playerPreDisguiseEvent.isCancelled()) if (playerPreDisguiseEvent.isCancelled())
{ {
UtilPlayer.message(caller, F.main(getName(), "Your disguise was cancelled by something")); UtilPlayer.message(caller, F.main(getName(), "Your disguise was cancelled by something"));
_pendingDisguise.remove(requestedUsername.toLowerCase()); _pendingDisguise.remove(requestedUsername.toLowerCase());
return; _cannotJoin.remove(requestedUsername.toLowerCase());
} return false;
CoreClient callerClient = getClientManager().Get(caller);
if (!requestedUsername.equalsIgnoreCase(caller.getName()))
{
getClientManager().getOrLoadClient(requestedUsername, other ->
{
Rank otherRank = other != null ? other.GetRank() : Rank.ALL;
if (otherRank.has(Rank.TWITCH))
{
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()))
{
UtilPlayer.message(caller, F.main("Disguise", "You can't disguise as players who are banned/muted!"));
return;
}
} }
Rank otherRank = otherClient != null ? otherClient.GetRank() : Rank.ALL;
callerClient.disguise(requestedUsername, requestedProfile.getId(), otherRank); callerClient.disguise(requestedUsername, requestedProfile.getId(), otherRank);
_mapping.put(callerClient.getDisguisedAs().toLowerCase(), callerClient.getName()); _mapping.put(callerClient.getDisguisedAs().toLowerCase(), callerClient.getName());
@ -591,8 +627,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
Map map = (Map) playersByName.get(MinecraftServer.getServer().getPlayerList()); Map map = (Map) playersByName.get(MinecraftServer.getServer().getPlayerList());
map.remove(disguisePlayer.getOriginalProfile().getName()); map.remove(disguisePlayer.getOriginalProfile().getName());
map.put(disguisePlayer.getProfile().getName(), disguisePlayer.getEntity()); map.put(disguisePlayer.getProfile().getName(), disguisePlayer.getEntity());
} } catch (Throwable t)
catch (Throwable t)
{ {
t.printStackTrace(); t.printStackTrace();
} }
@ -603,10 +638,12 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
callerProfile.getProperties().putAll(disguisePlayer.getProfile().getProperties()); callerProfile.getProperties().putAll(disguisePlayer.getProfile().getProperties());
callerProfile.getProperties().removeAll(ORIGINAL_UUID_KEY); callerProfile.getProperties().removeAll(ORIGINAL_UUID_KEY);
callerProfile.getProperties().put(ORIGINAL_UUID_KEY, new Property(ORIGINAL_UUID_KEY, caller.getUniqueId().toString())); 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.getOriginalProfile().getId(), null);
require(FriendManager.class).updatePlayerStatus(disguisePlayer.getProfile().getId(), new PlayerStatus(disguisePlayer.getProfile().getId(), requestedUsername, _serverName)); require(FriendManager.class).updatePlayerStatus(disguisePlayer.getProfile().getId(),
new PlayerStatus(disguisePlayer.getProfile().getId(), requestedUsername, _serverName));
getPreferencesManager().handlePlayerJoin(caller, true); getPreferencesManager().handlePlayerJoin(caller, true);
@ -622,29 +659,11 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
_cannotJoin.remove(requestedUsername.toLowerCase()); _cannotJoin.remove(requestedUsername.toLowerCase());
}); });
});
}
else
{
DisguisePlayer disguisePlayer = new DisguisePlayer(caller, requestedProfile);
disguisePlayer.showInTabList(true, 0);
allow(caller);
getDisguiseManager().disguise(disguisePlayer, () ->
{
((CraftPlayer) caller).getProfile().getProperties().clear();
((CraftPlayer) caller).getProfile().getProperties().putAll(disguisePlayer.getProfile().getProperties());
storeDisguiseData(caller, caller.getName(), requestedProfile); return true;
getPreferencesManager().handlePlayerJoin(caller, true);
_disguises.put(caller.getUniqueId(), disguisePlayer);
_pendingDisguise.remove(requestedUsername.toLowerCase());
});
}
} }
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, requestedUsername, true)) return;
if (!validateUsername(caller, requestedSkin, false)) return; if (!validateUsername(caller, requestedSkin, false)) return;
@ -667,7 +686,7 @@ public class PlayerDisguiseManager extends MiniPlugin implements IPacketHandler
requestedProfile.getProperties().clear(); requestedProfile.getProperties().clear();
requestedProfile.getProperties().put("textures", skinData.getProperty()); requestedProfile.getProperties().put("textures", skinData.getProperty());
disguise(caller, requestedProfile); tryDisguise(caller, requestedProfile, onComplete);
}; };
if (!requestedUsername.equalsIgnoreCase(requestedSkin)) if (!requestedUsername.equalsIgnoreCase(requestedSkin))