Revert "Packet based movement freeze (#1879)" (#1882)

This reverts commit db3acce70c.
This commit is contained in:
Gabriele C
2019-08-07 10:49:23 +02:00
committed by GitHub
parent db3acce70c
commit 48357831cd
26 changed files with 1145 additions and 234 deletions
@@ -68,7 +68,9 @@ class LimboPlayerViewer implements DebugSection {
sender.sendMessage(ChatColor.BLUE + "Player / limbo / disk limbo info for '" + arguments.get(0) + "'");
new InfoDisplayer(sender, player, memoryLimbo, diskLimbo)
.sendEntry("Is op", Player::isOp, LimboPlayer::isOperator)
.sendEntry("Walk speed", Player::getWalkSpeed, LimboPlayer::getWalkSpeed)
.sendEntry("Can fly", Player::getAllowFlight, LimboPlayer::isCanFly)
.sendEntry("Fly speed", Player::getFlySpeed, LimboPlayer::getFlySpeed)
.sendEntry("Location", p -> formatLocation(p.getLocation()), l -> formatLocation(l.getLocation()))
.sendEntry("Prim. group",
p -> permissionsManager.hasGroupSupport() ? permissionsManager.getPrimaryGroup(p) : "N/A",
@@ -12,19 +12,27 @@ import java.util.Collection;
*/
public class LimboPlayer {
public static final float DEFAULT_WALK_SPEED = 0.2f;
public static final float DEFAULT_FLY_SPEED = 0.1f;
private final boolean canFly;
private final boolean operator;
private final Collection<String> groups;
private final Location loc;
private final float walkSpeed;
private final float flySpeed;
private BukkitTask timeoutTask = null;
private MessageTask messageTask = null;
private LimboPlayerState state = LimboPlayerState.PASSWORD_REQUIRED;
public LimboPlayer(Location loc, boolean operator, Collection<String> groups, boolean fly) {
public LimboPlayer(Location loc, boolean operator, Collection<String> groups, boolean fly, float walkSpeed,
float flySpeed) {
this.loc = loc;
this.operator = operator;
this.groups = groups;
this.canFly = fly;
this.walkSpeed = walkSpeed;
this.flySpeed = flySpeed;
}
/**
@@ -58,6 +66,14 @@ public class LimboPlayer {
return canFly;
}
public float getWalkSpeed() {
return walkSpeed;
}
public float getFlySpeed() {
return flySpeed;
}
/**
* Return the timeout task, which kicks the player if he hasn't registered or logged in
* after a configurable amount of time.
@@ -14,6 +14,8 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import static fr.xephi.authme.settings.properties.LimboSettings.RESTORE_ALLOW_FLIGHT;
import static fr.xephi.authme.settings.properties.LimboSettings.RESTORE_FLY_SPEED;
import static fr.xephi.authme.settings.properties.LimboSettings.RESTORE_WALK_SPEED;
/**
* Service for managing players that are in "limbo," a temporary state players are
@@ -117,6 +119,8 @@ public class LimboService {
} else {
player.setOp(limbo.isOperator());
settings.getProperty(RESTORE_ALLOW_FLIGHT).restoreAllowFlight(player, limbo);
settings.getProperty(RESTORE_FLY_SPEED).restoreFlySpeed(player, limbo);
settings.getProperty(RESTORE_WALK_SPEED).restoreWalkSpeed(player, limbo);
limbo.clearTasks();
logger.debug("Restored LimboPlayer stats for `{0}`", lowerName);
persistence.removeLimboPlayer(player);
@@ -5,6 +5,7 @@ import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.LimboSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -39,11 +40,13 @@ class LimboServiceHelper {
// For safety reasons an unregistered player should not have OP status after registration
boolean isOperator = isRegistered && player.isOp();
boolean flyEnabled = player.getAllowFlight();
float walkSpeed = player.getWalkSpeed();
float flySpeed = player.getFlySpeed();
Collection<String> playerGroups = permissionsManager.hasGroupSupport()
? permissionsManager.getGroups(player) : Collections.emptyList();
logger.debug("Player `{0}` has groups `{1}`", player.getName(), String.join(", ", playerGroups));
return new LimboPlayer(location, isOperator, playerGroups, flyEnabled);
return new LimboPlayer(location, isOperator, playerGroups, flyEnabled, walkSpeed, flySpeed);
}
/**
@@ -58,12 +61,18 @@ class LimboServiceHelper {
player.setOp(false);
settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)
.processPlayer(player);
if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) {
player.setFlySpeed(0.0f);
player.setWalkSpeed(0.0f);
}
}
/**
* Merges two existing LimboPlayer instances of a player. Merging is done the following way:
* <ul>
* <li><code>isOperator, allowFlight</code>: true if either limbo has true</li>
* <li><code>flySpeed, walkSpeed</code>: maximum value of either limbo player</li>
* <li><code>groups, location</code>: from old limbo if not empty/null, otherwise from new limbo</li>
* </ul>
*
@@ -80,10 +89,12 @@ class LimboServiceHelper {
boolean isOperator = newLimbo.isOperator() || oldLimbo.isOperator();
boolean canFly = newLimbo.isCanFly() || oldLimbo.isCanFly();
float flySpeed = Math.max(newLimbo.getFlySpeed(), oldLimbo.getFlySpeed());
float walkSpeed = Math.max(newLimbo.getWalkSpeed(), oldLimbo.getWalkSpeed());
Collection<String> groups = getLimboGroups(oldLimbo.getGroups(), newLimbo.getGroups());
Location location = firstNotNull(oldLimbo.getLocation(), newLimbo.getLocation());
return new LimboPlayer(location, isOperator, groups, canFly);
return new LimboPlayer(location, isOperator, groups, canFly, walkSpeed, flySpeed);
}
private static Location firstNotNull(Location first, Location second) {
@@ -0,0 +1,123 @@
package fr.xephi.authme.data.limbo;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import org.bukkit.entity.Player;
/**
* Possible types to restore the walk and fly speed from LimboPlayer
* back to Bukkit Player.
*/
public enum WalkFlySpeedRestoreType {
/**
* Restores from LimboPlayer to Player.
*/
RESTORE {
@Override
public void restoreFlySpeed(Player player, LimboPlayer limbo) {
logger.debug(() -> "Restoring fly speed for LimboPlayer " + player.getName() + " to "
+ limbo.getFlySpeed() + " (RESTORE mode)");
player.setFlySpeed(limbo.getFlySpeed());
}
@Override
public void restoreWalkSpeed(Player player, LimboPlayer limbo) {
logger.debug(() -> "Restoring walk speed for LimboPlayer " + player.getName() + " to "
+ limbo.getWalkSpeed() + " (RESTORE mode)");
player.setWalkSpeed(limbo.getWalkSpeed());
}
},
/**
* Restores from LimboPlayer, using the default speed if the speed on LimboPlayer is 0.
*/
RESTORE_NO_ZERO {
@Override
public void restoreFlySpeed(Player player, LimboPlayer limbo) {
float limboFlySpeed = limbo.getFlySpeed();
if (limboFlySpeed > 0.01f) {
logger.debug(() -> "Restoring fly speed for LimboPlayer " + player.getName() + " to "
+ limboFlySpeed + " (RESTORE_NO_ZERO mode)");
player.setFlySpeed(limboFlySpeed);
} else {
logger.debug(() -> "Restoring fly speed for LimboPlayer " + player.getName()
+ " to DEFAULT, it was 0! (RESTORE_NO_ZERO mode)");
player.setFlySpeed(LimboPlayer.DEFAULT_FLY_SPEED);
}
}
@Override
public void restoreWalkSpeed(Player player, LimboPlayer limbo) {
float limboWalkSpeed = limbo.getWalkSpeed();
if (limboWalkSpeed > 0.01f) {
logger.debug(() -> "Restoring walk speed for LimboPlayer " + player.getName() + " to "
+ limboWalkSpeed + " (RESTORE_NO_ZERO mode)");
player.setWalkSpeed(limboWalkSpeed);
} else {
logger.debug(() -> "Restoring walk speed for LimboPlayer " + player.getName() + ""
+ " to DEFAULT, it was 0! (RESTORE_NO_ZERO mode)");
player.setWalkSpeed(LimboPlayer.DEFAULT_WALK_SPEED);
}
}
},
/**
* Uses the max speed of Player (current speed) and the LimboPlayer.
*/
MAX_RESTORE {
@Override
public void restoreFlySpeed(Player player, LimboPlayer limbo) {
float newSpeed = Math.max(player.getFlySpeed(), limbo.getFlySpeed());
logger.debug(() -> "Restoring fly speed for LimboPlayer " + player.getName() + " to " + newSpeed
+ " (Current: " + player.getFlySpeed() + ", Limbo: " + limbo.getFlySpeed() + ") (MAX_RESTORE mode)");
player.setFlySpeed(newSpeed);
}
@Override
public void restoreWalkSpeed(Player player, LimboPlayer limbo) {
float newSpeed = Math.max(player.getWalkSpeed(), limbo.getWalkSpeed());
logger.debug(() -> "Restoring walk speed for LimboPlayer " + player.getName() + " to " + newSpeed
+ " (Current: " + player.getWalkSpeed() + ", Limbo: " + limbo.getWalkSpeed() + ") (MAX_RESTORE mode)");
player.setWalkSpeed(newSpeed);
}
},
/**
* Always sets the default speed to the player.
*/
DEFAULT {
@Override
public void restoreFlySpeed(Player player, LimboPlayer limbo) {
logger.debug(() -> "Restoring fly speed for LimboPlayer " + player.getName()
+ " to DEFAULT (DEFAULT mode)");
player.setFlySpeed(LimboPlayer.DEFAULT_FLY_SPEED);
}
@Override
public void restoreWalkSpeed(Player player, LimboPlayer limbo) {
logger.debug(() -> "Restoring walk speed for LimboPlayer " + player.getName()
+ " to DEFAULT (DEFAULT mode)");
player.setWalkSpeed(LimboPlayer.DEFAULT_WALK_SPEED);
}
};
private static final ConsoleLogger logger = ConsoleLoggerFactory.get(WalkFlySpeedRestoreType.class);
/**
* Restores the fly speed from Limbo to Player according to the restoration type.
*
* @param player the player to modify
* @param limbo the limbo player to read from
*/
public abstract void restoreFlySpeed(Player player, LimboPlayer limbo);
/**
* Restores the walk speed from Limbo to Player according to the restoration type.
*
* @param player the player to modify
* @param limbo the limbo player to read from
*/
public abstract void restoreWalkSpeed(Player player, LimboPlayer limbo);
}
@@ -18,6 +18,7 @@ import java.util.List;
import java.util.function.Function;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.CAN_FLY;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.FLY_SPEED;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.GROUPS;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.IS_OP;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.LOCATION;
@@ -27,6 +28,7 @@ import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.LOC_X
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.LOC_Y;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.LOC_YAW;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.LOC_Z;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.WALK_SPEED;
import static java.util.Optional.ofNullable;
/**
@@ -54,8 +56,10 @@ class LimboPlayerDeserializer implements JsonDeserializer<LimboPlayer> {
Collection<String> groups = getLimboGroups(jsonObject);
boolean canFly = getBoolean(jsonObject, CAN_FLY);
float walkSpeed = getFloat(jsonObject, WALK_SPEED, LimboPlayer.DEFAULT_WALK_SPEED);
float flySpeed = getFloat(jsonObject, FLY_SPEED, LimboPlayer.DEFAULT_FLY_SPEED);
return new LimboPlayer(loc, operator, groups, canFly);
return new LimboPlayer(loc, operator, groups, canFly, walkSpeed, flySpeed);
}
private Location deserializeLocation(JsonObject jsonObject) {
@@ -26,6 +26,8 @@ class LimboPlayerSerializer implements JsonSerializer<LimboPlayer> {
static final String GROUPS = "groups";
static final String IS_OP = "operator";
static final String CAN_FLY = "can-fly";
static final String WALK_SPEED = "walk-speed";
static final String FLY_SPEED = "fly-speed";
private static final Gson GSON = new Gson();
@@ -47,6 +49,8 @@ class LimboPlayerSerializer implements JsonSerializer<LimboPlayer> {
obj.addProperty(IS_OP, limboPlayer.isOperator());
obj.addProperty(CAN_FLY, limboPlayer.isCanFly());
obj.addProperty(WALK_SPEED, limboPlayer.getWalkSpeed());
obj.addProperty(FLY_SPEED, limboPlayer.getFlySpeed());
return obj;
}
}
@@ -1,128 +0,0 @@
/*
* Copyright (C) 2015 AuthMe-Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.xephi.authme.listener.protocollib;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
class FreezePacketAdapter extends PacketAdapter {
private static final String ATTRIBUTE_MOVEMENT_SPEED = "generic.movementSpeed";
private static final String ATTRIBUTE_FLYING_SPEED = "generic.flyingSpeed";
private final ConsoleLogger logger = ConsoleLoggerFactory.get(FreezePacketAdapter.class);
private final PlayerCache playerCache;
private final DataSource dataSource;
FreezePacketAdapter(AuthMe plugin, PlayerCache playerCache, DataSource dataSource) {
super(plugin, PacketType.Play.Server.UPDATE_ATTRIBUTES);
this.playerCache = playerCache;
this.dataSource = dataSource;
}
@Override
public void onPacketSending(PacketEvent packetEvent) {
Player player = packetEvent.getPlayer();
PacketContainer packet = packetEvent.getPacket();
int entityId = packet.getIntegers().read(0);
if (entityId != player.getEntityId()) {
return;
}
if (!shouldFreeze(player.getName())) {
return;
}
List<WrappedAttribute> newAttributes = new ArrayList<>();
for (WrappedAttribute attribute : packet.getAttributeCollectionModifier().read(0)) {
if (ATTRIBUTE_MOVEMENT_SPEED.equals(attribute.getAttributeKey())
|| ATTRIBUTE_FLYING_SPEED.equals(attribute.getAttributeKey())) {
newAttributes.add(WrappedAttribute.newBuilder(attribute)
.baseValue(0.0f).modifiers(Collections.emptyList()).build());
} else {
newAttributes.add(attribute);
}
}
packet.getAttributeCollectionModifier().write(0, newAttributes);
}
protected void register(BukkitService bukkitService) {
ProtocolLibrary.getProtocolManager().addPacketListener(this);
bukkitService.getOnlinePlayers().stream()
.filter(player -> shouldFreeze(player.getName()))
.forEach(this::sendFreezePacket);
}
private boolean shouldFreeze(String playerName) {
return !playerCache.isAuthenticated(playerName) && dataSource.isAuthAvailable(playerName);
}
public void unregister() {
ProtocolLibrary.getProtocolManager().removePacketListener(this);
}
protected void sendFreezePacket(Player player) {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
PacketContainer attributesPacket = protocolManager.createPacket(PacketType.Play.Server.UPDATE_ATTRIBUTES);
attributesPacket.getIntegers().write(0, player.getEntityId());
attributesPacket.getAttributeCollectionModifier().write(0, Arrays.asList(
WrappedAttribute.newBuilder()
.packet(attributesPacket)
.attributeKey(ATTRIBUTE_MOVEMENT_SPEED)
.baseValue(0.0f)
.build(),
WrappedAttribute.newBuilder()
.packet(attributesPacket)
.attributeKey(ATTRIBUTE_FLYING_SPEED)
.baseValue(0.0f)
.build()
));
try {
protocolManager.sendServerPacket(player, attributesPacket, false);
} catch (InvocationTargetException invocationExc) {
logger.logException("Error during sending freeze packet", invocationExc);
}
}
public void sendUnFreezePacket(Player player) {
player.setWalkSpeed(player.getWalkSpeed());
player.setFlySpeed(player.getFlySpeed());
}
}
@@ -22,12 +22,10 @@ public class ProtocolLibService implements SettingsDependent {
/* Packet Adapters */
private InventoryPacketAdapter inventoryPacketAdapter;
private TabCompletePacketAdapter tabCompletePacketAdapter;
private FreezePacketAdapter freezePacketAdapter;
/* Settings */
private boolean protectInvBeforeLogin;
private boolean denyTabCompleteBeforeLogin;
private boolean freezePlayerBeforeLogin;
/* Service */
private boolean isEnabled;
@@ -60,11 +58,6 @@ public class ProtocolLibService implements SettingsDependent {
logger.warning("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it...");
}
if (freezePlayerBeforeLogin) {
logger.warning("WARNING! In oder to prevent player movements in a nicer way consider"
+ " installing ProtocolLib!");
}
this.isEnabled = false;
return;
}
@@ -91,16 +84,6 @@ public class ProtocolLibService implements SettingsDependent {
tabCompletePacketAdapter = null;
}
if (freezePlayerBeforeLogin) {
if (freezePacketAdapter == null) {
freezePacketAdapter = new FreezePacketAdapter(plugin, playerCache, dataSource);
freezePacketAdapter.register(bukkitService);
}
} else if (freezePacketAdapter != null) {
freezePacketAdapter.unregister();
freezePacketAdapter = null;
}
this.isEnabled = true;
}
@@ -118,10 +101,6 @@ public class ProtocolLibService implements SettingsDependent {
tabCompletePacketAdapter.unregister();
tabCompletePacketAdapter = null;
}
if (freezePacketAdapter != null) {
freezePacketAdapter.unregister();
freezePacketAdapter = null;
}
}
/**
@@ -135,36 +114,12 @@ public class ProtocolLibService implements SettingsDependent {
}
}
/**
* Send a packet to the player to freeze any movement.
*
* @param player The player to send the packet to.
*/
public void sendFreezePacket(Player player) {
if (isEnabled && freezePacketAdapter != null) {
freezePacketAdapter.sendFreezePacket(player);
}
}
/**
* Send a packet to the player to unfreeze movements.
*
* @param player The player to send the packet to.
*/
public void sendUnFreezePacket(Player player) {
if (isEnabled && freezePacketAdapter != null) {
freezePacketAdapter.sendUnFreezePacket(player);
}
}
@Override
public void reload(Settings settings) {
final boolean oldProtectInventory = this.protectInvBeforeLogin;
final boolean oldFreezePlayer = this.freezePlayerBeforeLogin;
boolean oldProtectInventory = this.protectInvBeforeLogin;
this.protectInvBeforeLogin = settings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN);
this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
this.freezePlayerBeforeLogin = !settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT);
//it was true and will be deactivated now, so we need to restore the inventory for every player
if (oldProtectInventory && !protectInvBeforeLogin && inventoryPacketAdapter != null) {
@@ -175,14 +130,6 @@ public class ProtocolLibService implements SettingsDependent {
}
}
}
if (oldFreezePlayer && !freezePlayerBeforeLogin && freezePacketAdapter != null) {
freezePacketAdapter.unregister();
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
if (!playerCache.isAuthenticated(onlinePlayer.getName())) {
freezePacketAdapter.sendUnFreezePacket(onlinePlayer);
}
}
}
setup();
}
@@ -6,7 +6,6 @@ import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.LoginEvent;
import fr.xephi.authme.events.RestoreInventoryEvent;
import fr.xephi.authme.listener.protocollib.ProtocolLibService;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.process.SynchronousProcess;
@@ -24,7 +23,6 @@ import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject;
import java.util.List;
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT;
import static fr.xephi.authme.settings.properties.RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN;
public class ProcessSyncPlayerLogin implements SynchronousProcess {
@@ -59,9 +57,6 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
@Inject
private PermissionsManager permissionsManager;
@Inject
private ProtocolLibService protocolLibService;
ProcessSyncPlayerLogin() {
}
@@ -92,9 +87,6 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
if (commonService.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) {
restoreInventory(player);
}
if (!commonService.getProperty(ALLOW_UNAUTHED_MOVEMENT)) {
protocolLibService.sendUnFreezePacket(player);
}
final PlayerAuth auth = dataSource.getAuth(name);
teleportationService.teleportOnLogin(player, auth, limbo);
@@ -56,9 +56,6 @@ public class ProcessSyncPlayerLogout implements SynchronousProcess {
if (service.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN)) {
protocolLibService.sendBlankInventoryPacket(player);
}
if (!service.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)){
protocolLibService.sendFreezePacket(player);
}
applyLogoutEffect(player);
commandManager.runCommandsOnLogout(player);
@@ -5,6 +5,7 @@ import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.configurationdata.CommentsConfiguration;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.data.limbo.AllowFlightRestoreType;
import fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType;
import fr.xephi.authme.data.limbo.persistence.LimboPersistenceType;
import fr.xephi.authme.data.limbo.persistence.SegmentSize;
@@ -18,7 +19,7 @@ public final class LimboSettings implements SettingsHolder {
@Comment({
"Besides storing the data in memory, you can define if/how the data should be persisted",
"on disk. This is useful in case of a server crash, so next time the server starts we can",
"properly restore things like OP status and the ability to fly",
"properly restore things like OP status, ability to fly, and walk/fly speed.",
"DISABLED: no disk storage,",
"INDIVIDUAL_FILES: each player data in its own file,",
"DISTRIBUTED_FILES: distributes players into different files based on their UUID, see below"
@@ -48,6 +49,23 @@ public final class LimboSettings implements SettingsHolder {
public static final Property<AllowFlightRestoreType> RESTORE_ALLOW_FLIGHT =
newProperty(AllowFlightRestoreType.class, "limbo.restoreAllowFlight", AllowFlightRestoreType.RESTORE);
@Comment({
"Restore fly speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.",
"RESTORE: restore the speed the player had;",
"DEFAULT: always set to default speed;",
"MAX_RESTORE: take the maximum of the player's current speed and the previous one",
"RESTORE_NO_ZERO: Like 'restore' but sets speed to default if the player's speed was 0"
})
public static final Property<WalkFlySpeedRestoreType> RESTORE_FLY_SPEED =
newProperty(WalkFlySpeedRestoreType.class, "limbo.restoreFlySpeed", WalkFlySpeedRestoreType.RESTORE_NO_ZERO);
@Comment({
"Restore walk speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.",
"See above for a description of the values."
})
public static final Property<WalkFlySpeedRestoreType> RESTORE_WALK_SPEED =
newProperty(WalkFlySpeedRestoreType.class, "limbo.restoreWalkSpeed", WalkFlySpeedRestoreType.RESTORE_NO_ZERO);
private LimboSettings() {
}
@@ -55,7 +73,7 @@ public final class LimboSettings implements SettingsHolder {
public void registerComments(CommentsConfiguration conf) {
String[] limboExplanation = {
"Before a user logs in, various properties are temporarily removed from the player,",
"such as OP status and the ability to fly.",
"such as OP status, ability to fly, and walk/fly speed.",
"Once the user is logged in, we add back the properties we previously saved.",
"In this section, you may define how these properties should be handled.",
"Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Limbo-players"
@@ -22,28 +22,32 @@ public final class LimboPlayerMatchers {
public static Matcher<LimboPlayer> isLimbo(LimboPlayer limbo) {
String[] groups = limbo.getGroups().toArray(new String[limbo.getGroups().size()]);
return isLimbo(limbo.isOperator(), limbo.isCanFly(), groups);
return isLimbo(limbo.isOperator(), limbo.isCanFly(), limbo.getWalkSpeed(), limbo.getFlySpeed(), groups);
}
public static Matcher<LimboPlayer> isLimbo(boolean isOp, boolean canFly, String... groups) {
public static Matcher<LimboPlayer> isLimbo(boolean isOp, boolean canFly, float walkSpeed, float flySpeed,
String... groups) {
return new TypeSafeMatcher<LimboPlayer>() {
@Override
protected boolean matchesSafely(LimboPlayer item) {
return item.isOperator() == isOp
&& collectionContains(item.getGroups(), groups)
&& item.isCanFly() == canFly;
&& item.isCanFly() == canFly
&& walkSpeed == item.getWalkSpeed()
&& flySpeed == item.getFlySpeed();
}
@Override
public void describeTo(Description description) {
description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s",
isOp, String.join(" ,", groups), canFly));
description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s, walkSpeed=%f, flySpeed=%f",
isOp, String.join(" ,", groups), canFly, walkSpeed, flySpeed));
}
@Override
public void describeMismatchSafely(LimboPlayer item, Description description) {
description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s",
item.isOperator(), String.join(" ,", item.getGroups()), item.isCanFly()));
description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s, walkSpeed=%f, flySpeed=%f",
item.isOperator(), String.join(" ,", item.getGroups()), item.isCanFly(),
item.getWalkSpeed(), item.getFlySpeed()));
}
};
}
@@ -106,7 +106,7 @@ public class LimboPlayerTaskManagerTest {
String name = "rats";
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList("grp"), false);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList("grp"), false, 0.1f, 0.0f);
MessageTask existingMessageTask = mock(MessageTask.class);
limboPlayer.setMessageTask(existingMessageTask);
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(8);
@@ -129,7 +129,7 @@ public class LimboPlayerTaskManagerTest {
String name = "race";
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList("grp"), false);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList("grp"), false, 0.1f, 0.0f);
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(12);
given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true);
String captcha = "M032";
@@ -180,7 +180,7 @@ public class LimboPlayerTaskManagerTest {
public void shouldCancelExistingTimeoutTask() {
// given
Player player = mock(Player.class);
LimboPlayer limboPlayer = new LimboPlayer(null, false, Collections.emptyList(), true);
LimboPlayer limboPlayer = new LimboPlayer(null, false, Collections.emptyList(), true, 0.3f, 0.1f);
BukkitTask existingTask = mock(BukkitTask.class);
limboPlayer.setTimeoutTask(existingTask);
given(settings.getProperty(RestrictionSettings.TIMEOUT)).willReturn(18);
@@ -37,9 +37,9 @@ public class LimboServiceHelperTest {
public void shouldMergeLimboPlayers() {
// given
Location newLocation = mock(Location.class);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList("grp-new"), false);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList("grp-new"), false, 0.0f, 0.0f);
Location oldLocation = mock(Location.class);
LimboPlayer oldLimbo = new LimboPlayer(oldLocation, true, Collections.singletonList("grp-old"), true);
LimboPlayer oldLimbo = new LimboPlayer(oldLocation, true, Collections.singletonList("grp-old"), true, 0.1f, 0.8f);
// when
LimboPlayer result = limboServiceHelper.merge(newLimbo, oldLimbo);
@@ -49,14 +49,16 @@ public class LimboServiceHelperTest {
assertThat(result.isOperator(), equalTo(true));
assertThat(result.getGroups(), contains("grp-old"));
assertThat(result.isCanFly(), equalTo(true));
assertThat(result.getWalkSpeed(), equalTo(0.1f));
assertThat(result.getFlySpeed(), equalTo(0.8f));
}
@Test
public void shouldFallBackToNewLimboForMissingData() {
// given
Location newLocation = mock(Location.class);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList("grp-new"), true);
LimboPlayer oldLimbo = new LimboPlayer(null, false, Collections.emptyList(), false);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList("grp-new"), true, 0.3f, 0.0f);
LimboPlayer oldLimbo = new LimboPlayer(null, false, Collections.emptyList(), false, 0.1f, 0.1f);
// when
LimboPlayer result = limboServiceHelper.merge(newLimbo, oldLimbo);
@@ -66,6 +68,8 @@ public class LimboServiceHelperTest {
assertThat(result.isOperator(), equalTo(false));
assertThat(result.getGroups(), contains("grp-new"));
assertThat(result.isCanFly(), equalTo(true));
assertThat(result.getWalkSpeed(), equalTo(0.3f));
assertThat(result.getFlySpeed(), equalTo(0.1f));
}
@Test
@@ -79,7 +79,7 @@ public class LimboServiceTest {
@Test
public void shouldCreateLimboPlayer() {
// given
Player player = newPlayer("Bobby", true, false);
Player player = newPlayer("Bobby", true, 0.3f, false, 0.2f);
Location playerLoc = mock(Location.class);
given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(playerLoc);
given(permissionsManager.hasGroupSupport()).willReturn(true);
@@ -93,13 +93,17 @@ public class LimboServiceTest {
verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.LOG_IN));
verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class));
verify(player).setAllowFlight(false);
verify(player).setFlySpeed(0.0f);
verify(player).setWalkSpeed(0.0f);
assertThat(limboService.hasLimboPlayer("Bobby"), equalTo(true));
LimboPlayer limbo = limboService.getLimboPlayer("Bobby");
verify(authGroupHandler).setGroup(player, limbo, AuthGroupType.REGISTERED_UNAUTHENTICATED);
assertThat(limbo, not(nullValue()));
assertThat(limbo.isOperator(), equalTo(true));
assertThat(limbo.getWalkSpeed(), equalTo(0.3f));
assertThat(limbo.isCanFly(), equalTo(false));
assertThat(limbo.getFlySpeed(), equalTo(0.2f));
assertThat(limbo.getLocation(), equalTo(playerLoc));
assertThat(limbo.getGroups(), equalTo(Collections.singletonList("permgrwp")));
}
@@ -107,7 +111,7 @@ public class LimboServiceTest {
@Test
public void shouldNotKeepOpStatusForUnregisteredPlayer() {
// given
Player player = newPlayer("CharleS", true, true);
Player player = newPlayer("CharleS", true, 0.1f, true, 0.4f);
Location playerLoc = mock(Location.class);
given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(playerLoc);
given(permissionsManager.hasGroupSupport()).willReturn(false);
@@ -121,12 +125,16 @@ public class LimboServiceTest {
verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class));
verify(permissionsManager, only()).hasGroupSupport();
verify(player).setAllowFlight(false);
verify(player).setFlySpeed(0.0f);
verify(player).setWalkSpeed(0.0f);
LimboPlayer limbo = limboService.getLimboPlayer("charles");
verify(authGroupHandler).setGroup(player, limbo, AuthGroupType.UNREGISTERED);
assertThat(limbo, not(nullValue()));
assertThat(limbo.isOperator(), equalTo(false));
assertThat(limbo.getWalkSpeed(), equalTo(0.1f));
assertThat(limbo.isCanFly(), equalTo(true));
assertThat(limbo.getFlySpeed(), equalTo(0.4f));
assertThat(limbo.getLocation(), equalTo(playerLoc));
assertThat(limbo.getGroups(), equalTo(Collections.emptyList()));
}
@@ -154,18 +162,22 @@ public class LimboServiceTest {
public void shouldRestoreData() {
// given
LimboPlayer limbo = Mockito.spy(convertToLimboPlayer(
newPlayer("John", true, false), null, Collections.emptyList()));
newPlayer("John", true, 0.4f, false, 0.0f), null, Collections.emptyList()));
getLimboMap().put("john", limbo);
Player player = newPlayer("John", false, false);
Player player = newPlayer("John", false, 0.2f, false, 0.7f);
given(settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)).willReturn(AllowFlightRestoreType.ENABLE);
given(settings.getProperty(LimboSettings.RESTORE_WALK_SPEED)).willReturn(WalkFlySpeedRestoreType.RESTORE);
given(settings.getProperty(LimboSettings.RESTORE_FLY_SPEED)).willReturn(WalkFlySpeedRestoreType.RESTORE_NO_ZERO);
// when
limboService.restoreData(player);
// then
verify(player).setOp(true);
verify(player).setWalkSpeed(0.4f);
verify(player).setAllowFlight(true);
verify(player).setFlySpeed(LimboPlayer.DEFAULT_FLY_SPEED);
verify(limbo).clearTasks();
verify(authGroupHandler).setGroup(player, limbo, AuthGroupType.LOGGED_IN);
assertThat(limboService.hasLimboPlayer("John"), equalTo(false));
@@ -220,15 +232,18 @@ public class LimboServiceTest {
return player;
}
private static Player newPlayer(String name, boolean isOp, boolean canFly) {
private static Player newPlayer(String name, boolean isOp, float walkSpeed, boolean canFly, float flySpeed) {
Player player = newPlayer(name);
given(player.isOp()).willReturn(isOp);
given(player.getWalkSpeed()).willReturn(walkSpeed);
given(player.getAllowFlight()).willReturn(canFly);
given(player.getFlySpeed()).willReturn(flySpeed);
return player;
}
private static LimboPlayer convertToLimboPlayer(Player player, Location location, Collection<String> groups) {
return new LimboPlayer(location, player.isOp(), groups, player.getAllowFlight());
return new LimboPlayer(location, player.isOp(), groups, player.getAllowFlight(),
player.getWalkSpeed(), player.getFlySpeed());
}
private Map<String, LimboPlayer> getLimboMap() {
@@ -0,0 +1,108 @@
package fr.xephi.authme.data.limbo;
import org.bukkit.entity.Player;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static fr.xephi.authme.data.limbo.LimboPlayer.DEFAULT_FLY_SPEED;
import static fr.xephi.authme.data.limbo.LimboPlayer.DEFAULT_WALK_SPEED;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.DEFAULT;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.MAX_RESTORE;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.RESTORE;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.RESTORE_NO_ZERO;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Test for {@link WalkFlySpeedRestoreType}.
*/
@RunWith(Parameterized.class)
public class WalkFlySpeedRestoreTypeTest {
private final TestParameters parameters;
public WalkFlySpeedRestoreTypeTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void shouldRestoreToExpectedValue() {
// given
LimboPlayer limbo = mock(LimboPlayer.class);
given(limbo.getWalkSpeed()).willReturn(parameters.givenLimboWalkSpeed);
given(limbo.getFlySpeed()).willReturn(parameters.givenLimboFlySpeed);
Player player = mock(Player.class);
given(player.getWalkSpeed()).willReturn(parameters.givenPlayerWalkSpeed);
given(player.getFlySpeed()).willReturn(parameters.givenPlayerFlySpeed);
// when
parameters.testedType.restoreWalkSpeed(player, limbo);
parameters.testedType.restoreFlySpeed(player, limbo);
// then
verify(player).setWalkSpeed(parameters.expectedWalkSpeed);
verify(player).setFlySpeed(parameters.expectedFlySpeed);
}
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> buildParams() {
List<TestParameters> parameters = Arrays.asList(
create(RESTORE).withLimbo(0.1f, 0.4f).withPlayer(0.3f, 0.9f).expect(0.1f, 0.4f),
create(RESTORE).withLimbo(0.9f, 0.2f).withPlayer(0.3f, 0.0f).expect(0.9f, 0.2f),
create(MAX_RESTORE).withLimbo(0.3f, 0.8f).withPlayer(0.5f, 0.2f).expect(0.5f, 0.8f),
create(MAX_RESTORE).withLimbo(0.4f, 0.2f).withPlayer(0.1f, 0.4f).expect(0.4f, 0.4f),
create(RESTORE_NO_ZERO).withLimbo(0.1f, 0.2f).withPlayer(0.5f, 0.1f).expect(0.1f, 0.2f),
create(RESTORE_NO_ZERO).withLimbo(0.0f, 0.005f).withPlayer(0.4f, 0.8f).expect(DEFAULT_WALK_SPEED, DEFAULT_FLY_SPEED),
create(DEFAULT).withLimbo(0.1f, 0.7f).withPlayer(0.4f, 0.0f).expect(DEFAULT_WALK_SPEED, DEFAULT_FLY_SPEED)
);
// Convert List<TestParameters> to List<Object[]>
return parameters.stream().map(p -> new Object[]{p}).collect(Collectors.toList());
}
private static TestParameters create(WalkFlySpeedRestoreType testedType) {
TestParameters params = new TestParameters();
params.testedType = testedType;
return params;
}
private static final class TestParameters {
private WalkFlySpeedRestoreType testedType;
private float givenLimboWalkSpeed;
private float givenLimboFlySpeed;
private float givenPlayerWalkSpeed;
private float givenPlayerFlySpeed;
private float expectedWalkSpeed;
private float expectedFlySpeed;
TestParameters withLimbo(float walkSpeed, float flySpeed) {
this.givenLimboWalkSpeed = walkSpeed;
this.givenLimboFlySpeed = flySpeed;
return this;
}
TestParameters withPlayer(float walkSpeed, float flySpeed) {
this.givenPlayerWalkSpeed = walkSpeed;
this.givenPlayerFlySpeed = flySpeed;
return this;
}
TestParameters expect(float walkSpeed, float flySpeed) {
this.expectedWalkSpeed = walkSpeed;
this.expectedFlySpeed = flySpeed;
return this;
}
@Override
public String toString() {
return testedType + " {" + expectedWalkSpeed + ", " + expectedFlySpeed + "}";
}
}
}
@@ -48,22 +48,22 @@ public class DistributedFilesPersistenceHandlerTest {
/** Player is in seg32-10110 and should be migrated into seg16-f. */
private static final UUID MIGRATED_UUID = fromString("f6a97c88-7c8f-c12e-4931-6206d4ca067d");
private static final Matcher<LimboPlayer> MIGRATED_LIMBO_MATCHER =
isLimbo(false, true, "noob");
isLimbo(false, true, 0.2f, 0.1f, "noob");
/** Existing player in seg16-f. */
private static final UUID UUID_FAB69 = fromString("fab69c88-2cd0-1fed-f00d-dead14ca067d");
private static final Matcher<LimboPlayer> FAB69_MATCHER =
isLimbo(false, false, "");
isLimbo(false, false, 0.2f, 0.1f, "");
/** Player in seg16-8. */
private static final UUID UUID_STAFF = fromString("88897c88-7c8f-c12e-4931-6206d4ca067d");
private static final Matcher<LimboPlayer> STAFF_MATCHER =
isLimbo(true, false, "staff", "mod");
isLimbo(true, false, 0.3f, 0.1f, "staff", "mod");
/** Player in seg16-8. */
private static final UUID UUID_8C679 = fromString("8c679491-1234-abcd-9102-1fa6e0cc3f81");
private static final Matcher<LimboPlayer> SC679_MATCHER =
isLimbo(false, true, "primary");
isLimbo(false, true, 0.1f, 0.0f, "primary");
/** UUID for which no data is stored (belongs to a segment file that does not exist, seg16-4). */
private static final UUID UNKNOWN_UUID = fromString("42d1cc0b-8f12-d04a-e7ba-a067d05cdc39");
@@ -156,10 +156,10 @@ public class DistributedFilesPersistenceHandlerTest {
// given
Player uuidToAdd1 = mockPlayerWithUuid(UNKNOWN_UUID);
Location location1 = mockLocation("1world");
LimboPlayer limbo1 = new LimboPlayer(location1, false, Collections.singletonList("group-1"), true);
LimboPlayer limbo1 = new LimboPlayer(location1, false, Collections.singletonList("group-1"), true, 0.1f, 0.2f);
Player uuidToAdd2 = mockPlayerWithUuid(UNKNOWN_UUID2);
Location location2 = mockLocation("2world");
LimboPlayer limbo2 = new LimboPlayer(location2, true, Collections.emptyList(), false);
LimboPlayer limbo2 = new LimboPlayer(location2, true, Collections.emptyList(), false, 0.0f, 0.25f);
// when
persistenceHandler.saveLimboPlayer(uuidToAdd1, limbo1);
@@ -78,6 +78,8 @@ public class IndividualFilesPersistenceHandlerTest {
assertThat(data, not(nullValue()));
assertThat(data.isOperator(), equalTo(true));
assertThat(data.isCanFly(), equalTo(true));
assertThat(data.getWalkSpeed(), equalTo(0.2f));
assertThat(data.getFlySpeed(), equalTo(0.1f));
assertThat(data.getGroups(), contains("players"));
Location location = data.getLocation();
assertThat(location.getX(), equalTo(-113.219));
@@ -112,7 +114,7 @@ public class IndividualFilesPersistenceHandlerTest {
World world = mock(World.class);
given(world.getName()).willReturn("player-world");
Location location = new Location(world, 0.2, 102.25, -89.28, 3.02f, 90.13f);
LimboPlayer limbo = new LimboPlayer(location, true, Collections.singletonList("primary-grp"), true);
LimboPlayer limbo = new LimboPlayer(location, true, Collections.singletonList("primary-grp"), true, 1.2f, 0.8f);
// when
handler.saveLimboPlayer(player, limbo);
@@ -9,5 +9,7 @@
},
"operator": true,
"can-fly": true,
"walk-speed": 0.2,
"fly-speed": 0.1,
"group": "players"
}
@@ -13,7 +13,9 @@
"mod"
],
"operator": true,
"can-fly": false
"can-fly": false,
"walk-speed": 0.3,
"fly-speed": 0.1
},
"8c679491-1234-abcd-9102-1fa6e0cc3f81": {
"location": {
@@ -26,6 +28,8 @@
},
"group": "primary",
"operator": false,
"can-fly": true
"can-fly": true,
"walk-speed": 0.1,
"fly-speed": 0.0
}
}
@@ -10,6 +10,8 @@
},
"group": "",
"operator": false,
"can-fly": false
"can-fly": false,
"walk-speed": 0.2,
"fly-speed": 0.1
}
}
@@ -10,6 +10,8 @@
},
"group": "noob",
"operator": false,
"can-fly": true
"can-fly": true,
"walk-speed": 0.2,
"fly-speed": 0.1
}
}