diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 351d25e..6c35d73 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -17,7 +17,6 @@ - diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 8c0840c..affa795 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,20 +5,26 @@ - - - - - + + + + + + + + + + + + + + + - - + + - - - - @@ -39,6 +45,15 @@ + + + + + + + + + @@ -55,14 +70,43 @@ + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + @@ -82,7 +126,7 @@ - + { "customColor": "", "associatedIndex": 0 @@ -97,8 +141,10 @@ + + + @@ -167,6 +216,28 @@ false + + + + + + + + + + + + + + + + + true + true + false + false + + @@ -181,6 +252,7 @@ + diff --git a/build.gradle.kts b/build.gradle.kts index 034572b..d4c61fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,10 @@ group = "de.kentoj.scrow" -version = "1.0.0" +version = "1.0.0-SNAPSHOT" subprojects { version = rootProject.version + + tasks.withType { + archiveVersion = "" + } } diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java index c733445..bad5d00 100644 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java @@ -3,6 +3,7 @@ package de.kentoj.scrow.bukkit; import de.kentoj.kencommandapi.BukkitKenCommandApi; import de.kentoj.scrow.bukkit.friends.FriendRequestService; import de.kentoj.scrow.bukkit.friends.FriendshipService; +import de.kentoj.scrow.bukkit.region.RegionManager; import de.kentoj.scrowlib.connection.DatabaseConnectionFactory; import de.kentoj.scrowlib.servermanager.ServerManager; import io.nats.client.Connection; @@ -19,8 +20,6 @@ public class ScrowAPI { @Getter private static DatabaseConnectionFactory dbConFactory; @Getter - private static BukkitKenCommandApi commandApi; - @Getter private static Scheduler minecraftScheduler; @Getter private static EconomyService economyService; @@ -32,17 +31,16 @@ public class ScrowAPI { private static Connection nats; @Getter private static ServerManager serverManager; + @Getter + private static final RegionManager regionManager = new RegionManager(); + @Getter + private static final BukkitKenCommandApi commandApi = new BukkitKenCommandApi(); @ApiStatus.Internal public static void setEconomyService(EconomyService economyService) { ScrowAPI.economyService = economyService; } - @ApiStatus.Internal - public static void setCommandApi(BukkitKenCommandApi commandApi) { - ScrowAPI.commandApi = commandApi; - } - @ApiStatus.Internal public static void setMinecraftScheduler(Scheduler minecraftScheduler) { ScrowAPI.minecraftScheduler = minecraftScheduler; diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/ChunkPos.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/ChunkPos.java new file mode 100644 index 0000000..d621849 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/ChunkPos.java @@ -0,0 +1,14 @@ +package de.kentoj.scrow.bukkit.region; + +import lombok.Value; +import org.bukkit.Chunk; + +@Value +public class ChunkPos { + int x; + int z; + + public static ChunkPos fromChunk(Chunk chunk) { + return new ChunkPos(chunk.getX(), chunk.getZ()); + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/Range.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/Range.java new file mode 100644 index 0000000..0b10d22 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/Range.java @@ -0,0 +1,27 @@ +package de.kentoj.scrow.bukkit.region; + +import lombok.Getter; + +@Getter +public final class Range { + private final double min; + private final double max; + + public boolean contains(double num) { + return min <= num && num <= max; + } + + public double getLength() { + return max - min; + } + + public Range(double a, double b) { + if (a < b) { + min = a; + max = b; + } else { + min = b; + max = a; + } + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/RegionManager.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/RegionManager.java new file mode 100644 index 0000000..95a9c83 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/RegionManager.java @@ -0,0 +1,42 @@ +package de.kentoj.scrow.bukkit.region; + +import de.kentoj.scrow.bukkit.region.impl.Region; + +import java.util.*; + +public class RegionManager { + + private final Map> chunkIndex = new HashMap<>(); + private final Map regions = new HashMap<>(); + + public List getRegions(ChunkPos chunkPos) { + long key = toKey(chunkPos.getX(), chunkPos.getZ()); + var uuids = chunkIndex.get(key); + if (uuids == null) return Collections.emptyList(); + return uuids.stream().map(regions::get).toList(); + } + + public void registerRegion(Region region) { + for (ChunkPos chunkPos : region.getChunks()) { + var key = toKey(chunkPos.getX(), chunkPos.getZ()); + chunkIndex.computeIfAbsent(key, __ -> new ArrayList<>()) + .add(region.getUuid()); + } + regions.put(region.getUuid(), region); + } + + public void unregisterRegion(Region region) { + for (ChunkPos chunkPos : region.getChunks()) { + var key = toKey(chunkPos.getX(), chunkPos.getZ()); + var list = chunkIndex.get(key); + if (list == null) return; + list.remove(region.getUuid()); + } + regions.remove(region.getUuid()); + } + + public long toKey(int x, int z) { + // chatgpt + return (((long) x) << 32) ^ (z & 0xffffffffL); + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionEvent.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionEvent.java new file mode 100644 index 0000000..f8ef7e4 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionEvent.java @@ -0,0 +1,14 @@ +package de.kentoj.scrow.bukkit.region.event; + +import de.kentoj.scrow.bukkit.region.impl.Region; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; + +@RequiredArgsConstructor +@Getter +public abstract class RegionEvent extends Event { + private final Region region; + private final Player player; +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionJoinEvent.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionJoinEvent.java new file mode 100644 index 0000000..8356425 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionJoinEvent.java @@ -0,0 +1,32 @@ +package de.kentoj.scrow.bukkit.region.event; + +import de.kentoj.scrow.bukkit.region.impl.Region; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class RegionJoinEvent extends RegionEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + @Setter + @Getter + private boolean isCancelled; + + public RegionJoinEvent(Region region, Player player) { + super(region, player); + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionLeaveEvent.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionLeaveEvent.java new file mode 100644 index 0000000..9c932d6 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/event/RegionLeaveEvent.java @@ -0,0 +1,31 @@ +package de.kentoj.scrow.bukkit.region.event; + +import de.kentoj.scrow.bukkit.region.impl.Region; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class RegionLeaveEvent extends RegionEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + @Setter + @Getter + private boolean isCancelled; + + public RegionLeaveEvent(Region region, Player player) { + super(region, player); + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/AbstractRegion.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/AbstractRegion.java new file mode 100644 index 0000000..1cf1e97 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/AbstractRegion.java @@ -0,0 +1,11 @@ +package de.kentoj.scrow.bukkit.region.impl; + +import lombok.Getter; + +import java.util.UUID; + +public abstract class AbstractRegion implements Region { + + @Getter + private final UUID uuid = UUID.randomUUID(); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/CuboidRegion.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/CuboidRegion.java new file mode 100644 index 0000000..ca3b598 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/CuboidRegion.java @@ -0,0 +1,45 @@ +package de.kentoj.scrow.bukkit.region.impl; + +import de.kentoj.scrow.bukkit.region.ChunkPos; +import de.kentoj.scrow.bukkit.region.Range; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.List; + +public class CuboidRegion extends AbstractRegion { + + private final Range xRange; + private final Range yRange; + private final Range zRange; + + public CuboidRegion(Location loc1, Location loc2) { + xRange = new Range(loc1.getX(), loc2.getX()); + yRange = new Range(loc1.getY(), loc2.getY()); + zRange = new Range(loc1.getY(), loc2.getY()); + } + + @Override + public boolean isInRegion(Location location) { + return xRange.contains(location.getX()) + && yRange.contains(location.getY()) + && zRange.contains(location.getZ()); + } + + @Override + public List getChunks() { + List res = new ArrayList<>(); + + int minChunkX = (int) Math.floor(xRange.getMin()) << 4; + int maxChunkX = (int) Math.ceil(xRange.getMax()) << 4; + for (int chunkX = minChunkX; chunkX < maxChunkX; chunkX++) { + int minChunkZ = (int) Math.floor(zRange.getMin()) << 4; + int maxChunkZ = (int) Math.ceil(zRange.getMax()) << 4; + for (int chunkZ = minChunkZ; chunkZ < maxChunkZ; chunkZ++) { + res.add(new ChunkPos(chunkX, chunkZ)); + } + } + + return res; + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/CylinderRegion.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/CylinderRegion.java new file mode 100644 index 0000000..5805113 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/CylinderRegion.java @@ -0,0 +1,42 @@ +package de.kentoj.scrow.bukkit.region.impl; + +import de.kentoj.scrow.bukkit.region.ChunkPos; +import lombok.RequiredArgsConstructor; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.List; + +@RequiredArgsConstructor +public class CylinderRegion extends AbstractRegion { + + private final Location center; + private final double height; + private final double radius; + + @Override + public boolean isInRegion(Location location) { + if (location.getY() < center.getY()) return false; + if (location.getY() > center.getY() + height) return false; + double diffX = location.getX() - center.getX(); + double diffZ = location.getZ() - center.getZ(); + return diffX * diffX + diffZ * diffZ <= radius * radius; + } + + @Override + public List getChunks() { + List res = new ArrayList<>(); + int a = (int) Math.ceil(radius / 16); + + var centerChunkX = center.getBlockX() >> 4; + var centerChunkZ = center.getBlockZ() >> 4; + + for (int x = -a; x <= a; x++) { + for (int z = -a; z <= a; z++) { + res.add(new ChunkPos(centerChunkX + x, centerChunkZ + z)); + } + } + + return res; + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/MergedRegion.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/MergedRegion.java new file mode 100644 index 0000000..30a785f --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/MergedRegion.java @@ -0,0 +1,39 @@ +package de.kentoj.scrow.bukkit.region.impl; + +import de.kentoj.scrow.bukkit.region.ChunkPos; +import lombok.RequiredArgsConstructor; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * region consisting of multiple regions + */ +@RequiredArgsConstructor +public class MergedRegion extends AbstractRegion { + + private final Region[] regions; + + @Override + public boolean isInRegion(Location location) { + for (Region region : regions) { + if (region.isInRegion(location)) return true; + } + return false; + } + + @Override + public List getChunks() { + return Arrays.stream(regions) + .map(Region::getChunks) + .reduce(new ArrayList<>(), (list, chunks) -> { + list.addAll(chunks); + return list; + }) + .stream() + .distinct() + .toList(); + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/Region.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/Region.java new file mode 100644 index 0000000..82dad09 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/region/impl/Region.java @@ -0,0 +1,19 @@ +package de.kentoj.scrow.bukkit.region.impl; + +import de.kentoj.scrow.bukkit.region.ChunkPos; +import org.bukkit.Location; + +import java.util.List; +import java.util.UUID; + +public interface Region { + + /** + * A random uuid assigned to the Region at creation + */ + UUID getUuid(); + + boolean isInRegion(Location location); + + List getChunks(); +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java index 689770b..be88436 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java @@ -3,6 +3,8 @@ package de.kentoj.scrow.bukkit; import de.kentoj.scrow.bukkit.crown.CrownCommand; import de.kentoj.scrow.bukkit.economy.CoinsCommand; import de.kentoj.scrow.bukkit.friends.command.FriendCommand; +import de.kentoj.scrow.bukkit.region.RegionPlayerTracker; +import de.kentoj.scrow.bukkit.region.RegionTask; import de.kentoj.scrow.bukkit.server.LobbyCommand; import de.kentoj.scrow.bukkit.server.PlayCommand; import org.bukkit.Bukkit; @@ -24,6 +26,8 @@ public class CoreImplPlugin extends JavaPlugin { ScrowAPI.getCommandApi().register(new LobbyCommand().getRootNode()); } + new RegionTask(new RegionPlayerTracker()).runTaskTimer(this, 10, 10); + registerServer(); } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java index 4784fee..648a1e7 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java @@ -52,7 +52,6 @@ public class ScrowAPISurface { new PostgresFriendRequestService(ScrowAPI.getDbConFactory()) : new InMemoryFriendRequestService()); ScrowAPI.setFriendshipService(ScrowAPI.getDbConFactory() != null ? new PostgresFriendshipService(ScrowAPI.getDbConFactory()) : new InMemoryFriendshipService()); - ScrowAPI.setCommandApi(new BukkitKenCommandApi()); } public static void destroyScrowAPI() { diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/region/RegionPlayerTracker.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/region/RegionPlayerTracker.java new file mode 100644 index 0000000..1f33cae --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/region/RegionPlayerTracker.java @@ -0,0 +1,28 @@ +package de.kentoj.scrow.bukkit.region; + +import de.kentoj.scrow.bukkit.region.impl.Region; +import org.bukkit.entity.Player; + +import java.util.*; + +public class RegionPlayerTracker { + + private final Map> playersInRegion = new HashMap<>(); + + public void registerInRegion(Region region, Player player) { + playersInRegion.computeIfAbsent(region.getUuid(), __ -> new ArrayList<>()) + .add(player.getUniqueId()); + } + + public void unregisterInRegion(Region region, Player player) { + var uuids = playersInRegion.get(region.getUuid()); + if (uuids == null) return; + uuids.remove(player.getUniqueId()); + } + + public boolean isInRegion(Region region, Player player) { + var uuids = playersInRegion.get(region.getUuid()); + if (uuids == null) return false; + return uuids.contains(player.getUniqueId()); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/region/RegionTask.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/region/RegionTask.java new file mode 100644 index 0000000..3a228cf --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/region/RegionTask.java @@ -0,0 +1,36 @@ +package de.kentoj.scrow.bukkit.region; + +import de.kentoj.scrow.bukkit.ScrowAPI; +import de.kentoj.scrow.bukkit.region.event.RegionJoinEvent; +import de.kentoj.scrow.bukkit.region.event.RegionLeaveEvent; +import de.kentoj.scrow.bukkit.region.impl.Region; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitRunnable; + +@RequiredArgsConstructor +public class RegionTask extends BukkitRunnable { + + private final RegionPlayerTracker playerTracker; + + @Override + public void run() { + for (var player : Bukkit.getOnlinePlayers()) { + var regions = ScrowAPI.getRegionManager().getRegions(ChunkPos.fromChunk(player.getLocation().getChunk())); + for (Region region : regions) { + var tracked = playerTracker.isInRegion(region, player); + var inside = region.isInRegion(player.getLocation()); + if (!tracked && inside) { + var ev = new RegionJoinEvent(region, player); + Bukkit.getPluginManager().callEvent(ev); + if (!ev.isCancelled()) playerTracker.registerInRegion(region, player); + } else if (tracked && !inside) { + var ev = new RegionLeaveEvent(region, player); + Bukkit.getPluginManager().callEvent(ev); + if (!ev.isCancelled()) + playerTracker.unregisterInRegion(region, player); + } + } + } + } +} diff --git a/core-lib/build.gradle.kts b/core-lib/build.gradle.kts index 88a6835..2952630 100644 --- a/core-lib/build.gradle.kts +++ b/core-lib/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("java") id("maven-publish") + id("de.kentoj.scrow.scrow-repository") id("de.kentoj.scrow.base-dependencies") id("de.kentoj.scrow.database") id("de.kentoj.scrow.java-library") diff --git a/core-velocity-api/build.gradle.kts b/core-velocity-api/build.gradle.kts index cd798f2..b1c24bb 100644 --- a/core-velocity-api/build.gradle.kts +++ b/core-velocity-api/build.gradle.kts @@ -37,4 +37,4 @@ publishing { from(components["java"]) } } -} +} \ No newline at end of file