add import/load command

This commit is contained in:
Sen 2025-06-28 19:36:55 +02:00
parent 96fb940087
commit 614e859146
Signed by: sen
GPG key ID: 3AC50A6F47D1B722
9 changed files with 178 additions and 32 deletions

View file

@ -10,9 +10,11 @@ import common.log.Log;
import common.rng.PerlinGen;
import common.rng.Random;
import common.util.BlockPos;
import common.util.Displayable;
import common.util.ExtMath;
import common.util.Identifyable;
public enum Biome {
public enum Biome implements Identifyable, Displayable {
NONE(0, "none", "<Keins>", 0x000000),
PLAINS(1, "plains", "Ebene", 0x8db360, 12.0f, 40.0f),
DESERT(2, "desert", "Wüste", 0xfa9418, 60.0f, 0.0f),
@ -232,4 +234,12 @@ public enum Biome {
double d1 = (double)ExtMath.clampf(this.humidity * 0.01f, 0.0F, 1.0F);
return Colorizer.getFoliageColor(d0, d1);
}
public String getName() {
return this.name;
}
public String getDisplay() {
return this.display;
}
}

View file

@ -92,6 +92,7 @@ public abstract class UniverseRegistry {
Dimension dim = Dimension.getByTag(list.get(z));
if(!REGISTRY.containsKey(dim.getDimensionId()) && !ALIASES.containsKey(dim.getDimensionName()))
register(dim);
nextDimId = dim.getDimensionId() + 1 > nextDimId ? dim.getDimensionId() + 1 : nextDimId;
}
list = tag.getList("Names");
@ -253,6 +254,7 @@ public abstract class UniverseRegistry {
public static Planet registerPlanet(String star, String name, String custom, int sky, int fog, int clouds, long orbit, long rotation,
float offset, float gravity, float temperature, int brightness) {
star = star == null ? new Random().pick(Lists.newArrayList(STAR_MAP.keySet())).getDimensionName() : star;
if(!ALIASES.containsKey(star) || ALIASES.get(star).getType() != DimType.STAR || ALIASES.containsKey(name))
return null;
Planet dim = new Planet(nextDimId++, name, sky, fog, clouds, orbit, rotation, offset, gravity, temperature, brightness);

View file

@ -103,7 +103,6 @@ import server.network.Player;
import server.network.User;
import server.vars.SVar;
import server.vars.SVars;
import server.world.Converter;
import server.world.Region;
import server.world.WorldServer;
@ -405,7 +404,6 @@ public final class Server implements IThreadListener, Executor {
public void run(long time) {
Region.loadMap();
Converter.convert("terra");
long wtime = this.loadServerConfig();
if(this.keyPair == null) {
Log.SYSTEM.info("Generiere neues Schlüsselpaar");

View file

@ -6,6 +6,10 @@ import java.util.List;
import common.collect.Lists;
public abstract class ArgumentParser {
public static String escape(String str) {
return str.replace("\\", "\\\\").replace(" ", "\\ ").replace("\"", "\\\"").replace(";", "\\;");
}
public static String[][] splitString(String str) {
// if(str.isEmpty()) {
// return new String[0];

View file

@ -120,6 +120,22 @@ public abstract class Command implements Executable {
return this.addParameter(new IntParser(name, false, def, min, max, def));
}
protected Command addLong(String name, char shortName, long min, long max, long def) {
return this.addParameter(shortName, new LongParser(name, def, min, max, def));
}
protected Command addLong(String name, long min, long max, long def) {
return this.addParameter(new LongParser(name, def, min, max, def));
}
protected Command addColor(String name, char shortName, int def) {
return this.addParameter(shortName, new ColorParser(name, def));
}
protected Command addColor(String name, int def) {
return this.addParameter(new ColorParser(name, def));
}
protected Command addDouble(String name, char shortName, double min, double max, double def) {
return this.addParameter(shortName, new DoubleParser(name, def, min, max, false, def));
}
@ -240,6 +256,14 @@ public abstract class Command implements Executable {
return this.addParameter(new StringParser(name, null, allowEmpty, null, null, null, completions));
}
protected Command addString(String name, char shortName, boolean allowEmpty, Object ... completions) {
return this.addParameter(shortName, new StringParser(name, null, allowEmpty, null, null, null, completions));
}
protected Command addString(String name, char shortName, boolean allowEmpty, StringCompleter completer) {
return this.addParameter(shortName, new StringParser(name, null, allowEmpty, null, null, null, completer));
}
protected Command addString(String name, boolean allowEmpty, StringCompleter completer) {
return this.addParameter(new StringParser(name, null, allowEmpty, null, null, null, completer));
}

View file

@ -270,5 +270,6 @@ public class CommandEnvironment {
this.registerExecutable(new CommandMore());
this.registerExecutable(new CommandReturn());
this.registerExecutable(new CommandDeathspot());
this.registerExecutable(new CommandLoad());
}
}

View file

@ -0,0 +1,96 @@
package server.command.commands;
import java.io.File;
import java.util.Arrays;
import common.biome.Biome;
import common.collect.Iterables;
import common.collect.Lists;
import common.color.TextColor;
import common.dimension.Planet;
import common.dimension.Star;
import common.init.UniverseRegistry;
import server.command.ArgumentParser;
import server.command.Command;
import server.command.CommandEnvironment;
import server.command.Executor;
import server.command.RunException;
import server.network.Player;
import server.world.Converter;
public class CommandLoad extends Command {
private static final File IMPORTS_DIR = new File("import");
private String loadingDim;
public CommandLoad() {
super("load");
this.addString("name", false, null, null, ch -> (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_');
this.addString("display", false);
this.setParamsOptional();
this.addString("star", false);
this.addEnum("biome", Biome.DEF_BIOME, Biome.class, Biome.values());
this.addString("convert", 'c', false, (env, last) -> {
File[] list = IMPORTS_DIR.listFiles(file -> file.isDirectory());
if(list != null)
Arrays.sort(list);
return list == null ? Lists.newArrayList() : Lists.newArrayList(Iterables.transform(Lists.newArrayList(list), file -> ArgumentParser.escape(file.getName())));
});
this.addFlag("teleport", 't');
this.addColor("sky", 's', 0xffffffff);
this.addColor("fog", 'f', 0xc0d8ff);
this.addColor("clouds", 'w', 0xffffff);
this.addLong("orbit", 'o', 1L, 1000000000000L, UniverseRegistry.EARTH_YEAR);
this.addLong("rotation", 'r', 1L, 1000000000000L, 24000L);
this.addDouble("offset", 'm', 0.0, 100000.0, 0.0);
this.addDouble("gravity", 'g', 0.0, 100000.0, 9.81);
this.addDouble("temperature", 'k', 0.0, 100000.0, 259.15);
this.addInt("brightness", 'b', 0, 15, 0);
}
public void exec(CommandEnvironment env, Executor exec, String name, String display, String star, Biome biome, String convert, boolean teleport, int sky, int fog, int clouds,
long orbit, long rotation, double offset, double gravity, double temperature, int brightness) {
if(teleport && !exec.isPlayer())
throw new RunException("Nur Spieler können teleportiert werden");
if(UniverseRegistry.getDimension(name) != null)
throw new RunException("Dimension '%s' existiert bereits", name);
if(name.equals(this.loadingDim))
throw new RunException("Dimension '%s' wird gerade geladen", name);
if(star != null && !(UniverseRegistry.getDimension(star) instanceof Star))
throw new RunException("'%s' ist kein Stern", star);
if(convert != null) {
File dir = new File(IMPORTS_DIR, convert);
if(!IMPORTS_DIR.equals(dir.getParentFile()))
throw new RunException("Welt %s muss ein direkter Unterordner von %s/ sein", convert, IMPORTS_DIR);
if(!dir.exists()) {
IMPORTS_DIR.mkdirs();
throw new RunException("Welt %s existiert nicht", dir);
}
this.loadingDim = name;
if(Converter.convert(dir, name, pos -> env.getServer().schedule(() -> {
Planet planet = UniverseRegistry.registerPlanet(star, name, display, sky, fog, clouds, orbit, rotation, (float)offset, (float)gravity, (float)temperature, brightness);
planet.setBiome(biome);
this.loadingDim = null;
exec.log(TextColor.GREEN + "Welt %s wurde erfolgreich in Dimension '%s' konvertiert", dir, display);
if(teleport && pos != null) {
Player player = env.getServer().getPlayer(((Player)exec).getUser());
if(player != null && player.getPresentEntity() != null)
player.getPresentEntity().teleport(CommandWorld.adjust(env.getServer().getWorld(planet.getDimensionId()), pos), planet.getDimensionId());
}
})) == null) {
this.loadingDim = null;
throw new RunException("Welt %s konnte nicht konvertiert werden", dir);
}
else {
exec.log(TextColor.ACID + "Welt %s wird in Dimension '%s' konvertiert ...", dir, display);
}
return;
}
Planet planet = UniverseRegistry.registerPlanet(star, name, display, sky, fog, clouds, orbit, rotation, (float)offset, (float)gravity, (float)temperature, brightness);
planet.setBiome(biome);
exec.log(TextColor.GREEN + "Dimension '%s' wurde registriert", display);
if(teleport && ((Player)exec).getPresentEntity() != null)
((Player)exec).getPresentEntity().teleport(CommandWorld.adjust(env.getServer().getWorld(planet.getDimensionId()), ((Player)exec).getPresentEntity().getPosition()), planet.getDimensionId());
}
}

View file

@ -21,20 +21,24 @@ public class CommandWorld extends Command {
public Object exec(CommandEnvironment env, Executor exec, WorldServer world, List<Entity> entities) {
for(Entity entity : entities) {
BlockPos pos = entity.getPosition();
pos = pos.getY() < 0 ? new BlockPos(pos.getX(), 0, pos.getZ()) : pos;
if(world.getState(pos).getBlock().getMaterial().blocksMovement() || world.getState(pos).getBlock().getMaterial().isLiquid()) {
while((world.getState(pos).getBlock().getMaterial().blocksMovement() || world.getState(pos).getBlock().getMaterial().isLiquid()) && pos.getY() < 511)
pos = pos.up();
}
else {
while(!(world.getState(pos).getBlock().getMaterial().blocksMovement() || world.getState(pos).getBlock().getMaterial().isLiquid()) && pos.getY() > 0)
pos = pos.down();
pos = pos.up();
}
BlockPos pos = adjust(world, entity.getPosition());
entity.teleport(pos, world.dimension.getDimensionId());
exec.log("%s nach %d, %d, %d in %s teleportiert", entity.getCommandName(), pos.getX(), pos.getY(), pos.getZ(), world.dimension.getFormattedName(false));
}
return entities.size();
}
public static BlockPos adjust(WorldServer world, BlockPos pos) {
pos = pos.getY() < 0 ? new BlockPos(pos.getX(), 0, pos.getZ()) : pos;
if(world.getState(pos).getBlock().getMaterial().blocksMovement() || world.getState(pos).getBlock().getMaterial().isLiquid()) {
while((world.getState(pos).getBlock().getMaterial().blocksMovement() || world.getState(pos).getBlock().getMaterial().isLiquid()) && pos.getY() < 511)
pos = pos.up();
}
else {
while(!(world.getState(pos).getBlock().getMaterial().blocksMovement() || world.getState(pos).getBlock().getMaterial().isLiquid()) && pos.getY() > 0)
pos = pos.down();
pos = pos.up();
}
return pos;
}
}

View file

@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
@ -2034,12 +2035,12 @@ public abstract class Converter {
return start;
}
public static BlockPos convert(String dest) {
if(new File("server.cdt").exists())
public static BlockPos convert(File dir, String dest, Consumer<BlockPos> finish) {
if(new File(dir, "server.cdt").exists())
return null;
File ldat = new File("level.dat");
File ldat = new File(dir, "level.dat");
if(!ldat.exists())
ldat = new File("level.dat_old");
ldat = new File(dir, "level.dat_old");
if(!ldat.exists())
return null;
Log.IO.info("Welt wird konvertiert ...");
@ -2073,7 +2074,7 @@ public abstract class Converter {
Log.IO.warn("Konvertiere keine Chunk-Daten, da Version zu neu (%s)", ver);
return null;
}
File regionDir = new File("region");
File regionDir = new File(dir, "region");
if(!regionDir.exists()) {
Log.IO.info("Kein Ordner region/ gefunden");
return null;
@ -2093,19 +2094,25 @@ public abstract class Converter {
if(ver == SaveVersion.RELEASE_1_9)
Log.IO.warn("Konvertiere von neuerer Version, dies wird Blöcke entfernen ...");
chunkDir.mkdirs();
int progress = 0;
long time = System.currentTimeMillis();
long start = postProgress(time, 0);
for(File file : files) {
int percent = (int)Math.round(100.0D * (double)progress / (double)files.length);
Log.IO.info("Konvertiere Chunk-Daten: " + file.getName() + " (" + progress + "/" + files.length + ")");
start = convertChunks(chunkDir, file, start, progress, files.length);
++progress;
start = postProgress(start, percent);
}
time = System.currentTimeMillis() - time;
Log.IO.info("Fertig. Konversion dauerte " + ((time / 60000L) > 0 ? ((time / 60000L) + " Minuten und ") : "") + ((time / 1000L) % 60L) + " Sekunden.");
BlockPos pos = new BlockPos(tag.getInt("SpawnX"), tag.getInt("SpawnY"), tag.getInt("SpawnZ"));
new Thread(new Runnable() {
public void run() {
int progress = 0;
long time = System.currentTimeMillis();
long start = postProgress(time, 0);
for(File file : files) {
int percent = (int)Math.round(100.0D * (double)progress / (double)files.length);
Log.IO.info("Konvertiere Chunk-Daten: " + file.getName() + " (" + progress + "/" + files.length + ")");
start = convertChunks(chunkDir, file, start, progress, files.length);
++progress;
start = postProgress(start, percent);
}
time = System.currentTimeMillis() - time;
Log.IO.info("Fertig. Konversion dauerte " + ((time / 60000L) > 0 ? ((time / 60000L) + " Minuten und ") : "") + ((time / 1000L) % 60L) + " Sekunden.");
finish.accept(pos);
}
}, "Chunk converter " + dest + " thread").start();
Log.IO.info("Einstiegspunkt: /tele %d %d %d %s", tag.getInt("SpawnX"), tag.getInt("SpawnY"), tag.getInt("SpawnZ"), dest);
return new BlockPos(tag.getInt("SpawnX"), tag.getInt("SpawnY"), tag.getInt("SpawnZ"));
return pos;
}
}