From 9140a779121f8d7cae72f6c29d0baf7652431858 Mon Sep 17 00:00:00 2001
From: panda-hackerman <58387096+panda-hackerman@users.noreply.github.com>
Date: Thu, 15 Sep 2022 02:00:30 -0700
Subject: [PATCH] Fixed stuff, it works now
---
pom.xml | 26 ++++-
.../randomspawnpoint/RandomSpawnpoint.java | 8 ++
.../async/GenerateLocationAsync.java | 97 +++++++++++++++++++
.../randomspawnpoint/async/LocationCache.java | 76 +++++++++++++++
.../randomspawnpoint/commands/AsyncTest.java | 45 +++++++++
.../events/PlayerRespawn.java | 9 +-
.../randomspawnpoint/util/SetSpawn.java | 2 +-
src/main/resources/plugin.yml | 6 ++
8 files changed, 255 insertions(+), 14 deletions(-)
create mode 100644 src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/GenerateLocationAsync.java
create mode 100644 src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/LocationCache.java
create mode 100644 src/main/java/dev/michaud/greenpanda/randomspawnpoint/commands/AsyncTest.java
diff --git a/pom.xml b/pom.xml
index e5c02fa..95e9dae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,16 +31,22 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.2.4
+ 3.3.0
+
+ ${project.build.directory}/dependency-reduced-pom.xml
+
+
+ io.papermc.lib
+ dev.michaud.greenpanda.randomspawnpoint.paperlib
+
+
+
package
shade
-
- false
-
@@ -58,6 +64,10 @@
papermc-repo
https://repo.papermc.io/repository/maven-public/
+
+ papermc
+ https://papermc.io/repo/repository/maven-public/
+
sonatype
https://oss.sonatype.org/content/groups/public/
@@ -71,5 +81,11 @@
1.19-R0.1-SNAPSHOT
provided
+
+ io.papermc
+ paperlib
+ 1.0.7
+ compile
+
-
+
\ No newline at end of file
diff --git a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/RandomSpawnpoint.java b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/RandomSpawnpoint.java
index b7808d2..c2bd5e1 100644
--- a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/RandomSpawnpoint.java
+++ b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/RandomSpawnpoint.java
@@ -1,9 +1,12 @@
package dev.michaud.greenpanda.randomspawnpoint;
+import dev.michaud.greenpanda.randomspawnpoint.async.LocationCache;
+import dev.michaud.greenpanda.randomspawnpoint.commands.AsyncTest;
import dev.michaud.greenpanda.randomspawnpoint.commands.SetRandomSpawnpoint;
import dev.michaud.greenpanda.randomspawnpoint.events.PlayerJoin;
import dev.michaud.greenpanda.randomspawnpoint.events.PlayerRespawn;
import dev.michaud.greenpanda.randomspawnpoint.util.ServerProperties;
+import io.papermc.lib.PaperLib;
import java.util.HashSet;
import org.bukkit.Material;
import org.bukkit.World;
@@ -42,9 +45,14 @@ public void onEnable() {
getServer().getPluginManager().registerEvents(new PlayerJoin(), this);
getCommand("setrandomspawnpoint").setExecutor(new SetRandomSpawnpoint());
+ getCommand("asynctest").setExecutor(new AsyncTest());
loadConfig();
+ PaperLib.suggestPaper(plugin);
+
+ LocationCache.generateLocations(10);
+
}
private void loadConfig() {
diff --git a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/GenerateLocationAsync.java b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/GenerateLocationAsync.java
new file mode 100644
index 0000000..578bb71
--- /dev/null
+++ b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/GenerateLocationAsync.java
@@ -0,0 +1,97 @@
+package dev.michaud.greenpanda.randomspawnpoint.async;
+
+import dev.michaud.greenpanda.randomspawnpoint.RandomSpawnpoint;
+import io.papermc.lib.PaperLib;
+import java.util.HashSet;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadLocalRandom;
+import org.bukkit.ChunkSnapshot;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class GenerateLocationAsync {
+
+ private static void log(String msg) {
+ String prefix = "[Thread " + Thread.currentThread().getId() + "] ";
+ RandomSpawnpoint.getPlugin().getServer().getConsoleSender().sendMessage(prefix + msg);
+ }
+
+ @Contract(" -> new")
+ public static @NotNull CompletableFuture getLocation() {
+ return CompletableFuture.supplyAsync(GenerateLocationAsync::generateSafeLocation);
+ }
+
+ private static @NotNull Location generateSafeLocation() {
+
+ log("Generating location...");
+
+ Location location;
+
+ do {
+ location = randomLocationInCircle(RandomSpawnpoint.spawnRadius, 0, 0);
+ }
+ while (!isSafeLocation(location));
+
+ return location;
+
+ }
+
+ @Contract("_, _, _ -> new")
+ private static @NotNull Location randomLocationInCircle(int radius, int centerX, int centerY) {
+
+ log("Getting random location in a circle");
+
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ double rand1 = random.nextDouble();
+ double rand2 = random.nextDouble();
+
+ double r = radius * Math.sqrt(rand1);
+ double theta = rand2 * 2 * Math.PI;
+
+ int x = (int) (centerX + r * Math.cos(theta));
+ int z = (int) (centerY + r * Math.sin(theta));
+
+ return new Location(RandomSpawnpoint.defaultWorld, x, 0, z);
+
+ }
+
+ @Contract("null -> false")
+ private static boolean isSafeLocation(@Nullable Location location) {
+
+ if (location == null) {
+ return false;
+ }
+
+ World world = location.getWorld();
+ int x = location.getBlockX() >> 4;
+ int z = location.getBlockZ() >> 4;
+
+ ChunkSnapshot chunk = PaperLib.getChunkAtAsync(world, x, z, true, true)
+ .join()
+ .getChunkSnapshot();
+
+ int chunkX = location.getBlockX() - (chunk.getX() << 4);
+ int chunkZ = location.getBlockZ() - (chunk.getZ() << 4);
+ int chunkY = chunk.getHighestBlockYAt(chunkX, chunkZ);
+
+ location.setY(chunkY + 1);
+
+ Material ground = chunk.getBlockType(chunkX, chunkY, chunkZ);
+ Material feet = chunk.getBlockType(chunkX, chunkY + 1, chunkZ);
+ Material head = chunk.getBlockType(chunkX, chunkY + 2, chunkZ);
+
+ HashSet blacklist = RandomSpawnpoint.blockBlacklist;
+
+ return ground.isSolid()
+ && feet.isAir() && head.isAir()
+ && !blacklist.contains(ground)
+ && !blacklist.contains(feet)
+ && !blacklist.contains(head);
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/LocationCache.java b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/LocationCache.java
new file mode 100644
index 0000000..f99221f
--- /dev/null
+++ b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/async/LocationCache.java
@@ -0,0 +1,76 @@
+package dev.michaud.greenpanda.randomspawnpoint.async;
+
+import dev.michaud.greenpanda.randomspawnpoint.RandomSpawnpoint;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.bukkit.Location;
+
+public class LocationCache {
+
+ public static final int TIMEOUT_SECONDS = 120;
+ public static final int MAX_CACHE_SIZE = 10;
+
+ private static final Queue locations = new LinkedBlockingQueue<>(MAX_CACHE_SIZE);
+ private static final Logger logger = RandomSpawnpoint.getPlugin().getLogger();
+
+ public static Location getLocation() {
+
+ Location location = locations.poll();
+
+ generateLocations(1);
+
+ return location;
+
+ }
+
+ public static void generateLocations(int num) {
+
+ for (int i = 0; i < num; i++) {
+ generateLocation();
+ }
+
+ }
+
+ public static void generateLocation() {
+
+ GenerateLocationAsync.getLocation()
+ .thenAccept(LocationCache::addIfNotNull)
+ .orTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ .whenComplete(LocationCache::onComplete);
+
+ }
+
+ private static void onComplete(Void value, Throwable error) {
+
+ if (error instanceof TimeoutException) {
+ logger.log(Level.SEVERE, "Thread timed out" + error);
+ } else if (error != null) {
+ logger.log(Level.SEVERE, "Thread ran into an error: " + error);
+ }
+
+ }
+
+ private static void addIfNotNull(Location location) {
+
+ if (location != null) {
+ locations.offer(location);
+ logger.log(Level.INFO, "Adding location {0}", location);
+ } else {
+ generateLocations(1);
+ logger.log(Level.INFO, "Location was null");
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/commands/AsyncTest.java b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/commands/AsyncTest.java
new file mode 100644
index 0000000..4b07cad
--- /dev/null
+++ b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/commands/AsyncTest.java
@@ -0,0 +1,45 @@
+package dev.michaud.greenpanda.randomspawnpoint.commands;
+
+import dev.michaud.greenpanda.randomspawnpoint.async.LocationCache;
+import io.papermc.lib.PaperLib;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Location;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
+import org.jetbrains.annotations.NotNull;
+
+public class AsyncTest implements CommandExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
+ @NotNull String label, @NotNull String[] args) {
+
+ if (!(sender instanceof Player player)) {
+ sender.sendMessage("You must be a player to run that command");
+ return true;
+ }
+
+ Location location = LocationCache.getLocation();
+
+ if (location == null) {
+ player.sendMessage(Component.text("Unable to find a suitable spawn location")
+ .color(NamedTextColor.RED));
+ return true;
+ }
+
+ int x = location.getBlockX();
+ int y = location.getBlockY();
+ int z = location.getBlockZ();
+
+ player.sendMessage(Component.text("Set your spawn point to " + x + ", " + y + ", " + z));
+ PaperLib.teleportAsync(player, location, TeleportCause.COMMAND);
+
+ return true;
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/events/PlayerRespawn.java b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/events/PlayerRespawn.java
index 061080b..845c6c4 100644
--- a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/events/PlayerRespawn.java
+++ b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/events/PlayerRespawn.java
@@ -16,15 +16,8 @@ public class PlayerRespawn implements Listener {
public void onPlayerRespawn(PlayerRespawnEvent event) {
Player player = event.getPlayer();
- /*Location location = event.getRespawnLocation();*/
- if (event.isBedSpawn()) {
- /*player.sendMessage(Component.text("Spawned in bed").color(NamedTextColor.AQUA));*/
- return;
- }
-
- if (event.isAnchorSpawn()) {
- /*player.sendMessage(Component.text("Spawned at anchor").color(NamedTextColor.AQUA));*/
+ if (event.isBedSpawn() || event.isAnchorSpawn()) {
return;
}
diff --git a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/util/SetSpawn.java b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/util/SetSpawn.java
index 5f24af0..0c54147 100644
--- a/src/main/java/dev/michaud/greenpanda/randomspawnpoint/util/SetSpawn.java
+++ b/src/main/java/dev/michaud/greenpanda/randomspawnpoint/util/SetSpawn.java
@@ -20,7 +20,7 @@ public class SetSpawn {
/**
* Sets the player's spawn point to a random position. The location must be safe as defined by
- * {@link SetSpawn#getSafeLocation(World)}. If after 100 tries no suitable location is found, the
+ * {@link SetSpawn#getSafeLocation(World)}.
*
* @param world the world to set the player's spawnpoint in.
* @param player the player to set the spawnpoint for.
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 1ce864f..018b023 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -8,7 +8,13 @@ commands:
setrandomspawnpoint:
description: Sets your spawnpoint to a random location.
permission: randomspawnpoint.setspawn
+ asynctest:
+ description: Sets your spawnpoint to a random location asynchronously.
+ permission: randomspawnpoint.asynctest
permissions:
randomspawnpoint.setspawn:
description: Lets you set your spawnpoint to a random location.
+ default: op
+ randomspawnpoint.asynctest:
+ description: Lets you spawnpoint to a random location asynchronously.
default: op
\ No newline at end of file