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.plot; 020 021import com.plotsquared.core.location.Direction; 022import org.checkerframework.checker.nullness.qual.NonNull; 023import org.checkerframework.checker.nullness.qual.Nullable; 024 025import java.util.Iterator; 026import java.util.NoSuchElementException; 027 028/** 029 * Plot (X,Y) tuples for plot locations 030 * within a plot area 031 */ 032public final class PlotId { 033 034 private final int x; 035 private final int y; 036 private final int hash; 037 038 /** 039 * PlotId class (PlotId x,y values do not correspond to Block locations) 040 * 041 * @param x The plot x coordinate 042 * @param y The plot y coordinate 043 */ 044 private PlotId(final int x, final int y) { 045 this.x = x; 046 this.y = y; 047 this.hash = (this.getX() << 16) | (this.getY() & 0xFFFF); 048 } 049 050 /** 051 * Create a new plot ID instance 052 * 053 * @param x The plot x coordinate 054 * @param y The plot y coordinate 055 * @return a new PlotId at x,y 056 */ 057 public static @NonNull PlotId of(final int x, final int y) { 058 return new PlotId(x, y); 059 } 060 061 /** 062 * Get a Plot Id based on a string 063 * 064 * @param string to create id from 065 * @return the PlotId representation of the argument 066 * @throws IllegalArgumentException if the string does not contain a valid PlotId 067 */ 068 public static @NonNull PlotId fromString(final @NonNull String string) { 069 final PlotId plot = fromStringOrNull(string); 070 if (plot == null) { 071 throw new IllegalArgumentException("Cannot create PlotID. String invalid."); 072 } 073 return plot; 074 } 075 076 /** 077 * Attempt to parse a plot ID from a string 078 * 079 * @param string ID string 080 * @return Plot ID, or {@code null} if none could be parsed 081 */ 082 public static @Nullable PlotId fromStringOrNull(final @NonNull String string) { 083 final String[] parts = string.split("[;_,.]"); 084 if (parts.length < 2) { 085 return null; 086 } 087 int x; 088 int y; 089 try { 090 x = Integer.parseInt(parts[0]); 091 y = Integer.parseInt(parts[1]); 092 } catch (final NumberFormatException ignored) { 093 return null; 094 } 095 return of(x, y); 096 } 097 098 /** 099 * Gets the PlotId from the HashCode<br> 100 * Note: Only accurate for small x,z values (short) 101 * 102 * @param hash ID hash 103 * @return Plot ID 104 */ 105 public static @NonNull PlotId unpair(final int hash) { 106 return PlotId.of(hash >> 16, hash & 0xFFFF); 107 } 108 109 /** 110 * Get a copy of the plot ID 111 * 112 * @return Plot ID copy 113 * @deprecated PlotId is immutable, copy is not required. 114 */ 115 @Deprecated(forRemoval = true, since = "6.10.2") 116 public @NonNull PlotId copy() { 117 return this; 118 } 119 120 /** 121 * Get the ID X component 122 * 123 * @return X component 124 */ 125 public int getX() { 126 return this.x; 127 } 128 129 /** 130 * Get the ID Y component 131 * 132 * @return Y component 133 */ 134 public int getY() { 135 return this.y; 136 } 137 138 /** 139 * Get the next plot ID for claiming purposes 140 * 141 * @return Next plot ID 142 */ 143 public @NonNull PlotId getNextId() { 144 final int absX = Math.abs(x); 145 final int absY = Math.abs(y); 146 if (absX > absY) { 147 if (x > 0) { 148 return PlotId.of(x, y + 1); 149 } else { 150 return PlotId.of(x, y - 1); 151 } 152 } else if (absY > absX) { 153 if (y > 0) { 154 return PlotId.of(x - 1, y); 155 } else { 156 return PlotId.of(x + 1, y); 157 } 158 } else { 159 if (x == y && x > 0) { 160 return PlotId.of(x, y + 1); 161 } 162 if (x == absX) { 163 return PlotId.of(x, y + 1); 164 } 165 if (y == absY) { 166 return PlotId.of(x, y - 1); 167 } 168 return PlotId.of(x + 1, y); 169 } 170 } 171 172 /** 173 * Get the PlotId in a relative direction 174 * 175 * @param direction Direction 176 * @return Relative plot ID 177 */ 178 public @NonNull PlotId getRelative(final @NonNull Direction direction) { 179 return switch (direction) { 180 case NORTH -> PlotId.of(this.getX(), this.getY() - 1); 181 case EAST -> PlotId.of(this.getX() + 1, this.getY()); 182 case SOUTH -> PlotId.of(this.getX(), this.getY() + 1); 183 case WEST -> PlotId.of(this.getX() - 1, this.getY()); 184 default -> this; 185 }; 186 } 187 188 @Override 189 public boolean equals(final Object obj) { 190 if (this == obj) { 191 return true; 192 } 193 if (obj == null) { 194 return false; 195 } 196 if (this.hashCode() != obj.hashCode()) { 197 return false; 198 } 199 if (getClass() != obj.getClass()) { 200 return false; 201 } 202 final PlotId other = (PlotId) obj; 203 return this.getX() == other.getX() && this.getY() == other.getY(); 204 } 205 206 /** 207 * Get a String representation of the plot ID where the 208 * components are separated by ";" 209 * 210 * @return {@code x + ";" + y} 211 */ 212 @Override 213 public @NonNull String toString() { 214 return this.getX() + ";" + this.getY(); 215 } 216 217 /** 218 * Get a String representation of the plot ID where the 219 * components are separated by a specified string 220 * 221 * @param separator Separator 222 * @return {@code x + separator + y} 223 */ 224 public @NonNull String toSeparatedString(String separator) { 225 return this.getX() + separator + this.getY(); 226 } 227 228 /** 229 * Get a String representation of the plot ID where the 230 * components are separated by "," 231 * 232 * @return {@code x + "," + y} 233 */ 234 public @NonNull String toCommaSeparatedString() { 235 return this.getX() + "," + this.getY(); 236 } 237 238 /** 239 * Get a String representation of the plot ID where the 240 * components are separated by "_" 241 * 242 * @return {@code x + "_" + y} 243 */ 244 public @NonNull String toUnderscoreSeparatedString() { 245 return this.getX() + "_" + this.getY(); 246 } 247 248 /** 249 * Get a String representation of the plot ID where the 250 * components are separated by "-" 251 * 252 * @return {@code x + "-" + y} 253 */ 254 public @NonNull String toDashSeparatedString() { 255 return this.getX() + "-" + this.getY(); 256 } 257 258 @Override 259 public int hashCode() { 260 return this.hash; 261 } 262 263 264 public static final class PlotRangeIterator implements Iterator<PlotId>, Iterable<PlotId> { 265 266 private final PlotId start; 267 private final PlotId end; 268 269 private int x; 270 private int y; 271 272 private PlotRangeIterator(final @NonNull PlotId start, final @NonNull PlotId end) { 273 this.start = start; 274 this.end = end; 275 this.x = this.start.getX(); 276 this.y = this.start.getY(); 277 } 278 279 public static PlotRangeIterator range(final @NonNull PlotId start, final @NonNull PlotId end) { 280 return new PlotRangeIterator(start, end); 281 } 282 283 @Override 284 public boolean hasNext() { 285 // end is fully included 286 return this.x <= this.end.getX() && this.y <= this.end.getY(); 287 } 288 289 @Override 290 public PlotId next() { 291 if (!hasNext()) { 292 throw new NoSuchElementException("The iterator has no more entries"); 293 } 294 // increment *after* getting the result to include the minimum 295 // the id to return 296 PlotId result = PlotId.of(this.x, this.y); 297 // first increase y, then x 298 if (this.y == this.end.getY()) { 299 this.x++; 300 this.y = this.start.getY(); 301 } else { 302 this.y++; 303 } 304 return result; 305 } 306 307 @NonNull 308 @Override 309 public Iterator<PlotId> iterator() { 310 return this; 311 } 312 313 } 314 315}