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