PandaLib/Core/src/main/java/fr/pandacube/lib/core/players/IOffPlayer.java

461 lines
12 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package fr.pandacube.lib.core.players;
import java.util.Calendar;
import java.util.OptionalLong;
import java.util.UUID;
import java.util.stream.LongStream;
import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.chat.ChatColorUtil;
import fr.pandacube.lib.db.DBException;
import fr.pandacube.lib.permissions.PermPlayer;
import fr.pandacube.lib.permissions.Permissions;
import fr.pandacube.lib.util.Log;
import static fr.pandacube.lib.chat.ChatStatic.dataText;
import static fr.pandacube.lib.chat.ChatStatic.successText;
import static fr.pandacube.lib.chat.ChatStatic.text;
import static fr.pandacube.lib.chat.ChatStatic.warningText;
public interface IOffPlayer {
/** From how long the last web activity should be before considering the user offline (in ms)? */
long TIMEOUT_WEB_SESSION = 10000; // msec
/*
* General data and state
*/
/**
* Return the ID of the minecraft account.
*
* @return the id of the player
*/
UUID getUniqueId();
/**
* Tells if the current account is an alt account generated by Pandacube.
*
* An alt account uses a specific bit in the UUID to distinguish themselves from the original account.
*/
default boolean isAltAccount() {
return (getUniqueId().getMostSignificantBits() & 0x8000L) == 0x8000L;
}
/**
* Gets the index of the current alt account generated by Pandacube.
*
* The first generated alt account will be numbered 1, the second 2, ...
*
* This method will return undetermined value if {@link #isAltAccount()} is false.
*/
default int getAltIndex() {
return (int) (getUniqueId().getMostSignificantBits() >> 8) & 0xF;
}
/**
* @return the last known player name of this player, or null if this player never joined the network.
*/
default String getName() {
return PlayerFinder.getLastKnownName(getUniqueId());
}
/**
* Indicate if this player is connected to the current node (server or proxy, depending on interface implementation)
* @return wether the player is online or not
*/
boolean isOnline();
/**
* Provides informations of the online status of the player:
* online in game, online on the website, or offline.
* If the player is online in game, it provides the current server they are
* connected.
*/
default PlayerStatusOnServer getPlayerStatus() {
IOnlinePlayer op = getOnlineInstance();
if (op != null && !op.isVanished())
return new PlayerStatusOnServer(PlayerStatusOnServer.PlayerStatus.ONLINE_IG, op.getServerName());
try {
SQLPlayer webSession = getDbPlayer();
if (webSession != null) {
long lastWebActivity = webSession.get(SQLPlayer.lastWebActivity);
if (System.currentTimeMillis() - lastWebActivity < TIMEOUT_WEB_SESSION)
return new PlayerStatusOnServer(PlayerStatusOnServer.PlayerStatus.ONLINE_WEB, null);
}
} catch (Exception e) {
Log.severe(e);
}
return new PlayerStatusOnServer(PlayerStatusOnServer.PlayerStatus.OFFLINE, null);
}
record PlayerStatusOnServer(PlayerStatus status, String server) {
public Chat toComponent() {
if (status == PlayerStatus.ONLINE_IG)
return successText("En ligne, " + server);
if (status == PlayerStatus.ONLINE_WEB)
return warningText("En ligne, web");
if (status == PlayerStatus.OFFLINE)
return dataText("Hors ligne");
return text("N/A");
}
public enum PlayerStatus {
ONLINE_IG, ONLINE_WEB, OFFLINE
}
}
/*
* Floodgate related stuff
*/
default boolean isBedrockAccount() {
int v = getUniqueId().version();
return v == 0 || v == 8; // also 8 if one day we supports alt accounts for floodgate players
}
default boolean isJavaAccount() {
return !isBedrockAccount();
}
/*
* Related class instances
*/
/**
* Return the online instance of this player, if any exists.
* May return itself if the current instance already represent an online player.
*/
IOnlinePlayer getOnlineInstance();
/**
* Get the database entry of this player, or null if the player never joined the network.
*/
default SQLPlayer getDbPlayer() throws DBException {
return SQLPlayer.getPlayerFromUUID(getUniqueId());
}
/**
* Get the permission instance of this player. This will never return null.
* @return the permission instance of this player
*/
default PermPlayer getPermissionUser() {
return Permissions.getPlayer(getUniqueId());
}
/*
* Display name
*/
/**
* Returns the name of the player (if any), with eventual prefix and suffix depending on permission groups
* (and team for bukkit implementation)
* @return the display name of the player
*/
String getDisplayName();
/**
* Get an updated display name of the user,
* generated using eventual permissions prefix(es) and suffix(es) of the player,
* and with color codes translated to Minecrafts native {@code §}.
*/
default String getDisplayNameFromPermissionSystem() {
PermPlayer permU = getPermissionUser();
return ChatColorUtil.translateAlternateColorCodes('&',
permU.getPrefix() + getName() + permU.getSuffix());
}
/*
* Permissions and groups
*/
/**
* Tells if this player has the specified permission.
* If the player is online, this will redirect the
* method call to the {@link IOnlinePlayer} instance,
* that MUST override this current method to avoid recussive
* loop.
* If the player is offline, it just call the Pandacube
* permission system.
* @param permission the permission node to test
* @return whether this player has the provided permission
*/
default boolean hasPermission(String permission) {
IOnlinePlayer online = getOnlineInstance();
if (online != null)
return online.hasPermission(permission);
// at this point, the player is offline
return getPermissionUser().hasPermissionOr(permission, null, null, false);
}
/**
* Tells if this player has the permission resulted from the provided expression.
* If the player is online, this will redirect the
* method call to the {@link IOnlinePlayer} instance,
* that MUST override this current method to avoid recussive
* loop.
* If the player is offline, it just call the Pandacube
* permission system.
* @param permissionExpression the permission node to test
* @return whether this player has the provided permission
*/
default boolean hasPermissionExpression(String permissionExpression) {
IOnlinePlayer online = getOnlineInstance();
if (online != null)
return online.hasPermissionExpression(permissionExpression);
// at this point, the player is offline
return getPermissionUser().hasPermissionExpression(permissionExpression, null, null);
}
/**
* Lists all the values for a set of permission indicating an integer in a range.
* <p>
* A permission range is used to easily attribute a number to a group or player,
* like the maximum number of homes allowed. For instance, if the player has the permission
* {@code essentials.home.12}, this method would return a stream containing the value 12,
* if the parameter {@code permissionPrefix} is {@code "essentials.home."}.
* <p>
* The use of a stream allow the caller to get either the maximum, the minimum, or do any
* other treatment to the values.
* @param permissionPrefix the permission prefix to search for.
* @return a LongStream containing all the values found for the specified permission prefix.
*/
default LongStream getPermissionRangeValues(String permissionPrefix) {
IOnlinePlayer online = getOnlineInstance();
if (online != null)
return online.getPermissionRangeValues(permissionPrefix);
// at this point, the player is offline
return getPermissionUser().getPermissionRangeValues(permissionPrefix, null, null);
}
/**
* Returns the maximum value returned by {@link IOffPlayer#getPermissionRangeValues(String)}.
*/
default OptionalLong getPermissionRangeMax(String permissionPrefix) {
IOnlinePlayer online = getOnlineInstance();
if (online != null)
return online.getPermissionRangeMax(permissionPrefix);
// at this point, the player is offline
return getPermissionUser().getPermissionRangeMax(permissionPrefix, null, null);
}
/**
* Tells if the this player is part of the specified group
*
* @param group the permissions group
* @return <i>true</i> if this player is part of the group,
* <i>false</i> otherwise
*/
default boolean isInGroup(String group) {
return getPermissionUser().isInGroup(group);
}
/**
* Tells if this player is part of the staff, based on permission groups
*/
default boolean isInStaff() {
return getPermissionUser().inheritsFromGroup("staff-base", true);
}
/*
* Ignore
*/
/**
* Tells if this player have the right to ignore the provided player
* @param ignored the player that is potentially ignored by this player.
* If this parameter is null, this method returns false.
*/
default boolean canIgnore(IOffPlayer ignored) {
if (ignored == null)
return false;
if (equals(ignored))
return false;
if (!isInStaff() && !ignored.isInStaff())
return true;
return hasPermission("pandacube.ignore.bypassfor." + ignored.getUniqueId());
}
/**
* Tells if the provided player have the right to ignore this player
* @param ignorer the player that potentially ignore this player
* If this parameter is null, this method returns false.
* @implNote the default implementation just calls {@link #canIgnore(IOffPlayer) ignorer.canIgnore(this)}.
*/
default boolean canBeIgnoredBy(IOffPlayer ignorer) {
if (ignorer == null)
return false;
return ignorer.canIgnore(this);
}
/**
* Determine if this player ignore the provided player.
* @param ignored the player that is potentially ignored by this player.
* If this parameter is null, this method returns false.
* @return true if this player have to right to ignore the provided player and is actually ignoring him.
*/
default boolean isIgnoring(IOffPlayer ignored) {
if (!canIgnore(ignored))
return false;
try {
return SQLPlayerIgnore.isPlayerIgnoringPlayer(getUniqueId(), ignored.getUniqueId());
} catch (DBException e) {
Log.severe("Can't determine if a player ignore another player, because we can't access to the database", e);
return false;
}
}
/**
* Determine if the provided player ignore this player, taking into account the exception permissions.
* @param ignorer the player that potentially ignore this player
* If this parameter is null, this method returns false.
* @return true if the provided player have to right to ignore this player and is actually ignoring him.
* @implNote the default implementation just calls {@link #isIgnoring(IOffPlayer) ignorer.isIgnoring(this)}.
*/
default boolean isIgnoredBy(IOffPlayer ignorer) {
return ignorer.isIgnoring(this);
}
/*
* Modération
*/
/**
* Retrieve the time when the player will be unmuted, or null if the player is not muted.
* @return the timestamp in millisecond of when the player will be unmuted
*/
default Long getMuteTimeout() {
try {
Long muteTimeout = getDbPlayer().get(SQLPlayer.muteTimeout);
if (muteTimeout == null || muteTimeout <= System.currentTimeMillis())
return null;
return muteTimeout;
} catch (DBException e) {
Log.severe(e);
return null;
}
}
/**
* Tells if the player is currently muted, meaning that they cannot communicate
* through the chat or private messages.
*/
default boolean isMuted() {
return getMuteTimeout() != null;
}
/*
* Birthday
*/
default void setBirthday(int day, int month, Integer year) {
try {
SQLPlayer dbPlayer = getDbPlayer();
dbPlayer.setBirthday(day, month, year);
dbPlayer.save();
} catch (DBException e) {
throw new RuntimeException(e);
}
}
default Calendar getBirthday() {
try {
return getDbPlayer().getBirthday();
} catch (DBException e) {
throw new RuntimeException(e);
}
}
/*
* Player config
*/
default String getConfig(String key) throws DBException {
return SQLPlayerConfig.get(getUniqueId(), key);
}
default String getConfig(String key, String deflt) throws DBException {
return SQLPlayerConfig.get(getUniqueId(), key, deflt);
}
default void setConfig(String key, String value) throws DBException {
SQLPlayerConfig.set(getUniqueId(), key, value);
}
default void unsetConfig(String key) throws DBException {
SQLPlayerConfig.unset(getUniqueId(), key);
}
default boolean isWelcomeQuizzDone() {
try {
return Boolean.parseBoolean(getConfig("welcome.quizz.done", "false"));
} catch (DBException e) {
Log.severe("Error knowing if player has already done the quizz. Assuming they did for now.", e);
return true;
}
}
}