001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.command; 020 021import com.google.inject.Inject; 022import com.plotsquared.core.configuration.Settings; 023import com.plotsquared.core.configuration.caption.TranslatableCaption; 024import com.plotsquared.core.database.DBFunc; 025import com.plotsquared.core.events.PlotRateEvent; 026import com.plotsquared.core.events.TeleportCause; 027import com.plotsquared.core.permissions.Permission; 028import com.plotsquared.core.player.PlotPlayer; 029import com.plotsquared.core.plot.Plot; 030import com.plotsquared.core.plot.PlotInventory; 031import com.plotsquared.core.plot.PlotItemStack; 032import com.plotsquared.core.plot.Rating; 033import com.plotsquared.core.plot.flag.implementations.DoneFlag; 034import com.plotsquared.core.util.EventDispatcher; 035import com.plotsquared.core.util.InventoryUtil; 036import com.plotsquared.core.util.MathMan; 037import com.plotsquared.core.util.TabCompletions; 038import com.plotsquared.core.util.query.PlotQuery; 039import com.plotsquared.core.util.task.TaskManager; 040import net.kyori.adventure.text.Component; 041import net.kyori.adventure.text.minimessage.tag.Tag; 042import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 043import org.checkerframework.checker.nullness.qual.NonNull; 044 045import java.util.Collection; 046import java.util.Collections; 047import java.util.HashMap; 048import java.util.LinkedList; 049import java.util.List; 050import java.util.Map.Entry; 051import java.util.UUID; 052import java.util.stream.Collectors; 053 054@CommandDeclaration(command = "rate", 055 permission = "plots.rate", 056 usage = "/plot rate [# | next | purge]", 057 aliases = "rt", 058 category = CommandCategory.INFO, 059 requiredType = RequiredType.PLAYER) 060public class Rate extends SubCommand { 061 062 private final EventDispatcher eventDispatcher; 063 private final InventoryUtil inventoryUtil; 064 065 @Inject 066 public Rate( 067 final @NonNull EventDispatcher eventDispatcher, 068 final @NonNull InventoryUtil inventoryUtil 069 ) { 070 this.eventDispatcher = eventDispatcher; 071 this.inventoryUtil = inventoryUtil; 072 } 073 074 @Override 075 public boolean onCommand(final PlotPlayer<?> player, String[] args) { 076 if (args.length == 1) { 077 switch (args[0].toLowerCase()) { 078 case "next" -> { 079 final List<Plot> plots = PlotQuery.newQuery().whereBasePlot().asList(); 080 plots.sort((p1, p2) -> { 081 double v1 = 0; 082 if (!p1.getRatings().isEmpty()) { 083 for (Entry<UUID, Rating> entry : p1.getRatings().entrySet()) { 084 v1 -= 11 - entry.getValue().getAverageRating(); 085 } 086 } 087 double v2 = 0; 088 if (!p2.getRatings().isEmpty()) { 089 for (Entry<UUID, Rating> entry : p2.getRatings().entrySet()) { 090 v2 -= 11 - entry.getValue().getAverageRating(); 091 } 092 } 093 if (v1 == v2) { 094 return -0; 095 } 096 return v2 > v1 ? 1 : -1; 097 }); 098 UUID uuid = player.getUUID(); 099 for (Plot p : plots) { 100 if ((!Settings.Done.REQUIRED_FOR_RATINGS || DoneFlag.isDone(p)) && p 101 .isBasePlot() && !p.getRatings().containsKey(uuid) && !p 102 .isAdded(uuid)) { 103 p.teleportPlayer(player, TeleportCause.COMMAND_RATE, result -> { 104 }); 105 player.sendMessage(TranslatableCaption.of("tutorial.rate_this")); 106 return true; 107 } 108 } 109 player.sendMessage(TranslatableCaption.of("invalid.found_no_plots")); 110 return false; 111 } 112 case "purge" -> { 113 final Plot plot = player.getCurrentPlot(); 114 if (plot == null) { 115 player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); 116 return false; 117 } 118 if (!player.hasPermission(Permission.PERMISSION_ADMIN_COMMAND_PURGE_RATINGS, true)) { 119 return false; 120 } 121 plot.clearRatings(); 122 player.sendMessage(TranslatableCaption.of("ratings.ratings_purged")); 123 return true; 124 } 125 } 126 } 127 final Plot plot = player.getCurrentPlot(); 128 if (plot == null) { 129 player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); 130 return false; 131 } 132 if (!plot.hasOwner()) { 133 player.sendMessage(TranslatableCaption.of("ratings.rating_not_owned")); 134 return false; 135 } 136 if (plot.isOwner(player.getUUID())) { 137 player.sendMessage(TranslatableCaption.of("ratings.rating_not_your_own")); 138 return false; 139 } 140 if (Settings.Done.REQUIRED_FOR_RATINGS && !DoneFlag.isDone(plot)) { 141 player.sendMessage(TranslatableCaption.of("ratings.rating_not_done")); 142 return false; 143 } 144 if (Settings.Ratings.CATEGORIES != null && !Settings.Ratings.CATEGORIES.isEmpty()) { 145 final Runnable run = new Runnable() { 146 @Override 147 public void run() { 148 if (plot.getRatings().containsKey(player.getUUID())) { 149 player.sendMessage( 150 TranslatableCaption.of("ratings.rating_already_exists"), 151 TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) 152 ); 153 return; 154 } 155 final MutableInt index = new MutableInt(0); 156 final MutableInt rating = new MutableInt(0); 157 String title = Settings.Ratings.CATEGORIES.get(0); 158 PlotInventory inventory = new PlotInventory(inventoryUtil, player, 1, title) { 159 @Override 160 public boolean onClick(int i) { 161 rating.add((i + 1) * Math.pow(10, index.getValue())); 162 index.increment(); 163 if (index.getValue() >= Settings.Ratings.CATEGORIES.size()) { 164 int rV = rating.getValue(); 165 PlotRateEvent event = Rate.this.eventDispatcher 166 .callRating(this.getPlayer(), plot, new Rating(rV)); 167 if (event.getRating() != null) { 168 plot.addRating(this.getPlayer().getUUID(), event.getRating()); 169 getPlayer().sendMessage( 170 TranslatableCaption.of("ratings.rating_applied"), 171 TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) 172 ); 173 } 174 return false; 175 } 176 setTitle(Settings.Ratings.CATEGORIES.get(index.getValue())); 177 return true; 178 } 179 }; 180 inventory.setItem(0, new PlotItemStack(Settings.Ratings.BLOCK_0, 1, 181 TranslatableCaption.of("ratings.0-8").getComponent(player) 182 )); 183 inventory.setItem(1, new PlotItemStack(Settings.Ratings.BLOCK_1, 1, 184 TranslatableCaption.of("ratings.1-8").getComponent(player) 185 )); 186 inventory.setItem(2, new PlotItemStack(Settings.Ratings.BLOCK_2, 2, 187 TranslatableCaption.of("ratings.2-8").getComponent(player) 188 )); 189 inventory.setItem(3, new PlotItemStack(Settings.Ratings.BLOCK_3, 3, 190 TranslatableCaption.of("ratings.3-8").getComponent(player) 191 )); 192 inventory.setItem(4, new PlotItemStack(Settings.Ratings.BLOCK_4, 4, 193 TranslatableCaption.of("ratings.4-8").getComponent(player) 194 )); 195 inventory.setItem(5, new PlotItemStack(Settings.Ratings.BLOCK_5, 5, 196 TranslatableCaption.of("ratings.5-8").getComponent(player) 197 )); 198 inventory.setItem(6, new PlotItemStack(Settings.Ratings.BLOCK_6, 6, 199 TranslatableCaption.of("ratings.6-8").getComponent(player) 200 )); 201 inventory.setItem(7, new PlotItemStack(Settings.Ratings.BLOCK_7, 7, 202 TranslatableCaption.of("ratings.7-8").getComponent(player) 203 )); 204 inventory.setItem(8, new PlotItemStack(Settings.Ratings.BLOCK_8, 8, 205 TranslatableCaption.of("ratings.8-8").getComponent(player) 206 )); 207 inventory.openInventory(); 208 } 209 }; 210 if (plot.getSettings().getRatings() == null) { 211 if (!Settings.Enabled_Components.RATING_CACHE) { 212 TaskManager.runTaskAsync(() -> { 213 plot.getSettings().setRatings(DBFunc.getRatings(plot)); 214 run.run(); 215 }); 216 return true; 217 } 218 plot.getSettings().setRatings(new HashMap<>()); 219 } 220 run.run(); 221 return true; 222 } 223 if (args.length < 1) { 224 player.sendMessage(TranslatableCaption.of("ratings.rating_not_valid")); 225 return true; 226 } 227 String arg = args[0]; 228 final int rating; 229 if (MathMan.isInteger(arg) && arg.length() < 3 && !arg.isEmpty()) { 230 rating = Integer.parseInt(arg); 231 if (rating > 10 || rating < 1) { 232 player.sendMessage(TranslatableCaption.of("ratings.rating_not_valid")); 233 return false; 234 } 235 } else { 236 player.sendMessage(TranslatableCaption.of("ratings.rating_not_valid")); 237 return false; 238 } 239 final UUID uuid = player.getUUID(); 240 final Runnable run = () -> { 241 if (plot.getRatings().containsKey(uuid)) { 242 player.sendMessage( 243 TranslatableCaption.of("ratings.rating_already_exists"), 244 TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) 245 ); 246 return; 247 } 248 PlotRateEvent event = 249 this.eventDispatcher.callRating(player, plot, new Rating(rating)); 250 if (event.getRating() != null) { 251 plot.addRating(uuid, event.getRating()); 252 player.sendMessage( 253 TranslatableCaption.of("ratings.rating_applied"), 254 TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) 255 ); 256 } 257 }; 258 if (plot.getSettings().getRatings() == null) { 259 if (!Settings.Enabled_Components.RATING_CACHE) { 260 TaskManager.runTaskAsync(() -> { 261 plot.getSettings().setRatings(DBFunc.getRatings(plot)); 262 run.run(); 263 }); 264 return true; 265 } 266 plot.getSettings().setRatings(new HashMap<>()); 267 } 268 run.run(); 269 return true; 270 } 271 272 @Override 273 public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) { 274 if (args.length == 1) { 275 final List<String> completions = new LinkedList<>(); 276 if (player.hasPermission(Permission.PERMISSION_RATE)) { 277 completions.add("1 - 10"); 278 } 279 if (player.hasPermission(Permission.PERMISSION_ADMIN_COMMAND_PURGE_RATINGS)) { 280 completions.add("purge"); 281 } 282 final List<Command> commands = completions.stream().filter(completion -> completion 283 .toLowerCase() 284 .startsWith(args[0].toLowerCase())) 285 .map(completion -> new Command(null, true, completion, "", RequiredType.PLAYER, CommandCategory.INFO) { 286 }).collect(Collectors.toCollection(LinkedList::new)); 287 if (player.hasPermission(Permission.PERMISSION_RATE) && args[0].length() > 0) { 288 commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList())); 289 } 290 return commands; 291 } 292 return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList()); 293 } 294 295 private static class MutableInt { 296 297 private int value; 298 299 MutableInt(int i) { 300 this.value = i; 301 } 302 303 void increment() { 304 this.value++; 305 } 306 307 void decrement() { 308 this.value--; 309 } 310 311 int getValue() { 312 return this.value; 313 } 314 315 void add(Number v) { 316 this.value += v.intValue(); 317 } 318 319 } 320 321}