204 lines
7.2 KiB
Java
204 lines
7.2 KiB
Java
package games.dmg.spigotbase;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.UUID;
|
|
import java.util.function.Function;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.NamespacedKey;
|
|
import org.bukkit.Registry;
|
|
import org.bukkit.Server;
|
|
import org.bukkit.World;
|
|
import org.bukkit.WorldCreator;
|
|
import org.bukkit.block.Biome;
|
|
|
|
final class PocketBaseWorldService {
|
|
private static final int PLATFORM_Y = 64;
|
|
private static final String WORLD_PREFIX = "spigot_base_pocket_";
|
|
private final Server server;
|
|
private final PluginSettingsProvider settings;
|
|
private final PocketBasePolicy policy;
|
|
private final Function<PocketBaseBiome, Biome> biomeResolver;
|
|
private final Map<UUID, UUID> ownersByWorld = new HashMap<>();
|
|
|
|
PocketBaseWorldService(Server server, PluginSettingsProvider settings) {
|
|
this(server, settings, biome -> resolveBiome(server, biome));
|
|
}
|
|
|
|
PocketBaseWorldService(
|
|
Server server,
|
|
PluginSettingsProvider settings,
|
|
Function<PocketBaseBiome, Biome> biomeResolver
|
|
) {
|
|
this.server = server;
|
|
this.settings = settings;
|
|
this.policy = new PocketBasePolicy(settings);
|
|
this.biomeResolver = biomeResolver;
|
|
}
|
|
|
|
void loadExisting(Map<UUID, PocketBaseState> states) {
|
|
for (PocketBaseState state : states.values()) {
|
|
if (state.level() > 0) {
|
|
setBiome(state.ownerId(), state.level(), state.biome());
|
|
setMobSpawning(
|
|
state.ownerId(),
|
|
state.hostileMobSpawningEnabled(),
|
|
state.passiveMobSpawningEnabled()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
World expand(UUID ownerId, int previousLevel, int newLevel) {
|
|
return expand(ownerId, previousLevel, newLevel, PocketBaseBiome.THE_VOID);
|
|
}
|
|
|
|
World expand(
|
|
UUID ownerId,
|
|
int previousLevel,
|
|
int newLevel,
|
|
PocketBaseBiome biome
|
|
) {
|
|
World world = ensureWorld(ownerId);
|
|
int newHalfSize = policy.size(newLevel) / 2;
|
|
int previousHalfSize = previousLevel == 0 ? 0 : policy.size(previousLevel) / 2;
|
|
for (int x = -newHalfSize; x < newHalfSize; x++) {
|
|
for (int z = -newHalfSize; z < newHalfSize; z++) {
|
|
boolean previouslyUnlocked = previousLevel > 0
|
|
&& x >= -previousHalfSize && x < previousHalfSize
|
|
&& z >= -previousHalfSize && z < previousHalfSize;
|
|
if (previouslyUnlocked) {
|
|
continue;
|
|
}
|
|
for (int y = PLATFORM_Y - 3; y < PLATFORM_Y; y++) {
|
|
world.getBlockAt(x, y, z).setType(Material.DIRT, false);
|
|
}
|
|
world.getBlockAt(x, PLATFORM_Y, z).setType(Material.GRASS_BLOCK, false);
|
|
}
|
|
}
|
|
applyBiome(world, previousLevel, newLevel, biome);
|
|
if (previousLevel == 0) {
|
|
createReturnPortal(world);
|
|
world.setSpawnLocation(0, PLATFORM_Y + 1, 5);
|
|
}
|
|
world.save();
|
|
return world;
|
|
}
|
|
|
|
Optional<UUID> ownerForWorld(UUID worldId) {
|
|
return Optional.ofNullable(ownersByWorld.get(worldId));
|
|
}
|
|
|
|
World world(UUID ownerId) {
|
|
return ensureWorld(ownerId);
|
|
}
|
|
|
|
void setMobSpawning(
|
|
UUID ownerId,
|
|
boolean hostileEnabled,
|
|
boolean passiveEnabled
|
|
) {
|
|
ensureWorld(ownerId).setSpawnFlags(hostileEnabled, passiveEnabled);
|
|
}
|
|
|
|
void setBiome(UUID ownerId, int level, PocketBaseBiome biome) {
|
|
World world = ensureWorld(ownerId);
|
|
applyBiome(world, 0, level, biome);
|
|
world.save();
|
|
}
|
|
|
|
Location arrival(UUID ownerId) {
|
|
return new Location(ensureWorld(ownerId), 0.5, PLATFORM_Y + 1, 5.5, 180.0f, 0.0f);
|
|
}
|
|
|
|
PocketPortalLocation returnPortal(UUID ownerId) {
|
|
World world = ensureWorld(ownerId);
|
|
return new PocketPortalLocation(
|
|
world.getUID(), world.getName(), -2, PLATFORM_Y + 1, 0, PocketPortalAxis.X
|
|
);
|
|
}
|
|
|
|
boolean returnPortalIsIntact(UUID ownerId, PocketPortalLocation portal) {
|
|
World world = ensureWorld(ownerId);
|
|
if (!portal.worldId().equals(world.getUID())) {
|
|
return false;
|
|
}
|
|
Material frame = Material.valueOf(settings.current().pocketBasePortalFrameMaterial());
|
|
return PocketPortalGeometry.frameBlocks(portal).stream()
|
|
.allMatch(position -> world.getBlockAt(
|
|
position.x(), position.y(), position.z()
|
|
).getType() == frame);
|
|
}
|
|
|
|
private World ensureWorld(UUID ownerId) {
|
|
String name = worldName(ownerId);
|
|
World world = server.getWorld(name);
|
|
boolean created = world == null;
|
|
if (created) {
|
|
WorldCreator creator = new WorldCreator(NamespacedKey.minecraft(name))
|
|
.environment(World.Environment.NORMAL)
|
|
.generator(new VoidPocketChunkGenerator())
|
|
.generateStructures(false);
|
|
world = server.createWorld(creator);
|
|
}
|
|
if (world == null) {
|
|
throw new IllegalStateException("Could not create Pocket Base world " + name);
|
|
}
|
|
if (created) {
|
|
world.setSpawnFlags(false, false);
|
|
}
|
|
ownersByWorld.put(world.getUID(), ownerId);
|
|
return world;
|
|
}
|
|
|
|
private void applyBiome(
|
|
World world,
|
|
int previousLevel,
|
|
int newLevel,
|
|
PocketBaseBiome selectedBiome
|
|
) {
|
|
int newHalfSize = policy.size(newLevel) / 2;
|
|
int previousHalfSize = previousLevel == 0 ? 0 : policy.size(previousLevel) / 2;
|
|
Biome biome = biomeResolver.apply(selectedBiome);
|
|
for (int x = -newHalfSize; x < newHalfSize; x += 4) {
|
|
for (int z = -newHalfSize; z < newHalfSize; z += 4) {
|
|
boolean previouslyUnlocked = previousLevel > 0
|
|
&& x >= -previousHalfSize && x < previousHalfSize
|
|
&& z >= -previousHalfSize && z < previousHalfSize;
|
|
if (previouslyUnlocked) {
|
|
continue;
|
|
}
|
|
for (int y = world.getMinHeight(); y < world.getMaxHeight(); y += 4) {
|
|
world.setBiome(x, y, z, biome);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Biome resolveBiome(Server server, PocketBaseBiome selectedBiome) {
|
|
Registry<Biome> biomeRegistry = server.getRegistry(Biome.class);
|
|
if (biomeRegistry == null) {
|
|
throw new IllegalStateException("Biome registry is unavailable");
|
|
}
|
|
return biomeRegistry.getOrThrow(
|
|
NamespacedKey.minecraft(selectedBiome.commandName())
|
|
);
|
|
}
|
|
|
|
private void createReturnPortal(World world) {
|
|
PocketPortalLocation portal = new PocketPortalLocation(
|
|
world.getUID(), world.getName(), -2, PLATFORM_Y + 1, 0, PocketPortalAxis.X
|
|
);
|
|
Material frame = Material.valueOf(settings.current().pocketBasePortalFrameMaterial());
|
|
for (BlockPosition position : PocketPortalGeometry.frameBlocks(portal)) {
|
|
world.getBlockAt(position.x(), position.y(), position.z()).setType(frame, false);
|
|
}
|
|
}
|
|
|
|
private static String worldName(UUID ownerId) {
|
|
return WORLD_PREFIX + ownerId.toString().replace("-", "");
|
|
}
|
|
}
|