DREAMFIRE Docs ← Back to site
Loading...
Searching...
No Matches
DreamLocationLimiter.java
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 Dreamfire Studio
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24package com.dreamfirestudios.dreamcore.DreamLocationLimiter;
25
26import com.dreamfirestudios.dreamcore.DreamCore;
27import com.dreamfirestudios.dreamcore.DreamJava.DreamClassID;
28import lombok.Getter;
29import org.bukkit.Location;
30import org.bukkit.World;
31import org.bukkit.entity.Player;
32import org.bukkit.util.Vector;
33
34import java.lang.reflect.Method;
35import java.util.ArrayList;
36import java.util.List;
37import java.util.Objects;
38import java.util.UUID;
39
61public class DreamLocationLimiter extends DreamClassID {
62
63 private final List<Player> players = new ArrayList<>();
64
65 @Getter private LocationLimiterType locationLimiterType = LocationLimiterType.SNAP_TO_ORIGIN;
66 @Getter private LocationLimiterStart locationLimiterStart = LocationLimiterStart.ORIGIN_POINT;
67 @Getter private String edgeMessage = "You have reached the edge of the location!";
68 @Getter private Location originPoint;
69 @Getter private int distanceExtents = 10;
70 @Getter private boolean stopped = false;
71
72 private World originWorld;
73
87 public void AddPlayer(Player player) {
88 if (player == null) return;
89 if (players.contains(player)) return;
90
92 if (evt.isCancelled()) return;
93
94 if (!canPlayerBeLimited(player)) return;
95
96 players.add(player);
97 }
98
106 public void RemovePlayer(Player player) {
107 if (player == null) return;
108 if (!players.remove(player)) return;
109 new LocationLimiterPlayerRemovedEvent(this, player);
110 }
111
116 public void startLocationLimiter() {
117 if (originPoint == null || originPoint.getWorld() == null) {
118 throw new IllegalStateException("Origin point/world must be set before starting the limiter.");
119 }
120 originWorld = originPoint.getWorld();
121
122 if (locationLimiterStart == LocationLimiterStart.ORIGIN_POINT) {
123 for (Player player : players) {
124 safeTeleport(player, originPoint);
125 }
126 }
127 }
128
136 public void tickLocationLimiter() {
137 if (stopped) return;
138 if (originPoint == null || originWorld == null) return;
139
140 for (Player player : new ArrayList<>(players)) {
141 if (player == null || !player.isOnline()) continue;
142
143 if (!Objects.equals(player.getWorld(), originWorld)) {
144 safeTeleport(player, originPoint);
145 continue;
146 }
147
148 final double distance = player.getLocation().distance(originPoint);
149 if (distance >= distanceExtents) {
150 handleOutOfBoundsPlayer(player, distance);
151 }
152 }
153 }
154
163 private void handleOutOfBoundsPlayer(Player player, double distance) {
164 if (edgeMessage != null && !edgeMessage.isEmpty()) {
165 player.sendMessage(edgeMessage);
166 }
167
168 switch (locationLimiterType) {
169 case SNAP_TO_ORIGIN:
170 safeTeleport(player, originPoint);
171 new LocationLimiterLimitHit(this, player);
172 break;
173 case PUSH_BACK:
174 pushPlayerBack(player);
175 new LocationLimiterLimitHit(this, player);
176 break;
177 }
178 }
179
184 private void pushPlayerBack(Player player) {
185 Vector toOrigin = originPoint.toVector().subtract(player.getLocation().toVector()).normalize();
186 player.setVelocity(toOrigin.multiply(0.5));
187 }
188
193 public void toggleLocationLimiter(boolean state) {
194 this.stopped = !state;
195 }
196
200 public void stopLocationLimiter() {
201 this.stopped = true;
202 DreamCore.DreamLocationLimiters.remove(getClassID());
203 }
204
209 public void setEdgeMessage(String message) {
210 this.edgeMessage = message == null ? "" : message;
211 }
212
217 public void setDistanceExtents(int extents) {
218 if (extents <= 0) throw new IllegalArgumentException("Distance extents must be > 0");
219 this.distanceExtents = extents;
220 }
221
227 this.locationLimiterType = Objects.requireNonNull(type, "type");
228 }
229
234 public void setOriginPoint(Location origin) {
235 if (origin == null || origin.getWorld() == null)
236 throw new IllegalArgumentException("Origin point/world cannot be null.");
237 this.originPoint = origin;
238 this.originWorld = origin.getWorld();
239 }
240
241 @SuppressWarnings("unchecked")
242 private boolean canPlayerBeLimited(Player player) {
243 try {
244 Class<?> actionEnum = Class.forName("com.dreamfirestudios.dreamcore.DreamfirePlayerAction");
245 Class<?> apiClass = Class.forName("com.dreamfirestudios.dreamcore.DreamfirePlayerActionAPI");
246
247 Object actionValue = Enum.valueOf((Class<Enum>) actionEnum, "PlayerLocationLimiter");
248 Method m = apiClass.getMethod("CanPlayerAction", actionEnum, UUID.class);
249 Object result = m.invoke(null, actionValue, player.getUniqueId());
250 return result instanceof Boolean && (Boolean) result;
251 } catch (Throwable ignored) {
252 return true; // default allow
253 }
254 }
255
256 private void safeTeleport(Player player, Location target) {
257 if (player == null || target == null || target.getWorld() == null) return;
258 player.teleport(target);
259 }
260
261 // ---------------------------------------------------------------------
262 // Builder
263 // ---------------------------------------------------------------------
264
280 public static class LocationLimiterBuilder {
281 private final List<Player> players = new ArrayList<>();
284 private String edgeMessage = "You have reached the edge of the location!";
285 private int extents = 10;
286
287 public LocationLimiterBuilder addPlayer(Player player) {
288 if (player != null) this.players.add(player);
289 return this;
290 }
291
293 this.type = Objects.requireNonNull(type, "type");
294 return this;
295 }
296
298 this.start = Objects.requireNonNull(start, "start");
299 return this;
300 }
301
302 public LocationLimiterBuilder edgeMessage(String message) {
303 if (message != null && !message.isBlank()) this.edgeMessage = message;
304 return this;
305 }
306
307 public LocationLimiterBuilder extents(int distance) {
308 if (distance <= 0) throw new IllegalArgumentException("Distance extents must be > 0");
309 this.extents = distance;
310 return this;
311 }
312
318 public DreamLocationLimiter build(Location originPoint) {
319 if (originPoint == null || originPoint.getWorld() == null)
320 throw new IllegalArgumentException("Origin point/world cannot be null.");
322 limiter.locationLimiterType = this.type;
323 limiter.locationLimiterStart = this.start;
324 limiter.edgeMessage = this.edgeMessage;
325 limiter.distanceExtents = this.extents;
326 limiter.originPoint = originPoint;
327 limiter.originWorld = originPoint.getWorld();
328 for (Player p : players) limiter.AddPlayer(p);
329 return DreamCore.DreamLocationLimiters.put(limiter.getClassID(), limiter);
330 }
331 }
332}
static final LinkedHashMap< UUID, DreamLocationLimiter > DreamLocationLimiters
DreamLocationLimiter build(Location originPoint)
Builds a new limiter and registers it with DreamCore.
Runtime limiter that constrains players to a defined radius around an origin point.
void setLocationLimiterType(LocationLimiterType type)
Updates the limiter type.
void RemovePlayer(Player player)
Removes a player from this limiter.
void toggleLocationLimiter(boolean state)
Enables or disables limiter enforcement.
void startLocationLimiter()
Starts the limiter and teleports players to origin if configured.
void stopLocationLimiter()
Stops this limiter and unregisters it from DreamCore.
void tickLocationLimiter()
Main tick handler that enforces the limiter each loop.
void setDistanceExtents(int extents)
Updates the distance extent (radius).
Event fired when a player is about to be added to a DreamLocationLimiter.
ORIGIN_POINT
Teleports all players to the origin point when the limiter starts.
Behavior of DreamLocationLimiter when players exceed the boundary.