Copies directly from seed world and places structures

This commit is contained in:
tastybento 2022-12-03 21:41:56 -08:00
parent 12e1568c75
commit e6026dca4f
11 changed files with 393 additions and 44 deletions

View File

@ -26,6 +26,7 @@ import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.boxed.generators.BoxedBiomeGenerator;
import world.bentobox.boxed.generators.BoxedChunkGenerator;
import world.bentobox.boxed.generators.BoxedSeedChunkGenerator;
import world.bentobox.boxed.generators.SeedBiomeGenerator;
import world.bentobox.boxed.listeners.AdvancementListener;
import world.bentobox.boxed.listeners.EnderPearlListener;
import world.bentobox.boxed.listeners.NewAreaListener;
@ -147,7 +148,7 @@ public class Boxed extends GameModeAddon {
log("Creating Boxed Seed world ...");
seedWorld = WorldCreator
.name("seed")
.generator(new BoxedSeedChunkGenerator())
.generator(new BoxedSeedChunkGenerator(this))
.environment(Environment.NORMAL)
.generateStructures(false)
.seed(getSettings().getSeed())
@ -188,10 +189,10 @@ public class Boxed extends GameModeAddon {
double percent = size * 4D * size;
int count = 0;
int last = 0;
for (int x = -size; x < size; x ++) {
for (int z = -size; z < size; z++) {
ChunkSnapshot chunk = seedWorld.getChunkAt(x, z).getChunkSnapshot(true, true, false);
this.chunkGenerator.setChunk(chunk);
for (int x = -size; x <= size; x ++) {
for (int z = -size; z <= size; z++) {
ChunkSnapshot chunk = seedWorld.getChunkAt(this.settings.getChunkX() + x, this.settings.getChunkZ() + z).getChunkSnapshot(true, true, false);
this.chunkGenerator.setChunk(x, z, chunk);
count++;
int p = (int) (count / percent * 100);
if (p % 10 == 0 && p != last) {

View File

@ -73,10 +73,15 @@ public class Settings implements WorldSettings {
private String worldName = "boxed_world";
@ConfigComment("World seed.")
@ConfigComment("If you change this, stop the server and delete the worlds made.")
@ConfigEntry(path = "world.generator.seed", needsReset = true)
private long seed = 602103456450L;
@ConfigComment("Seed center chunk. This is where the areas are copied from.")
@ConfigEntry(path = "world.generator.start-chunk.x")
private int chunkX = 0;
@ConfigEntry(path = "world.generator.start-chunk.z")
private int chunkZ = 0;
@ConfigComment("World difficulty setting - PEACEFUL, EASY, NORMAL, HARD")
@ConfigComment("Other plugins may override this setting")
@ConfigEntry(path = "world.difficulty")
@ -1825,4 +1830,32 @@ public class Settings implements WorldSettings {
public void setDenyVisitorAdvancements(boolean denyVisitorAdvancements) {
this.denyVisitorAdvancements = denyVisitorAdvancements;
}
/**
* @return the chunkX
*/
public int getChunkX() {
return chunkX;
}
/**
* @param chunkX the chunkX to set
*/
public void setChunkX(int chunkX) {
this.chunkX = chunkX;
}
/**
* @return the chunkZ
*/
public int getChunkZ() {
return chunkZ;
}
/**
* @param chunkZ the chunkZ to set
*/
public void setChunkZ(int chunkZ) {
this.chunkZ = chunkZ;
}
}

View File

@ -25,6 +25,7 @@ import com.google.common.base.Enums;
import world.bentobox.boxed.Boxed;
/**
* NOT USED
* @author tastybento
*
*/
@ -104,7 +105,7 @@ public abstract class AbstractBoxedBiomeProvider extends BiomeProvider {
return getMappedBiome(x,z);
}
} else {
return Biome.WARM_OCEAN;
return this.defaultBiome;
}
}

View File

@ -0,0 +1,58 @@
package world.bentobox.boxed.generators;
import java.util.Arrays;
import java.util.List;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.WorldInfo;
import world.bentobox.boxed.Boxed;
/**
* Copies biomes from seed world
* @author tastybento
*
*/
public abstract class AbstractCopyBiomeProvider extends BiomeProvider {
private final Boxed addon;
private final Biome defaultBiome;
protected final int dist;
protected AbstractCopyBiomeProvider(Boxed boxed, Environment env, Biome defaultBiome) {
this.addon = boxed;
this.defaultBiome = defaultBiome;
dist = addon.getSettings().getIslandDistance();
}
@Override
public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) {
int chunkX = (int)((double)x/16);
int chunkZ = (int)((double)z/16);
int size = (int)(dist / 16D); // Convert to chunk
chunkX = BoxedChunkGenerator.repeatCalc(chunkX, size);
chunkZ = BoxedChunkGenerator.repeatCalc(chunkZ, size);
ChunkSnapshot c = addon.getChunkGenerator().getChunk(chunkX, chunkZ);
if (c != null) {
int xx = Math.floorMod(x, 16);
int zz = Math.floorMod(z, 16);
int yy = Math.max(Math.min(y * 4, worldInfo.getMaxHeight()), worldInfo.getMinHeight()); // To handle bug in Spigot
return c.getBiome(xx, yy, zz);
} else {
return defaultBiome;
}
}
@Override
public List<Biome> getBiomes(WorldInfo worldInfo) {
// Return all of them for now!
return Arrays.stream(Biome.values()).filter(b -> !b.equals(Biome.CUSTOM)).toList();
}
}

View File

@ -0,0 +1,147 @@
package world.bentobox.boxed.generators;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.WorldInfo;
import org.bukkit.util.Vector;
import com.google.common.base.Enums;
import world.bentobox.boxed.Boxed;
/**
* Generates the biomes for the seed world
* @author tastybento
*
*/
public abstract class AbstractSeendBiomeProvider extends BiomeProvider {
private static final Map<Environment, String> ENV_MAP;
static {
Map<Environment, String> e = new EnumMap<>(Environment.class);
e.put(Environment.NORMAL, "distribution.overworld");
e.put(Environment.NETHER, "distribution.nether");
e.put(Environment.THE_END, "distribution.the_end");
ENV_MAP = Collections.unmodifiableMap(e);
}
private final Boxed addon;
private final Biome defaultBiome;
protected final int dist;
private final int offsetX;
private final int offsetZ;
protected final Map<BlockFace, SortedMap<Double, Biome>> quadrants;
protected AbstractSeendBiomeProvider(Boxed boxed, Environment env, Biome defaultBiome) {
this.addon = boxed;
this.defaultBiome = defaultBiome;
dist = addon.getSettings().getIslandDistance();
offsetX = addon.getSettings().getIslandXOffset();
offsetZ = addon.getSettings().getIslandZOffset();
// Load the config
File biomeFile = new File(addon.getDataFolder(), "biomes.yml");
// Check if it exists and if not, save it from the jar
if (!biomeFile.exists()) {
addon.saveResource("biomes.yml", true);
}
YamlConfiguration config = YamlConfiguration.loadConfiguration(biomeFile);
SortedMap<Double, Biome> northEast = loadQuad(config, ENV_MAP.get(env) + ".north-east");
SortedMap<Double, Biome> southEast = loadQuad(config, ENV_MAP.get(env) + ".south-east");
SortedMap<Double, Biome> northWest = loadQuad(config, ENV_MAP.get(env) + ".north-west");
SortedMap<Double, Biome> southWest = loadQuad(config, ENV_MAP.get(env) + ".south-west");
quadrants = new EnumMap<>(BlockFace.class);
quadrants.put(BlockFace.NORTH_EAST, northEast);
quadrants.put(BlockFace.NORTH_WEST, northWest);
quadrants.put(BlockFace.SOUTH_EAST, southEast);
quadrants.put(BlockFace.SOUTH_WEST, southWest);
}
private Biome getBiome(BlockFace dir, double d) {
Entry<Double, Biome> en = ((TreeMap<Double, Biome>) quadrants.get(dir)).ceilingEntry(d);
return en == null ? defaultBiome : en.getValue();
}
@Override
public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) {
return getMappedBiome(x,z);
}
private Biome getMappedBiome(int x, int z) {
/*
* Biomes go around the island centers
*
*/
Vector s = new Vector(x, 0, z);
Vector l = getClosestIsland(s);
double dis = l.distanceSquared(s);
double d = dis / (dist * dist);
Vector direction = s.subtract(l);
if (direction.getBlockX() <= 0 && direction.getBlockZ() <= 0) {
return getBiome(BlockFace.NORTH_WEST, d);
} else if (direction.getBlockX() > 0 && direction.getBlockZ() <= 0) {
return getBiome(BlockFace.NORTH_EAST, d);
} else if (direction.getBlockX() <= 0 && direction.getBlockZ() > 0) {
return getBiome(BlockFace.SOUTH_WEST, d);
}
return getBiome(BlockFace.SOUTH_EAST, d);
}
@Override
public List<Biome> getBiomes(WorldInfo worldInfo) {
// Return all of them for now!
return Arrays.stream(Biome.values()).filter(b -> !b.equals(Biome.CUSTOM)).toList();
}
private Vector getClosestIsland(Vector v) {
int d = dist * 2;
long x = Math.round((double) v.getBlockX() / d) * d + offsetX;
long z = Math.round((double) v.getBlockZ() / d) * d + offsetZ;
return new Vector(x, 0, z);
}
private SortedMap<Double, Biome> loadQuad(YamlConfiguration config, String string) {
SortedMap<Double, Biome> result = new TreeMap<>();
if (!config.contains(string)) {
return result;
}
for (String ring : config.getStringList(string)) {
String[] split = ring.split(":");
if (split.length == 2) {
try {
double d = Double.parseDouble(split[0]);
Biome biome = Enums.getIfPresent(Biome.class, split[1].toUpperCase(Locale.ENGLISH)).orNull();
if (biome == null) {
addon.logError(split[1].toUpperCase(Locale.ENGLISH) + " is an unknown biome on this server.");
} else {
result.put(d, biome);
}
} catch(Exception e) {
addon.logError(string + ": " + split[0] + " does not seem to be a double. For integers add a .0 to the end");
}
} else {
addon.logError(ring + " must be in the format ratio:biome where ratio is a double.");
}
}
return result;
}
}

View File

@ -9,7 +9,7 @@ import world.bentobox.boxed.Boxed;
* @author tastybento
*
*/
public class BoxedBiomeGenerator extends AbstractBoxedBiomeProvider {
public class BoxedBiomeGenerator extends AbstractCopyBiomeProvider {
public BoxedBiomeGenerator(Boxed boxed) {
super(boxed, Environment.NORMAL, Biome.OCEAN);

View File

@ -37,11 +37,12 @@ public class BoxedChunkGenerator extends ChunkGenerator {
}
/**
* @param z - chunk z coord
* @param x - chunk x coord
* @param chunk the chunk to set
*/
public void setChunk(ChunkSnapshot chunk) {
// Make the coords always positive
chunks.putIfAbsent(new Pair<>(chunk.getX(), chunk.getZ()), chunk);
public void setChunk(int x, int z, ChunkSnapshot chunk) {
chunks.put(new Pair<>(x, z), chunk);
}
/**
@ -81,6 +82,21 @@ public class BoxedChunkGenerator extends ChunkGenerator {
}
// Copy the chunk
ChunkSnapshot chunk = chunks.get(coords);
copyChunkVerbatim(cd, chunk, minY, height);
}
private void copyChunkVerbatim(ChunkData cd, ChunkSnapshot chunk, int minY, int height) {
for (int x = 0; x < 16; x ++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < height; y++) {
cd.setBlock(x, y, z, chunk.getBlockData(x, y, z));
}
}
}
}
private void copyChunk(ChunkData cd, ChunkSnapshot chunk, int minY, int height) {
for (int x = 0; x < 16; x ++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < height; y++) {
@ -185,17 +201,20 @@ public class BoxedChunkGenerator extends ChunkGenerator {
@Override
public boolean shouldGenerateSurface() {
return this.addon.getSettings().isGenerateSurface();
return false;
}
@Override
public boolean shouldGenerateCaves() {
return this.addon.getSettings().isGenerateCaves();
return false;
//return this.addon.getSettings().isGenerateCaves();
}
@Override
public boolean shouldGenerateDecorations() {
return this.addon.getSettings().isGenerateDecorations();
return false;
//return this.addon.getSettings().isGenerateDecorations();
}
@Override
@ -205,7 +224,8 @@ public class BoxedChunkGenerator extends ChunkGenerator {
@Override
public boolean shouldGenerateStructures() {
return this.addon.getSettings().isAllowStructures();
return false;
//return this.addon.getSettings().isAllowStructures();
}
}

View File

@ -1,13 +1,33 @@
package world.bentobox.boxed.generators;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo;
import world.bentobox.boxed.Boxed;
/**
* Generates the seed world chunks
* @author tastybento
*
*/
public class BoxedSeedChunkGenerator extends ChunkGenerator {
BiomeProvider seedBiomeProvider;
/**
* @param seedBiomeProvider
*/
public BoxedSeedChunkGenerator(Boxed boxed) {
this.seedBiomeProvider = new SeedBiomeGenerator(boxed);
}
/*
@Override
public BiomeProvider getDefaultBiomeProvider(WorldInfo worldInfo) {
return seedBiomeProvider;
}
*/
@Override
public boolean shouldGenerateNoise() {
return true;
@ -16,6 +36,7 @@ public class BoxedSeedChunkGenerator extends ChunkGenerator {
@Override
public boolean shouldGenerateSurface() {
return true;
// return this.addon.getSettings().isGenerateSurface();
}
@Override

View File

@ -0,0 +1,18 @@
package world.bentobox.boxed.generators;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import world.bentobox.boxed.Boxed;
/**
* @author tastybento
*
*/
public class SeedBiomeGenerator extends AbstractSeendBiomeProvider {
public SeedBiomeGenerator(Boxed boxed) {
super(boxed, Environment.NORMAL, Biome.OCEAN);
}
}

View File

@ -1,17 +1,26 @@
package world.bentobox.boxed.listeners;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import world.bentobox.bentobox.api.events.island.IslandCreatedEvent;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import world.bentobox.boxed.Boxed;
@ -22,7 +31,11 @@ import world.bentobox.boxed.Boxed;
public class NewAreaListener implements Listener {
private final Boxed addon;
private final YamlConfiguration config;
private File structureFile;
private Queue<Item> itemsToBuild = new LinkedList<>();
private boolean pasting;
private record Item(World w, List<Pair<Integer, Integer>> cs, String cmd) {};
/**
* @param addon addon
@ -30,32 +43,48 @@ public class NewAreaListener implements Listener {
public NewAreaListener(Boxed addon) {
this.addon = addon;
// Load the config
File structureFile = new File(addon.getDataFolder(), "structures.yml");
structureFile = new File(addon.getDataFolder(), "structures.yml");
// Check if it exists and if not, save it from the jar
if (!structureFile.exists()) {
addon.saveResource("structures.yml", true);
}
config = YamlConfiguration.loadConfiguration(structureFile);
// Try to build something every 10 seconds
Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> BuildItem(), 20, 200);
}
private void BuildItem() {
// Only kick off a build if there is something to build and something isn't already being built
if (!pasting && !itemsToBuild.isEmpty()) {
// Build item
Item item = itemsToBuild.poll();
LoadChunksAsync(item.w, item.cs, 0, item.cmd);
}
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIslandCreated(IslandCreatedEvent e) {
addon.log(e.getEventName());
// Load the latest config so that admins can change it on the fly without reloading
YamlConfiguration config = YamlConfiguration.loadConfiguration(structureFile);
//addon.log(e.getEventName());
Island island = e.getIsland();
addon.getPlugin().getIWM().getAddon(island.getWorld()).ifPresent(gma -> addon.log(gma.getDescription().getName()));
// Check if this island is in this game
/*
if (!addon.getPlugin().getIWM().getAddon(island.getWorld()).map(gma -> gma.equals(addon)).orElse(false)) {
// Not correct addon
return;
}*/
Location center = island.getProtectionCenter();
ConfigurationSection section = config.getConfigurationSection("structures.new");
if (section == null) {
addon.logError("structures.new not found");
return;
addon.log("structures.new not found");
} else {
place("structure",section, center);
}
}
private void place(String string, ConfigurationSection section, Location center) {
// Loop through the structures in the file - there could be more than one
for (String structure : section.getKeys(false)) {
addon.log(structure);
String value = section.getString(structure,"");
@ -63,29 +92,47 @@ public class NewAreaListener implements Listener {
String[] coords = value.split(",");
if (coords.length == 3) {
int x = Integer.valueOf(coords[0]) + center.getBlockX();
int y = Integer.valueOf(coords[1]) + center.getBlockY();
int y = Integer.valueOf(coords[1]);
int z = Integer.valueOf(coords[2]) + center.getBlockZ();
Util.getChunkAtAsync(center.getWorld(), x >> 4, z >> 4, true).thenAccept(c -> {
// Run command
String cmd = "place structure minecraft:" + structure + " "
+ x + " "
+ y + " "
+ z + " ";
Bukkit.getScheduler().runTaskLater(addon.getPlugin(), () -> {
addon.log("run command " + cmd);
addon.getServer().dispatchCommand(Bukkit.getConsoleSender(), cmd);
}, 1000);
});
List<Pair<Integer, Integer>> cs = new ArrayList<>();
int size = 10;
for (int cx = (x >> 4) - size; cx < (x >>4) + size; cx++) {
for (int cz = (z >> 4) - size; cz < (z >>4) + size; cz++) {
cs.add(new Pair<>(cx, cz));
}
}
// Make command
String cmd = "execute in " + center.getWorld().getName() + " run place "+ string + " minecraft:" + structure + " "
+ x + " "
+ y + " "
+ z + " ";
itemsToBuild.add(new Item(center.getWorld(), cs, cmd));
} else {
addon.logError("Structure file syntax error: " + structure + " " + value);
}
}
}
private void LoadChunksAsync(World w, List<Pair<Integer, Integer>> cs, int i, String cmd) {
pasting = true;
int total = cs.size();
//addon.log("Loading chunk async " + i);
if (i < total) {
Util.getChunkAtAsync(w, cs.get(i).x, cs.get(i).z, true).thenAccept(c -> {
//addon.log("Loaded chunk " + c.getX() + " " + c.getZ());
LoadChunksAsync(w, cs, i + 1, cmd);
});
} else {
addon.log("Complete");
addon.log("run command " + cmd);
Bukkit.getScheduler().runTaskLater(addon.getPlugin(), () -> {
addon.log("Comand success = " + addon.getServer().dispatchCommand(Bukkit.getConsoleSender(), cmd));
pasting = false;
}, 20L);
}
}
}

View File

@ -1,3 +1,6 @@
structures:
new-island:
"minecraft:village": 100,0,100
new:
village_plains: "0,64,80"
ruined_portal_mountain: "-38,63,20"
pillager_outpost: "3,63,-60"