Skip to content

Commit

Permalink
Optimistic player data creation. Updating of world / player name. (+)
Browse files Browse the repository at this point in the history
Create PlayerData instances if events allow proceeding:
* AsyncPlayerPreLogin.
* PlayerLogin (schedule for removal if denied).

Update world data:
* PlayerLogin
* (PlayerJoin, ...)

Update player name and log:
* PlayerLogin
* PlayerJoin

(+) PlayerData.updateCurrentWorld -> only do something if the WorldData
instance has changed.
  • Loading branch information
asofold committed Apr 20, 2018
1 parent e20fe53 commit 6bddb1a
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 11 deletions.
Expand Up @@ -112,7 +112,10 @@ public class PlayerData implements IPlayerData {
private static float heavyLoad = 500000f / (float) ticksMonitored;

// Default tags.
// TODO: Move elsewhere (API?)
public static final String TAG_NOTIFY_OFF = "notify_off";
/** Optimistic player data creation. */
public static final String TAG_OPTIMISTIC_CREATE = "optimistic_create";

private static final short frequentTaskLazyDefaultDelay = 10;
private static final short frequentTaskUnregisterDefaultDelay = 2;
Expand Down Expand Up @@ -142,9 +145,9 @@ public class PlayerData implements IPlayerData {

// TODO: Names could/should get updated. (In which case?)
/** Exact case name of the player. */
private final String playerName;
private String playerName;
/** Lower case name of the player. */
private final String lcName;
private String playerNameLowerCase;

private long lastJoinTime = 0;

Expand Down Expand Up @@ -195,10 +198,15 @@ public PlayerData(final UUID playerId, final String playerName,
final PermissionRegistry permissionRegistry) {
this.playerId = playerId;
this.playerName = playerName;
this.lcName = playerName.toLowerCase();
this.playerNameLowerCase = playerName.toLowerCase();
this.permissionRegistry = permissionRegistry;
}

void updatePlayerName(final String exactPlayerName) {
this.playerName = exactPlayerName;
this.playerNameLowerCase = exactPlayerName.toLowerCase();
}

/**
* Run with DataManager frequent tasks (each tick).
*
Expand Down Expand Up @@ -486,8 +494,10 @@ private void updateCurrentWorld(final World world,
*/
void updateCurrentWorld(final IWorldData worldData) {
// TODO: Consider storing last world too.
currentWorldData = worldData;
checkTypeTree.getNode(CheckType.ALL).updateDebug(worldData);
if (currentWorldData != worldData) {
currentWorldData = worldData;
checkTypeTree.getNode(CheckType.ALL).updateDebug(worldData);
}
}

private void invalidateOffline() {
Expand Down Expand Up @@ -627,7 +637,7 @@ public String getPlayerName() {

@Override
public String getPlayerNameLowerCase() {
return lcName;
return playerNameLowerCase;
}

@Override
Expand Down
Expand Up @@ -33,9 +33,11 @@
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.WorldUnloadEvent;

Expand Down Expand Up @@ -219,6 +221,31 @@ public void onEvent(final PlayerKickEvent event) {
playerLeaves(event.getPlayer());
}
},
new MiniListener<AsyncPlayerPreLoginEvent>() {
@EventHandler(priority = EventPriority.MONITOR)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", beforeTag = ".*")
@Override
public void onEvent(final AsyncPlayerPreLoginEvent event) {
// TODO: Maintain a flag for precondition (e.g. ProtocolLib present).
if (event.getLoginResult() == AsyncPlayerPreLoginEvent.Result.ALLOWED) {
onAsyncPlayerPreLogin(event);
}
}
},
new MiniListener<PlayerLoginEvent>() {
@EventHandler(priority = EventPriority.MONITOR)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", beforeTag = ".*")
@Override
public void onEvent(final PlayerLoginEvent event) {
// TODO: Maintain a flag for precondition (e.g. ProtocolLib present).
if (event.getResult() == PlayerLoginEvent.Result.ALLOWED) {
onPlayerLogin(event);
}
else {
onPlayerLoginDenied(event);
}
}
},
new MiniListener<PlayerJoinEvent>() {
@EventHandler(priority = EventPriority.LOWEST)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", beforeTag = ".*")
Expand Down Expand Up @@ -555,6 +582,70 @@ private void removeOnlinePlayer(final Player player) {
playerMap.remove(player);
}

/**
* Ensure a PlayerData instance exists for later use.
*
* @param event
*/
private void onAsyncPlayerPreLogin(final AsyncPlayerPreLoginEvent event) {
final UUID playerId = event.getUniqueId(); // Treat carefully :).
if (playerData.containsKey(playerId)) {
// Skip if a PlayerData instance already exists.
return;
}
else {
// Create with default world data.
getPlayerData(playerId, event.getName(), true, worldDataManager.getDefaultWorldData()).addTag(PlayerData.TAG_OPTIMISTIC_CREATE);
}
}

private void onPlayerLoginDenied(final PlayerLoginEvent event) {
// Consistency check existing data.
final UUID playerId = event.getPlayer().getUniqueId();
final PlayerData pData = getPlayerData(playerId);
if (pData != null && pData.hasTag(PlayerData.TAG_OPTIMISTIC_CREATE)) {
bulkPlayerDataRemoval.add(playerId);
}
}

/**
* Just update the world data for later use.
*
* @param event
*/
private void onPlayerLogin(final PlayerLoginEvent event) {
// Consistency check existing data.
final Player player = event.getPlayer();
final UUID playerId = player.getUniqueId();
final PlayerData pData = getPlayerData(playerId);
if (pData == null) {
// Create an instance.
// TODO: Legacy server compatibility with world getting?
getPlayerData(player);
}
else {
// Consistency check.
final String playerName = pData.getPlayerName();
if (!playerName.equals(player.getName())) {
updatePlayerName(playerId, playerName, pData, "login");
}
// Update world.
pData.updateCurrentWorld(worldDataManager.getWorldData(player.getWorld()));
}
pData.removeTag(PlayerData.TAG_OPTIMISTIC_CREATE);
}

private void updatePlayerName(final UUID playerId, final String playerName,
final PlayerData pData, String tag) {
// Name change.
pData.updatePlayerName(playerName);
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().info(Streams.STATUS,
CheckUtils.getLogMessagePrefix(playerName, null)
+ " Update player name for id " + playerId + ": " + playerName
+ "(" + tag + (pData.hasTag(PlayerData.TAG_OPTIMISTIC_CREATE) ?
", optimistically created data" : "") + ")");
}

private void playerJoins(final PlayerJoinEvent event) {
final long timeNow = System.currentTimeMillis();
final Player player = event.getPlayer();
Expand All @@ -564,10 +655,12 @@ private void playerJoins(final PlayerJoinEvent event) {
addOnlinePlayer(player);
//
final PlayerData pData = getPlayerData(player, true);
/*
* TODO: Now we update the world already with getPlayerData, in case
* it's just been created...
*/
// Consistency check.
final String playerName = pData.getPlayerName();
if (!playerName.equals(player.getName())) {
updatePlayerName(playerId, playerName, pData, "login");
}
// Data stuff.
final Collection<Class<? extends IDataOnJoin>> types = factoryRegistry.getGroupedTypes(IDataOnJoin.class);
pData.onPlayerJoin(player, player.getWorld(), timeNow, worldDataManager, types);
pData.getGenericInstance(CombinedData.class).lastJoinTime = timeNow;
Expand Down
Expand Up @@ -1278,7 +1278,7 @@ public void onPlayerLogin(final PlayerLoginEvent event) {
}
final Player player = event.getPlayer();
// Check if login is denied (plus expiration check).
// TODO: Store by id + HashMapLOW.
// TODO: Store by id + HashMapLOW + AsyncPlayerPreLogin.
if (checkDenyLoginsNames(player.getName())) {
if (DataManager.getPlayerData(player).hasPermission(Permissions.BYPASS_DENY_LOGIN, player)) {
return;
Expand Down

0 comments on commit 6bddb1a

Please sign in to comment.