/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.internal.providers;
import com.jcwhatever.nucleus.Nucleus;
import com.jcwhatever.nucleus.internal.NucMsg;
import com.jcwhatever.nucleus.providers.Provider;
import com.jcwhatever.nucleus.providers.playerlookup.IPlayerLookupProvider;
import com.jcwhatever.nucleus.providers.storage.DataStorage;
import com.jcwhatever.nucleus.storage.IDataNode;
import com.jcwhatever.nucleus.utils.CollectionUtils;
import com.jcwhatever.nucleus.utils.PreCon;
import com.jcwhatever.nucleus.utils.observer.future.FutureResultAgent;
import com.jcwhatever.nucleus.utils.observer.future.IFutureResult;
import com.jcwhatever.nucleus.utils.text.TextUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.Plugin;
import javax.annotation.Nullable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Deque;
import java.util.List;
import java.util.UUID;
/**
* Nucleus frameworks default player lookup provider
*/
public final class InternalPlayerLookupProvider extends Provider implements IPlayerLookupProvider {
private final Object _dataSync = new Object();
private volatile IDataNode _nameData;
public InternalPlayerLookupProvider(Plugin plugin) {
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(), plugin);
setInfo(new InternalProviderInfo(this.getClass(),
"NucleusPlayerLookup", "Default player lookup provider."));
}
@Nullable
@Override
public UUID getPlayerId(String playerName) {
PreCon.notNullOrEmpty(playerName);
// check for online player first
Player p = Bukkit.getPlayer(playerName);
if (p != null)
return p.getUniqueId();
// check stored id/name map
IDataNode nameData = getPlayerData();
synchronized (_dataSync) {
for (IDataNode node : nameData) {
String name = node.getString("name");
if (name != null && name.equalsIgnoreCase(playerName)) {
try {
return UUID.fromString(node.getName());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
}
return null;
}
@Nullable
@Override
public String getPlayerName(UUID playerId) {
PreCon.notNull(playerId);
IDataNode data = getPlayerData(playerId);
if (data == null)
return null;
synchronized (_dataSync) {
return data.getString("name");
}
}
@Nullable
@Override
public Date getFirstLogin(UUID playerId) {
PreCon.notNull(playerId);
IDataNode data = getPlayerData(playerId);
if (data == null)
return null;
synchronized (_dataSync) {
return data.getDate("first");
}
}
@Nullable
@Override
public Date getLastLogin(UUID playerId) {
PreCon.notNull(playerId);
IDataNode data = getPlayerData(playerId);
if (data == null)
return null;
synchronized (_dataSync) {
return data.getDate("last");
}
}
@Override
public int getLoginCount(UUID playerId) {
PreCon.notNull(playerId);
IDataNode data = getPlayerData(playerId);
if (data == null)
return 0;
synchronized (_dataSync) {
return data.getInteger("count", 0);
}
}
@Override
public IFutureResult<Collection<UUID>> searchNames(String searchText, int maxResults) {
Deque<PlayerData> unsorted = new ArrayDeque<>(15);
String lower = searchText.toLowerCase();
IDataNode playersNode = getPlayerData();
for (IDataNode node : playersNode) {
String name = node.getString("name");
if (name == null)
continue;
if (name.toLowerCase().contains(lower))
unsorted.add(new PlayerData(name, TextUtils.parseUUID(node.getName())));
}
Collection<PlayerData> sorted = CollectionUtils.textSearch(
unsorted, searchText, new CollectionUtils.ISearchTextGetter<PlayerData>() {
@Override
public String getText(PlayerData element) {
return element.name;
}
});
List<UUID> result = new ArrayList<>(Math.min(sorted.size(), maxResults));
int count = 0;
for (PlayerData data : sorted) {
if (count >= maxResults)
break;
result.add(data.id);
count++;
}
return new FutureResultAgent<Collection<UUID>>().success(result);
}
/*
* Update the NucleusFramework player name/player id map.
*/
private void setPlayerName(UUID playerId, String name) {
PreCon.notNull(playerId);
PreCon.notNullOrEmpty(name);
Date loginDate = new Date();
IDataNode data = getPlayerData(playerId);
synchronized (_dataSync) {
if (data == null) {
data = getPlayerData().getNode(playerId.toString());
data.set("first", loginDate);
}
int count = data.getInteger("count", 0);
data.set("last", loginDate);
data.set("count", count + 1);
}
String currentName = getPlayerName(playerId);
if (name.equals(currentName))
return;
synchronized (_dataSync) {
data.set("name", name);
}
data.save();
}
// get the node that contains player id/name data.
private IDataNode getPlayerData() {
if (_nameData == null) {
synchronized (_dataSync) {
if (_nameData != null)
return _nameData;
String dataPath = Bukkit.getOnlineMode() ? "player-data" : "offline-player-data";
IDataNode data = DataStorage.get(Nucleus.getPlugin(), getDataPath(dataPath));
if (!data.load()) {
NucMsg.warning("Failed to load player data file.");
}
_nameData = data;
}
}
return _nameData;
}
@Nullable
private IDataNode getPlayerData(UUID playerId) {
IDataNode data = getPlayerData();
String nodeName = playerId.toString();
if (!data.hasNode(nodeName))
return null;
return data.getNode(nodeName);
}
private class BukkitEventListener implements Listener {
@EventHandler(priority= EventPriority.LOW)
private void onPlayerJoin(PlayerJoinEvent event) {
final Player p = event.getPlayer();
// update player name in id lookup
setPlayerName(p.getUniqueId(), p.getName());
}
}
private static class PlayerData {
String name;
UUID id;
PlayerData(String name, UUID id) {
this.name = name;
this.id = id;
}
}
}