diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendManager.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendManager.java index 164dfdf41..c8384d418 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendManager.java @@ -7,6 +7,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.ClickEvent; @@ -17,6 +18,7 @@ import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; import mineplex.core.MiniDbClientPlugin; import mineplex.core.ReflectivelyCreateMiniPlugin; @@ -24,19 +26,24 @@ import mineplex.core.account.permissions.Permission; import mineplex.core.account.permissions.PermissionGroup; import mineplex.core.common.util.C; import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; import mineplex.core.common.util.UtilServer; import mineplex.core.common.util.UtilTime; import mineplex.core.friend.command.AddFriend; import mineplex.core.friend.command.DeleteFriend; +import mineplex.core.friend.command.FriendFavouriteCommand; +import mineplex.core.friend.command.FriendVisibilityCommand; import mineplex.core.friend.command.FriendsDisplay; import mineplex.core.friend.data.FriendData; import mineplex.core.friend.data.FriendRepository; import mineplex.core.friend.data.FriendStatus; +import mineplex.core.friend.redis.FriendAddMessage; import mineplex.core.portal.Portal; import mineplex.core.preferences.Preference; import mineplex.core.preferences.PreferencesManager; import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; +import mineplex.serverdata.commands.ServerCommandManager; import mineplex.serverdata.data.PlayerStatus; @ReflectivelyCreateMiniPlugin @@ -102,6 +109,27 @@ public class FriendManager extends MiniDbClientPlugin _repository = new FriendRepository(); generatePermissions(); + + ServerCommandManager.getInstance().registerCommandType(FriendAddMessage.class, command -> + { + Player target = UtilPlayer.searchExact(command.getTarget()); + + if (target == null) + { + return; + } + + for (FriendStatus status : Get(target).getFriends()) + { + if (status.Name.equals(command.getAccepter())) + { + target.sendMessage(F.main(getName(), F.name(status.Name) + " has accepted your friend request.")); + return; + } + } + + onFriendAdd(target, command.getAccepter()); + }); } private void generatePermissions() @@ -126,6 +154,8 @@ public class FriendManager extends MiniDbClientPlugin addCommand(new AddFriend(this)); addCommand(new DeleteFriend(this)); addCommand(new FriendsDisplay(this)); + addCommand(new FriendVisibilityCommand(this)); + addCommand(new FriendFavouriteCommand(this)); } @Override @@ -157,6 +187,34 @@ public class FriendManager extends MiniDbClientPlugin if (newPlayerData != null) { + List online = playerData.getFriends().stream() + .filter(FriendStatus::isOnline) + .map(friendStatus -> friendStatus.UUID) + .collect(Collectors.toList()); + List offline = playerData.getFriends().stream() + .filter(friendStatus -> !friendStatus.isOnline()) + .map(friendStatus -> friendStatus.UUID) + .collect(Collectors.toList()); + + newPlayerData.getFriends().forEach(friendStatus -> + { + if (!friendStatus.Favourite) + { + return; + } + + // Offline -> Online + if (friendStatus.isOnline() && offline.contains(friendStatus.UUID)) + { + player.sendMessage(F.main(getName(), F.name(friendStatus.Name) + " joined the network.")); + } + // Online -> Offline + else if (!friendStatus.isOnline() && online.contains(friendStatus.UUID)) + { + player.sendMessage(F.main(getName(), F.name(friendStatus.Name) + " left the network.")); + } + }); + playerData.setFriends(newPlayerData.getFriends()); } else @@ -168,6 +226,36 @@ public class FriendManager extends MiniDbClientPlugin }); } + @EventHandler + public void playerJoin(PlayerJoinEvent event) + { + Player player = event.getPlayer(); + + if (!_preferenceManager.get(player).isActive(Preference.PENDING_FRIEND_REQUESTS)) + { + return; + } + + FriendData data = Get(event.getPlayer()); + int pending = 0; + + for (FriendStatus status : data.getFriends()) + { + if (status.Status == FriendStatusType.Pending) + { + pending++; + } + } + + if (pending > 0) + { + player.spigot().sendMessage(new ComponentBuilder(F.main(getName(), "You have " + F.count(pending) + " pending friend request" + (pending == 1 ? "" : "s") + ".")) + .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(C.cGray + "Click to view your requests.").create())) + .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + AddFriend.COMMAND)) + .create()); + } + } + public void addFriend(final Player caller, final String name) { if (caller.getName().equalsIgnoreCase(name)) @@ -245,6 +333,8 @@ public class FriendManager extends MiniDbClientPlugin }); } + new FriendAddMessage(caller.getName(), name).publish(); + runSync(() -> { if (updateFinal) @@ -282,6 +372,75 @@ public class FriendManager extends MiniDbClientPlugin }); } + public void setVisibility(Player player, FriendVisibility visibility) + { + int accountId = ClientManager.getAccountId(player); + + runAsync(() -> + { + if (_repository.updateVisibility(accountId, visibility)) + { + runSync(() -> player.sendMessage(F.main(getName(), "Updated your friend visibility to " + F.elem(visibility.getName()) + "."))); + } + }); + } + + public void toggleFavourite(Player player, String target) + { + for (FriendStatus status : Get(player).getFriends()) + { + if (status.Name.equals(target)) + { + runAsync(() -> + { + if (_repository.updateFavourite(player.getName(), status.Name, !status.Favourite)) + { + runSync(() -> player.sendMessage(F.main(getName(), F.name(status.Name) + " is " + (status.Favourite ? "no longer" : "now") + " on your favourite friends."))); + } + }); + + return; + } + } + } + + public void onFriendAdd(Player viewer, String friend) + { + if (!_preferenceManager.get(viewer).isActive(Preference.PENDING_FRIEND_REQUESTS)) + { + return; + } + + TextComponent message = new TextComponent(""); + + TextComponent text = new TextComponent(F.main(getName(), F.name(friend) + " send you a friend request! ")); + + TextComponent accept = new TextComponent("ACCEPT"); + accept.setColor(ChatColor.GREEN); + accept.setBold(true); + accept.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Accept " + friend + "'s friendship request") + .create())); + accept.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + AddFriend.COMMAND + " " + friend)); + + TextComponent deny = new TextComponent("DENY"); + deny.setColor(ChatColor.RED); + deny.setBold(true); + deny.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Deny " + friend + "'s friendship request") + .create())); + deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + DeleteFriend.COMMAND + " " + friend)); + + TextComponent exclamation = new TextComponent("!"); + exclamation.setColor(ChatColor.GRAY); + + message.addExtra(text); + message.addExtra(accept); + message.addExtra(" "); + message.addExtra(deny); + message.addExtra(exclamation); + + viewer.spigot().sendMessage(message); + } + public void showFriends(Player caller) { boolean isStaff = ClientManager.Get(caller).hasPermission(Perm.JOIN_STAFF); @@ -294,7 +453,8 @@ public class FriendManager extends MiniDbClientPlugin // Use a LinkedHashMap so we maintain insertion order Map messages = new LinkedHashMap<>(); - String joinCommand = "/server ", friendCommand = "/" + AddFriend.COMMAND + " ", unfriendCommand = "/" + DeleteFriend.COMMAND + " ", favouriteCommand = "/Hello"; + String joinCommand = "/server ", friendCommand = "/" + AddFriend.COMMAND + " ", unfriendCommand = "/" + DeleteFriend.COMMAND + " "; + String favouriteCommand = "/" + FriendFavouriteCommand.COMMAND + " ", visiblityCommand = "/" + FriendVisibilityCommand.COMMAND + " "; for (FriendStatus friend : friendStatuses) { @@ -306,12 +466,13 @@ public class FriendManager extends MiniDbClientPlugin } TextComponent message = new TextComponent(""); + boolean online = friend.Online && friend.Visibility != FriendVisibility.INVISIBLE; boolean canJoin = canJoin(friend.ServerName, isStaff) && friend.Visibility == FriendVisibility.SHOWN; switch (type) { case Accepted: - if (friend.Online) + if (online) { if (canJoin) { @@ -352,7 +513,7 @@ public class FriendManager extends MiniDbClientPlugin { name.setColor(ChatColor.YELLOW); } - else if (friend.Online) + else if (online) { name.setColor(ChatColor.GREEN); } @@ -361,15 +522,14 @@ public class FriendManager extends MiniDbClientPlugin name.setColor(ChatColor.GRAY); } - name.setColor(friend.Online ? ChatColor.GREEN : ChatColor.GRAY); - name.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(friend.Favourite ? ("Add " + friend.Name + " to") : ("Remove " + friend.Name + " from") + " your favourite friends") + name.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder((friend.Favourite ? "Remove " + friend.Name + " from" : "Add " + friend.Name + " to") + " your favourite friends") .create())); name.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, favouriteCommand + friend.Name)); message.addExtra(name); message.addExtra(" - "); - if (friend.Online && friend.Visibility != FriendVisibility.INVISIBLE) + if (online) { if (canJoin) { @@ -379,9 +539,9 @@ public class FriendManager extends MiniDbClientPlugin } else if (friend.Visibility == FriendVisibility.PRESENCE) { - TextComponent server = new TextComponent("Hidden"); + TextComponent server = new TextComponent(friend.Visibility.getName()); server.setColor(ChatColor.DARK_GREEN); - server.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(friend.Name + " is in hidden mode") + server.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(friend.Name + " is in " + friend.Visibility.getName().toLowerCase() + " mode") .create())); message.addExtra(server); } @@ -460,7 +620,28 @@ public class FriendManager extends MiniDbClientPlugin } else { - messages.values().forEach(textComponent -> caller.spigot().sendMessage(textComponent)); + messages.values().forEach(message -> caller.spigot().sendMessage(message)); + + TextComponent message = new TextComponent(""); + + for (FriendVisibility visibility : FriendVisibility.values()) + { + TextComponent vis = new TextComponent(visibility.getName().toUpperCase()); + vis.setColor(visibility.getColour()); + vis.setBold(true); + vis.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Set your visibility to " + visibility.getName()) + .create())); + vis.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, visiblityCommand + visibility)); + + if (message.getExtra() != null) + { + message.addExtra(" - "); + } + + message.addExtra(vis); + } + + caller.spigot().sendMessage(message); } TextComponent toggle = new TextComponent(""); @@ -489,14 +670,12 @@ public class FriendManager extends MiniDbClientPlugin return false; } - boolean staff = serverName.startsWith("Staff"), cust = serverName.startsWith("CUST"); - - if (staff) + if (serverName.startsWith("Staff-")) { return isPlayerStaff; } - return !cust; + return !serverName.startsWith("CUST-"); } public void updatePlayerStatus(UUID playerUUID, PlayerStatus status) diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendVisibility.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendVisibility.java index 028b6de6c..4b462dfb2 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendVisibility.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/FriendVisibility.java @@ -1,10 +1,30 @@ package mineplex.core.friend; -public enum FriendVisibility +import net.md_5.bungee.api.ChatColor; + +public enum FriendVisibility { - SHOWN, - PRESENCE, - INVISIBLE + SHOWN("Visible", ChatColor.GREEN), + PRESENCE("Semi-Visible", ChatColor.YELLOW), + INVISIBLE("Invisible", ChatColor.RED); + private final String _name; + private final ChatColor _colour; + + FriendVisibility(String name, ChatColor colour) + { + _name = name; + _colour = colour; + } + + public String getName() + { + return _name; + } + + public ChatColor getColour() + { + return _colour; + } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/command/FriendFavouriteCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/command/FriendFavouriteCommand.java new file mode 100644 index 000000000..fddc58356 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/command/FriendFavouriteCommand.java @@ -0,0 +1,35 @@ +package mineplex.core.friend.command; + +import org.bukkit.entity.Player; + +import mineplex.core.command.CommandBase; +import mineplex.core.friend.FriendManager; +import mineplex.core.friend.FriendManager.Perm; + +public class FriendFavouriteCommand extends CommandBase +{ + + public static final String COMMAND = "friendfavourite"; + + public FriendFavouriteCommand(FriendManager plugin) + { + super(plugin, Perm.FRIEND_COMMAND, COMMAND); + } + + @Override + public void Execute(final Player caller, final String[] args) + { + if (args.length == 0) + { + return; + } + + _commandCenter.GetClientManager().checkPlayerName(caller, args[0], result -> + { + if (result != null) + { + Plugin.toggleFavourite(caller, result); + } + }); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/command/FriendVisibilityCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/command/FriendVisibilityCommand.java new file mode 100644 index 000000000..f47fe36cf --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/command/FriendVisibilityCommand.java @@ -0,0 +1,38 @@ +package mineplex.core.friend.command; + +import org.bukkit.entity.Player; + +import mineplex.core.command.CommandBase; +import mineplex.core.common.util.F; +import mineplex.core.friend.FriendManager; +import mineplex.core.friend.FriendManager.Perm; +import mineplex.core.friend.FriendVisibility; + +public class FriendVisibilityCommand extends CommandBase +{ + + public static final String COMMAND = "friendvisibility"; + + public FriendVisibilityCommand(FriendManager plugin) + { + super(plugin, Perm.FRIEND_COMMAND, COMMAND); + } + + @Override + public void Execute(final Player caller, final String[] args) + { + if (args.length == 0) + { + return; + } + + try + { + Plugin.setVisibility(caller, FriendVisibility.valueOf(args[0])); + } + catch (IllegalArgumentException ex) + { + caller.sendMessage(F.main(Plugin.getName(), "Invalid argument.")); + } + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendData.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendData.java index 983b8d73f..118919b91 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendData.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendData.java @@ -1,19 +1,22 @@ package mineplex.core.friend.data; import java.util.ArrayList; +import java.util.List; + import mineplex.core.friend.ui.FriendsGUI; public class FriendData { - private ArrayList _friends = new ArrayList(); + + private List _friends = new ArrayList<>(); private FriendsGUI _friendsPage; - public ArrayList getFriends() + public List getFriends() { return _friends; } - public void setFriends(ArrayList newFriends) + public void setFriends(List newFriends) { _friends = newFriends; updateGui(); diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendRepository.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendRepository.java index 17d3a975a..68eba8bb1 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendRepository.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendRepository.java @@ -18,6 +18,8 @@ import mineplex.serverdata.data.DataRepository; import mineplex.serverdata.data.PlayerStatus; import mineplex.serverdata.database.DBPool; import mineplex.serverdata.database.RepositoryBase; +import mineplex.serverdata.database.column.ColumnBoolean; +import mineplex.serverdata.database.column.ColumnInt; import mineplex.serverdata.database.column.ColumnVarChar; import mineplex.serverdata.redis.RedisDataRepository; @@ -30,6 +32,9 @@ public class FriendRepository extends RepositoryBase private static final String ADD_FRIEND_RECORD = "INSERT INTO accountFriend (uuidSource, uuidTarget, status, created) SELECT fA.uuid AS uuidSource, tA.uuid AS uuidTarget, ?, now() FROM accounts as fA LEFT JOIN accounts AS tA ON tA.name = ? WHERE fA.name = ?;"; private static final String UPDATE_MUTUAL_RECORD = "UPDATE accountFriend AS aF INNER JOIN accounts as fA ON aF.uuidSource = fA.uuid INNER JOIN accounts AS tA ON aF.uuidTarget = tA.uuid SET aF.status = ? WHERE tA.name = ? AND fA.name = ?;"; private static final String DELETE_FRIEND_RECORD = "DELETE aF FROM accountFriend AS aF INNER JOIN accounts as fA ON aF.uuidSource = fA.uuid INNER JOIN accounts AS tA ON aF.uuidTarget = tA.uuid WHERE fA.name = ? AND tA.name = ?;"; + private static final String INSERT_VISIBILITY = "INSERT INTO accountFriendData VALUES (?,?);"; + private static final String UPDATE_VISIBILITY = "UPDATE accountFriendData SET status=? WHERE accountId=?;"; + private static final String UPDATE_FAVOURITE = "UPDATE accountFriend AS aF INNER JOIN accounts as fA ON aF.uuidSource = fA.uuid INNER JOIN accounts AS tA ON aF.uuidTarget = tA.uuid SET aF.favourite = ? WHERE tA.name = ? AND fA.name = ?;"; // Repository holding active PlayerStatus data. private final DataRepository _repository; @@ -170,7 +175,7 @@ public class FriendRepository extends RepositoryBase { PlayerStatus status = playerStatuses.get(friend.UUID); friend.Online = (status != null); - friend.ServerName = (friend.Online) ? status.getServer() : null; + friend.ServerName = friend.Online ? status.getServer() : null; } } @@ -186,8 +191,21 @@ public class FriendRepository extends RepositoryBase } } - public PlayerStatus getStatus(UUID playerUUID) + public boolean updateVisibility(int accountId, FriendVisibility visibility) { - return _repository.getElement(playerUUID.toString()); + ColumnInt accountIdColumn = new ColumnInt("accountId", accountId), visibilityColumn = new ColumnInt("status", visibility.ordinal()); + + if (executeUpdate(UPDATE_VISIBILITY, visibilityColumn, accountIdColumn) > 0) + { + return true; + } + + return executeInsert(INSERT_VISIBILITY, null, accountIdColumn, visibilityColumn) > 0; } + + public boolean updateFavourite(String caller, String target, boolean favourite) + { + return executeUpdate(UPDATE_FAVOURITE, new ColumnBoolean("favourite", favourite), new ColumnVarChar("name", 100, target), new ColumnVarChar("name", 100, caller)) > 0; + } + } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendStatus.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendStatus.java index 2d5322cd7..b78fe71f7 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendStatus.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/data/FriendStatus.java @@ -18,4 +18,9 @@ public class FriendStatus public FriendStatusType Status; public FriendVisibility Visibility; public boolean Favourite; + + public boolean isOnline() + { + return Online && Visibility != FriendVisibility.INVISIBLE; + } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/friend/redis/FriendAddMessage.java b/Plugins/Mineplex.Core/src/mineplex/core/friend/redis/FriendAddMessage.java new file mode 100644 index 000000000..75857a858 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/friend/redis/FriendAddMessage.java @@ -0,0 +1,25 @@ +package mineplex.core.friend.redis; + +import mineplex.serverdata.commands.ServerCommand; + +public class FriendAddMessage extends ServerCommand +{ + + private final String _accepter, _target; + + public FriendAddMessage(String accepter, String target) + { + _accepter = accepter; + _target = target; + } + + public String getAccepter() + { + return _accepter; + } + + public String getTarget() + { + return _target; + } +}