diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Dislike.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Dislike.java new file mode 100644 index 000000000..ca807a263 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Dislike.java @@ -0,0 +1,14 @@ +package com.github.intellectualsites.plotsquared.plot.commands; + +import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; +import com.github.intellectualsites.plotsquared.plot.object.PlotPlayer; + +@CommandDeclaration(command = "dislike", permission = "plots.dislike", description = "Dislike the plot", + usage = "/plot dislike [next|purge]", category = CommandCategory.INFO, requiredType = RequiredType.PLAYER) +public class Dislike extends SubCommand { + + @Override public boolean onCommand(PlotPlayer player, String[] args) { + return Like.handleLike(player, args, false); + } + +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Info.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Info.java index 1bbc3bd93..4fb4d6859 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Info.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Info.java @@ -2,6 +2,7 @@ package com.github.intellectualsites.plotsquared.plot.commands; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; import com.github.intellectualsites.plotsquared.plot.config.Captions; +import com.github.intellectualsites.plotsquared.plot.config.Settings; import com.github.intellectualsites.plotsquared.plot.database.DBFunc; import com.github.intellectualsites.plotsquared.plot.flag.Flags; import com.github.intellectualsites.plotsquared.plot.object.*; @@ -32,6 +33,7 @@ import java.util.UUID; case "seen": case "owner": case "rating": + case "likes": plot = MainUtil.getPlotFromString(player, null, false); break; default: @@ -130,7 +132,7 @@ import java.util.UUID; if (info == null) { MainUtil.sendMessage(player, "&6Categories&7: &amembers&7, &aalias&7, &abiome&7, &aseen&7, &adenied&7, &aflags&7, &aid&7, &asize&7, &atrusted&7, " - + "&aowner&7, &arating"); + + "&aowner&7, " + (Settings.Ratings.USE_LIKES ? " &alikes" : " &arating")); return false; } full = true; @@ -169,6 +171,8 @@ import java.util.UUID; return Captions.PLOT_INFO_OWNER.s(); case "rating": return Captions.PLOT_INFO_RATING.s(); + case "likes": + return Captions.PLOT_INFO_LIKES.s(); case "seen": return Captions.PLOT_INFO_SEEN.s(); default: diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Like.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Like.java new file mode 100644 index 000000000..d7c6223c3 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Like.java @@ -0,0 +1,143 @@ +package com.github.intellectualsites.plotsquared.plot.commands; + +import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; +import com.github.intellectualsites.plotsquared.plot.PlotSquared; +import com.github.intellectualsites.plotsquared.plot.config.Captions; +import com.github.intellectualsites.plotsquared.plot.config.Settings; +import com.github.intellectualsites.plotsquared.plot.database.DBFunc; +import com.github.intellectualsites.plotsquared.plot.flag.Flags; +import com.github.intellectualsites.plotsquared.plot.object.Plot; +import com.github.intellectualsites.plotsquared.plot.object.PlotPlayer; +import com.github.intellectualsites.plotsquared.plot.object.Rating; +import com.github.intellectualsites.plotsquared.plot.util.EventUtil; +import com.github.intellectualsites.plotsquared.plot.util.MainUtil; +import com.github.intellectualsites.plotsquared.plot.util.Permissions; +import com.github.intellectualsites.plotsquared.plot.util.TaskManager; + +import java.util.*; + +@CommandDeclaration(command = "like", permission = "plots.like", description = "Like the plot", + usage = "/plot like [next|purge]", category = CommandCategory.INFO, requiredType = RequiredType.PLAYER) +public class Like extends SubCommand { + + @Override public boolean onCommand(PlotPlayer player, String[] args) { + return handleLike(player, args, true); + } + + protected static boolean handleLike(final PlotPlayer player, String[] args, final boolean like) { + final UUID uuid = player.getUUID(); + if (args.length == 1) { + switch (args[0].toLowerCase()) { + case "next": { + final List plots = new ArrayList<>(PlotSquared.get().getBasePlots()); + plots.sort((p1, p2) -> { + double v1 = getLikesPercentage(p1); + double v2 = getLikesPercentage(p2); + if (v1 == v2) { + return -0; + } + return v2 > v1 ? 1 : -1; + }); + for (final Plot plot : plots) { + if ((!Settings.Done.REQUIRED_FOR_RATINGS || plot.hasFlag(Flags.DONE)) + && plot.isBasePlot() && (!plot.getLikes().containsKey(uuid))) { + plot.teleportPlayer(player); + MainUtil.sendMessage(player, Captions.RATE_THIS); + return true; + } + } + MainUtil.sendMessage(player, Captions.FOUND_NO_PLOTS); + return true; + } + case "purge": { + final Plot plot = player.getCurrentPlot(); + if (plot == null) { + return !sendMessage(player, Captions.NOT_IN_PLOT); + } + if (!Permissions + .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_RATE, true)) { + return false; + } + plot.clearRatings(); + Captions.RATINGS_PURGED.send(player); + return true; + } + } + } + final Plot plot = player.getCurrentPlot(); + if (plot == null) { + return !sendMessage(player, Captions.NOT_IN_PLOT); + } + if (!plot.hasOwner()) { + sendMessage(player, Captions.RATING_NOT_OWNED); + return false; + } + if (plot.isOwner(player.getUUID())) { + sendMessage(player, Captions.RATING_NOT_YOUR_OWN); + return false; + } + if (Settings.Done.REQUIRED_FOR_RATINGS && !plot.hasFlag(Flags.DONE)) { + sendMessage(player, Captions.RATING_NOT_DONE); + return false; + } + final Runnable run = () -> { + final Boolean oldRating = plot.getLikes().get(uuid); + if (oldRating != null) { + sendMessage(player, Captions.RATING_ALREADY_EXISTS, plot.getId().toString()); + return; + } + final int rating; + if (like) { + rating = 10; + } else { + rating = 1; + } + plot.addRating(uuid, new Rating(rating)); + final Rating result = EventUtil.manager.callRating(player, plot, new Rating(rating)); + if (result != null) { + plot.addRating(uuid, result); + sendMessage(player, like ? Captions.RATING_LIKED : Captions.RATING_DISLIKED, + plot.getId().toString()); + } + }; + if (plot.getSettings().ratings == null) { + if (!Settings.Enabled_Components.RATING_CACHE) { + TaskManager.runTaskAsync(() -> { + plot.getSettings().ratings = DBFunc.getRatings(plot); + run.run(); + }); + return true; + } + plot.getSettings().ratings = new HashMap<>(); + } + run.run(); + return true; + } + + /** + * Get the likes to dislike ratio of a plot as a percentage (in decimal form) + * + * @return likes to dislike ratio, returns zero if the plot has no likes + */ + public static double getLikesPercentage(final Plot plot) { + if (!plot.hasRatings()) { + return 0; + } + final Collection reactions = plot.getLikes().values(); + double numLikes = 0, numDislikes = 0; + for (final boolean reaction : reactions) { + if (reaction) { + numLikes += 1; + } else { + numDislikes += 1; + } + } + if (numLikes == 0 && numDislikes == 0) { + return 0D; + } else if (numDislikes == 0) { + return 1.0D; + } + return numLikes / (numLikes + numDislikes); + } + +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/MainCommand.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/MainCommand.java index ccb4558fb..90007b66e 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/MainCommand.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/MainCommand.java @@ -4,6 +4,7 @@ import com.github.intellectualsites.plotsquared.commands.Command; import com.github.intellectualsites.plotsquared.commands.CommandCaller; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; import com.github.intellectualsites.plotsquared.plot.config.Captions; +import com.github.intellectualsites.plotsquared.plot.config.Settings; import com.github.intellectualsites.plotsquared.plot.object.*; import com.github.intellectualsites.plotsquared.plot.util.CmdConfirm; import com.github.intellectualsites.plotsquared.plot.util.EconHandler; @@ -68,7 +69,6 @@ public class MainCommand extends Command { new DebugPaste(); new Unlink(); new Kick(); - new Rate(); new DebugClaimTest(); new Inbox(); new Comment(); @@ -98,6 +98,14 @@ public class MainCommand extends Command { new SetHome(); new Cluster(); new DebugImportWorlds(); + + if (Settings.Ratings.USE_LIKES) { + new Like(); + new Dislike(); + } else { + new Rate(); + } + // Referenced commands instance.toggle = new Toggle(); instance.help = new Help(instance); diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SubCommand.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SubCommand.java index 82bb3a17d..7f36f6a4a 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SubCommand.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SubCommand.java @@ -32,7 +32,7 @@ public abstract class SubCommand extends Command { public abstract boolean onCommand(PlotPlayer player, String[] args); - public boolean sendMessage(PlotPlayer player, Captions message, Object... args) { + public static boolean sendMessage(PlotPlayer player, Captions message, Object... args) { message.send(player, args); return true; } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java index 092d5c6bb..c163a10a4 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java @@ -378,6 +378,9 @@ public enum Captions { RATING_ALREADY_EXISTS("$2You have already rated plot $2%s", "Ratings"), RATING_APPLIED( "$4You successfully rated plot $2%s", "Ratings"), + RATING_DISLIKED("$4You successfully disliked plot $2%s", "Ratings"), + RATING_LIKED("$4You successfully liked plot $2%s", "Ratings"), + RATING_NOT_YOUR_OWN("$2You cannot rate your own plot", "Ratings"), RATING_NOT_DONE( "$2You can only rate finished plots.", "Ratings"), @@ -693,6 +696,8 @@ public enum Captions { PLOT_INFO_BIOME("$1Biome:$2 %biome%", "Info"), PLOT_INFO_RATING("$1Rating:$2 %rating%", "Info"), + PLOT_INFO_LIKES("$1Like Ratio:$2 %likes%%", "Info"), + PLOT_INFO_OWNER("$1Owner:$2 %owner%", "Info"), PLOT_INFO_ID("$1ID:$2 %id%", "Info"), PLOT_INFO_ALIAS("$1Alias:$2 %alias%", "Info"), PLOT_INFO_SIZE("$1Size:$2 %size%", "Info"), diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Settings.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Settings.java index 790306c2c..f2a669823 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Settings.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Settings.java @@ -315,6 +315,9 @@ public class Settings extends Config { public static final class Ratings { + @Comment("Replace the rating system with a like system. Will add /plot like/dislike," + + " and remove the rating command") public static boolean USE_LIKES = false; + @Comment("Rating categories") public static List CATEGORIES = new ArrayList<>(); } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java index 5f8a3df0e..63db95632 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java @@ -1344,7 +1344,7 @@ public class Plot { } /** - * Clear the ratings for this plot + * Clear the ratings/likes for this plot */ public void clearRatings() { Plot base = this.getBasePlot(false); @@ -1355,6 +1355,15 @@ public class Plot { } } + public Map getLikes() { + final Map map = new HashMap<>(); + final Map ratings = this.getRatings(); + ratings.forEach((uuid, rating) -> { + map.put(uuid, rating.getLike()); + }); + return map; + } + /** * Gets the ratings associated with a plot
* - The rating object may contain multiple categories diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Rating.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Rating.java index 8d327188a..00fe5bcc2 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Rating.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Rating.java @@ -9,6 +9,9 @@ import java.util.Map.Entry; import java.util.stream.IntStream; public class Rating { + + private static final String LIKE_INTERNAL = "__LIKES__"; + /** * This is a map of the rating category to the rating value */ @@ -17,22 +20,27 @@ public class Rating { private boolean changed; public Rating(int value) { - this.initial = value; this.ratingMap = new HashMap<>(); - if (Settings.Ratings.CATEGORIES != null && Settings.Ratings.CATEGORIES.size() > 1) { - if (value < 10) { - for (String ratingCategory : Settings.Ratings.CATEGORIES) { - this.ratingMap.put(ratingCategory, value); - } - this.changed = true; - return; - } - for (String ratingCategory : Settings.Ratings.CATEGORIES) { - this.ratingMap.put(ratingCategory, value % 10 - 1); - value = value / 10; - } + if (Settings.Ratings.USE_LIKES) { + this.initial = value == 10 ? 10 : 1; + this.ratingMap.put(LIKE_INTERNAL, this.initial == 10 ? 10 : 1); } else { - this.ratingMap.put(null, value); + this.initial = value; + if (Settings.Ratings.CATEGORIES != null && Settings.Ratings.CATEGORIES.size() > 1) { + if (value < 10) { + for (String ratingCategory : Settings.Ratings.CATEGORIES) { + this.ratingMap.put(ratingCategory, value); + } + this.changed = true; + return; + } + for (String ratingCategory : Settings.Ratings.CATEGORIES) { + this.ratingMap.put(ratingCategory, value % 10 - 1); + value = value / 10; + } + } else { + this.ratingMap.put(null, value); + } } } @@ -44,10 +52,18 @@ public class Rating { } public double getAverageRating() { + if (Settings.Ratings.USE_LIKES) { + return getLike() ? 10 : 1; + } double total = this.ratingMap.entrySet().stream().mapToDouble(Entry::getValue).sum(); return total / this.ratingMap.size(); } + public boolean getLike() { + final Integer rating = this.getRating(LIKE_INTERNAL); + return rating != null && rating == 10; + } + public Integer getRating(String category) { return this.ratingMap.get(category); } @@ -64,6 +80,9 @@ public class Rating { if (!this.changed) { return this.initial; } + if (Settings.Ratings.USE_LIKES) { + return this.ratingMap.get(LIKE_INTERNAL); + } if (Settings.Ratings.CATEGORIES != null && Settings.Ratings.CATEGORIES.size() > 1) { return IntStream.range(0, Settings.Ratings.CATEGORIES.size()).map( i -> (int) ((i + 1) * Math diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MainUtil.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MainUtil.java index 17366dbbb..1ae9839b8 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MainUtil.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/MainUtil.java @@ -2,6 +2,7 @@ package com.github.intellectualsites.plotsquared.plot.util; import com.github.intellectualsites.plotsquared.commands.CommandCaller; import com.github.intellectualsites.plotsquared.plot.PlotSquared; +import com.github.intellectualsites.plotsquared.plot.commands.Like; import com.github.intellectualsites.plotsquared.plot.config.Captions; import com.github.intellectualsites.plotsquared.plot.config.Settings; import com.github.intellectualsites.plotsquared.plot.database.DBFunc; @@ -776,25 +777,29 @@ public class MainUtil { if (info.contains("%rating%")) { final String newInfo = info; TaskManager.runTaskAsync(() -> { - int max = 10; - if (Settings.Ratings.CATEGORIES != null && !Settings.Ratings.CATEGORIES.isEmpty()) { - max = 8; - } String info1; - if (full && Settings.Ratings.CATEGORIES != null - && Settings.Ratings.CATEGORIES.size() > 1) { - double[] ratings = MainUtil.getAverageRatings(plot); - String rating = ""; - String prefix = ""; - for (int i = 0; i < ratings.length; i++) { - rating += prefix + Settings.Ratings.CATEGORIES.get(i) + '=' + String - .format("%.1f", ratings[i]); - prefix = ","; - } - info1 = newInfo.replaceAll("%rating%", rating); + if (Settings.Ratings.USE_LIKES) { + info1 = newInfo.replaceAll("%rating%", String.format("%.0f%%", Like.getLikesPercentage(plot) * 100D)); } else { - info1 = newInfo.replaceAll("%rating%", - String.format("%.1f", plot.getAverageRating()) + '/' + max); + int max = 10; + if (Settings.Ratings.CATEGORIES != null && !Settings.Ratings.CATEGORIES.isEmpty()) { + max = 8; + } + if (full && Settings.Ratings.CATEGORIES != null + && Settings.Ratings.CATEGORIES.size() > 1) { + double[] ratings = MainUtil.getAverageRatings(plot); + String rating = ""; + String prefix = ""; + for (int i = 0; i < ratings.length; i++) { + rating += prefix + Settings.Ratings.CATEGORIES.get(i) + '=' + String + .format("%.1f", ratings[i]); + prefix = ","; + } + info1 = newInfo.replaceAll("%rating%", rating); + } else { + info1 = newInfo.replaceAll("%rating%", + String.format("%.1f", plot.getAverageRating()) + '/' + max); + } } whenDone.run(info1); }); diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/StringMan.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/StringMan.java index 739c31bec..c7f30e1fa 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/StringMan.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/StringMan.java @@ -245,10 +245,12 @@ public class StringMan { } public static boolean isEqual(String a, String b) { - return (a == b) || ((a != null) && (b != null) && (a.length() == b.length()) && ( - a.hashCode() == b.hashCode()) && a.equals(b)); - // return a.equals(b) || b != null && a.length() == b.length() && a.hashCode() == b.hashCode() - // && a.equals(b); + if ((a == null && b != null) || (a != null && b == null)) { + return false; + } else if (a == null /* implies that b is null */) { + return false; + } + return a.equals(b); } public static boolean isEqualIgnoreCase(String a, String b) {