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.events.TeleportCause; 025import com.plotsquared.core.permissions.Permission; 026import com.plotsquared.core.player.PlotPlayer; 027import com.plotsquared.core.plot.Plot; 028import com.plotsquared.core.plot.PlotArea; 029import com.plotsquared.core.plot.PlotId; 030import com.plotsquared.core.plot.world.PlotAreaManager; 031import com.plotsquared.core.util.MathMan; 032import com.plotsquared.core.util.TabCompletions; 033import com.plotsquared.core.util.query.PlotQuery; 034import com.plotsquared.core.util.query.SortingStrategy; 035import com.plotsquared.core.util.task.RunnableVal2; 036import com.plotsquared.core.util.task.RunnableVal3; 037import net.kyori.adventure.text.Component; 038import net.kyori.adventure.text.minimessage.tag.Tag; 039import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 040import org.checkerframework.checker.nullness.qual.NonNull; 041 042import java.util.ArrayList; 043import java.util.Collection; 044import java.util.List; 045import java.util.concurrent.CompletableFuture; 046 047@CommandDeclaration(command = "home", 048 permission = "plots.home", 049 usage = "/plot home [<page> | <alias> | <area;x;y> | <area> <x;y> | <area> <page>]", 050 aliases = {"h"}, 051 requiredType = RequiredType.PLAYER, 052 category = CommandCategory.TELEPORT) 053public class HomeCommand extends Command { 054 055 private final PlotAreaManager plotAreaManager; 056 057 @Inject 058 public HomeCommand(final @NonNull PlotAreaManager plotAreaManager) { 059 super(MainCommand.getInstance(), true); 060 this.plotAreaManager = plotAreaManager; 061 } 062 063 private void home( 064 final @NonNull PlotPlayer<?> player, 065 final @NonNull PlotQuery query, final int page, 066 final RunnableVal3<Command, Runnable, Runnable> confirm, 067 final RunnableVal2<Command, CommandResult> whenDone 068 ) { 069 List<Plot> plots = query.asList(); 070 if (plots.isEmpty()) { 071 player.sendMessage(TranslatableCaption.of("invalid.found_no_plots")); 072 return; 073 } else if (plots.size() < page || page < 1) { 074 player.sendMessage( 075 TranslatableCaption.of("invalid.number_not_in_range"), 076 TagResolver.builder() 077 .tag("min", Tag.inserting(Component.text(1))) 078 .tag("max", Tag.inserting(Component.text(plots.size()))) 079 .build() 080 ); 081 return; 082 } 083 Plot plot = plots.get(page - 1); 084 confirm.run(this, () -> plot.teleportPlayer(player, TeleportCause.COMMAND_HOME, result -> { 085 if (result) { 086 whenDone.run(this, CommandResult.SUCCESS); 087 } else { 088 whenDone.run(HomeCommand.this, CommandResult.FAILURE); 089 } 090 }), () -> whenDone.run(HomeCommand.this, CommandResult.FAILURE)); 091 } 092 093 @NonNull 094 private PlotQuery query(final @NonNull PlotPlayer<?> player) { 095 // everything plots need to have in common here 096 return PlotQuery.newQuery().thatPasses(plot -> plot.isOwner(player.getUUID())); 097 } 098 099 @Override 100 public CompletableFuture<Boolean> execute( 101 PlotPlayer<?> player, String[] args, 102 RunnableVal3<Command, Runnable, Runnable> confirm, 103 RunnableVal2<Command, CommandResult> whenDone 104 ) throws CommandException { 105 // /plot home <number> (or page, whatever it's called) 106 // /plot home <alias> 107 // /plot home <[area;]x;y> 108 // /plot home <area> <x;y> 109 // /plot home <area> <page> 110 if (!player.hasPermission(Permission.PERMISSION_VISIT_OWNED) && !player.hasPermission(Permission.PERMISSION_HOME)) { 111 player.sendMessage( 112 TranslatableCaption.of("permission.no_permission"), 113 TagResolver.resolver("node", Tag.inserting(Component.text(Permission.PERMISSION_VISIT_OWNED.toString()))) 114 ); 115 return CompletableFuture.completedFuture(false); 116 } 117 if (args.length > 2) { 118 sendUsage(player); 119 return CompletableFuture.completedFuture(false); 120 } 121 PlotQuery query = query(player); 122 int page = 1; // page = index + 1 123 String identifier; 124 PlotArea plotArea; 125 boolean basePlotOnly = true; 126 switch (args.length) { 127 case 1 -> { 128 identifier = args[0]; 129 if (MathMan.isInteger(identifier)) { 130 try { 131 page = Integer.parseInt(identifier); 132 } catch (NumberFormatException ignored) { 133 player.sendMessage( 134 TranslatableCaption.of("invalid.not_a_number"), 135 TagResolver.resolver("value", Tag.inserting(Component.text(identifier))) 136 ); 137 return CompletableFuture.completedFuture(false); 138 } 139 sortBySettings(query, player); 140 break; 141 } 142 // either plot id or alias 143 Plot fromId = Plot.getPlotFromString(player, identifier, false); 144 if (fromId != null && fromId.isOwner(player.getUUID())) { 145 // it was a valid plot id 146 basePlotOnly = false; 147 query.withPlot(fromId); 148 break; 149 } 150 // allow for plot home within a plot area 151 plotArea = this.plotAreaManager.getPlotAreaByString(args[0]); 152 if (plotArea != null) { 153 query.inArea(plotArea); 154 break; 155 } 156 // it wasn't a valid plot id, trying to find plot by alias 157 query.withAlias(identifier); 158 } 159 case 2 -> { 160 // we assume args[0] is a plot area and args[1] an identifier 161 plotArea = this.plotAreaManager.getPlotAreaByString(args[0]); 162 identifier = args[1]; 163 if (plotArea == null) { 164 // invalid command, therefore no plots 165 query.noPlots(); 166 break; 167 } 168 query.inArea(plotArea); 169 if (MathMan.isInteger(identifier)) { 170 // identifier is a page number 171 try { 172 page = Integer.parseInt(identifier); 173 } catch (NumberFormatException ignored) { 174 player.sendMessage( 175 TranslatableCaption.of("invalid.not_a_number"), 176 TagResolver.resolver("value", Tag.inserting(Component.text(identifier))) 177 ); 178 return CompletableFuture.completedFuture(false); 179 } 180 query.withSortingStrategy(SortingStrategy.SORT_BY_CREATION); 181 break; 182 } 183 // identifier needs to be a plot id then 184 PlotId id = PlotId.fromStringOrNull(identifier); 185 if (id == null) { 186 // invalid command, therefore no plots 187 query.noPlots(); 188 break; 189 } 190 // we can try to get this plot 191 Plot plot = plotArea.getPlot(id); 192 if (plot == null) { 193 query.noPlots(); 194 break; 195 } 196 // as the query already filters by owner, this is fine 197 basePlotOnly = false; 198 query.withPlot(plot); 199 } 200 case 0 -> sortBySettings(query, player); 201 } 202 if (basePlotOnly) { 203 query.whereBasePlot(); 204 } 205 home(player, query, page, confirm, whenDone); 206 return CompletableFuture.completedFuture(true); 207 } 208 209 private void sortBySettings(PlotQuery plotQuery, PlotPlayer<?> player) { 210 // Player may not be in a plot world when attempting to get to a plot home 211 PlotArea area = player.getApplicablePlotArea(); 212 if (Settings.Teleport.PER_WORLD_VISIT && area != null) { 213 plotQuery.relativeToArea(area) 214 .withSortingStrategy(SortingStrategy.SORT_BY_CREATION); 215 } else { 216 plotQuery.withSortingStrategy(SortingStrategy.SORT_BY_TEMP); 217 } 218 } 219 220 @Override 221 public Collection<Command> tab(PlotPlayer<?> player, String[] args, boolean space) { 222 final List<Command> completions = new ArrayList<>(); 223 switch (args.length - 1) { 224 case 0 -> { 225 completions.addAll( 226 TabCompletions.completeAreas(args[0])); 227 if (args[0].isEmpty()) { 228 // if no input is given, only suggest 1 - 3 229 completions.addAll( 230 TabCompletions.asCompletions("1", "2", "3")); 231 break; 232 } 233 // complete more numbers from the already given input 234 completions.addAll( 235 TabCompletions.completeNumbers(args[0], 10, 999)); 236 } 237 case 1 -> completions.addAll( 238 TabCompletions.completeNumbers(args[1], 10, 999)); 239 } 240 return completions; 241 } 242 243}