Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,22 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<version>3.3.0</version>
<configuration>
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
<relocations>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>dev.michaud.greenpanda.randomspawnpoint.paperlib</shadedPattern> <!-- Replace this -->
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
Expand All @@ -58,6 +64,10 @@
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
Expand All @@ -71,5 +81,11 @@
<version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.7</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
</project>
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Location> 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<Material> blacklist = RandomSpawnpoint.blockBlacklist;

return ground.isSolid()
&& feet.isAir() && head.isAir()
&& !blacklist.contains(ground)
&& !blacklist.contains(feet)
&& !blacklist.contains(head);

}

}
Original file line number Diff line number Diff line change
@@ -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<Location> 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");
}

}

}
Original file line number Diff line number Diff line change
@@ -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;

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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