Load structure scripts into registry

This commit is contained in:
dfsek
2020-12-23 02:35:07 -07:00
parent e9dc7428b8
commit 405a96034c
13 changed files with 117 additions and 52 deletions

View File

@@ -50,6 +50,7 @@ import java.util.Map;
public class Parser { public class Parser {
private final String data; private final String data;
private final Map<String, FunctionBuilder<? extends Function<?>>> functions = new HashMap<>(); private final Map<String, FunctionBuilder<? extends Function<?>>> functions = new HashMap<>();
private String id;
public Parser(String data) { public Parser(String data) {
this.data = data; this.data = data;
@@ -60,6 +61,10 @@ public class Parser {
return this; return this;
} }
public String getID() {
return id;
}
/** /**
* Parse input * Parse input
* *
@@ -75,6 +80,14 @@ public class Parser {
} catch(TokenizerException e) { } catch(TokenizerException e) {
throw new ParseException("Failed to tokenize input", e); throw new ParseException("Failed to tokenize input", e);
} }
// Parse ID
ParserUtil.checkType(tokens.remove(0), Token.Type.ID); // First token must be ID
Token idToken = tokens.get(0);
ParserUtil.checkType(tokens.remove(0), Token.Type.STRING); // Second token must be string literal containing ID
ParserUtil.checkType(tokens.remove(0), Token.Type.STATEMENT_END);
this.id = idToken.getContent();
// Check for dangling brackets // Check for dangling brackets
int blockLevel = 0; int blockLevel = 0;
for(Token t : tokens) { for(Token t : tokens) {

View File

@@ -1,4 +1,50 @@
package com.dfsek.terra.api.structures.script; package com.dfsek.terra.api.structures.script;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.structures.parser.Parser;
import com.dfsek.terra.api.structures.parser.exceptions.ParseException;
import com.dfsek.terra.api.structures.parser.lang.Block;
import com.dfsek.terra.api.structures.script.builders.BlockFunctionBuilder;
import com.dfsek.terra.api.structures.script.builders.CheckFunctionBuilder;
import com.dfsek.terra.api.structures.structure.Rotation;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
public class StructureScript { public class StructureScript {
private final Block block;
private final String id;
public StructureScript(InputStream inputStream, TerraPlugin main) {
Parser parser;
try {
parser = new Parser(IOUtils.toString(inputStream));
} catch(IOException e) {
throw new RuntimeException(e);
}
parser.addFunction("block", new BlockFunctionBuilder(main))
.addFunction("check", new CheckFunctionBuilder(main));
try {
block = parser.parse();
} catch(ParseException e) {
throw new RuntimeException(e);
}
this.id = parser.getID();
}
public void execute(Location location, Rotation rotation) {
block.apply(location, rotation);
}
public void execute(Location location, Chunk chunk, Rotation rotation) {
block.apply(location, chunk, rotation);
}
public String getId() {
return id;
}
} }

View File

@@ -206,6 +206,10 @@ public class Token {
/** /**
* Break statement * Break statement
*/ */
BREAK BREAK,
/**
* ID declaration
*/
ID
} }
} }

View File

@@ -141,6 +141,9 @@ public class Tokenizer {
if(tokenString.equals("break")) if(tokenString.equals("break"))
return new Token(tokenString, Token.Type.BREAK, new Position(reader.getLine(), reader.getIndex())); return new Token(tokenString, Token.Type.BREAK, new Position(reader.getLine(), reader.getIndex()));
if(tokenString.equals("id"))
return new Token(tokenString, Token.Type.ID, new Position(reader.getLine(), reader.getIndex()));
return new Token(tokenString, Token.Type.IDENTIFIER, new Position(reader.getLine(), reader.getIndex())); return new Token(tokenString, Token.Type.IDENTIFIER, new Position(reader.getLine(), reader.getIndex()));
} }

View File

@@ -8,6 +8,7 @@ import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.LoaderRegistrar; import com.dfsek.terra.api.LoaderRegistrar;
import com.dfsek.terra.api.loot.LootTable; import com.dfsek.terra.api.loot.LootTable;
import com.dfsek.terra.api.platform.TerraPlugin; import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.flora.Flora; import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.Palette;
@@ -44,6 +45,7 @@ import com.dfsek.terra.registry.CarverRegistry;
import com.dfsek.terra.registry.FloraRegistry; import com.dfsek.terra.registry.FloraRegistry;
import com.dfsek.terra.registry.OreRegistry; import com.dfsek.terra.registry.OreRegistry;
import com.dfsek.terra.registry.PaletteRegistry; import com.dfsek.terra.registry.PaletteRegistry;
import com.dfsek.terra.registry.ScriptRegistry;
import com.dfsek.terra.registry.StructureRegistry; import com.dfsek.terra.registry.StructureRegistry;
import com.dfsek.terra.registry.TerraRegistry; import com.dfsek.terra.registry.TerraRegistry;
import com.dfsek.terra.registry.TreeRegistry; import com.dfsek.terra.registry.TreeRegistry;
@@ -78,6 +80,7 @@ public class ConfigPack implements LoaderRegistrar {
private final FloraRegistry floraRegistry; private final FloraRegistry floraRegistry;
private final OreRegistry oreRegistry = new OreRegistry(); private final OreRegistry oreRegistry = new OreRegistry();
private final TreeRegistry treeRegistry; private final TreeRegistry treeRegistry;
private final ScriptRegistry scriptRegistry = new ScriptRegistry();
private final AbstractConfigLoader abstractConfigLoader = new AbstractConfigLoader(); private final AbstractConfigLoader abstractConfigLoader = new AbstractConfigLoader();
private final ConfigLoader selfLoader = new ConfigLoader(); private final ConfigLoader selfLoader = new ConfigLoader();
@@ -141,13 +144,19 @@ public class ConfigPack implements LoaderRegistrar {
abstractConfigLoader abstractConfigLoader
.registerLoader(LootTable.class, new LootTableLoader(loader, main)); // These loaders need access to the Loader instance to get files. .registerLoader(LootTable.class, new LootTableLoader(loader, main)); // These loaders need access to the Loader instance to get files.
loader loader
.open("palettes").then(streams -> buildAll(new PaletteFactory(), paletteRegistry, abstractConfigLoader.load(streams, PaletteTemplate::new), main)).close() .open("palettes", ".yml").then(streams -> buildAll(new PaletteFactory(), paletteRegistry, abstractConfigLoader.load(streams, PaletteTemplate::new), main)).close()
.open("palettes").thenNames(names -> names.forEach(System.out::println)).close() .open("ores", ".yml").then(streams -> buildAll(new OreFactory(), oreRegistry, abstractConfigLoader.load(streams, OreTemplate::new), main)).close()
.open("ores").then(streams -> buildAll(new OreFactory(), oreRegistry, abstractConfigLoader.load(streams, OreTemplate::new), main)).close() .open("flora", ".yml").then(streams -> buildAll(new FloraFactory(), floraRegistry, abstractConfigLoader.load(streams, FloraTemplate::new), main)).close()
.open("flora").then(streams -> buildAll(new FloraFactory(), floraRegistry, abstractConfigLoader.load(streams, FloraTemplate::new), main)).close() .open("carving", ".yml").then(streams -> buildAll(new CarverFactory(this), carverRegistry, abstractConfigLoader.load(streams, CarverTemplate::new), main)).close()
.open("carving").then(streams -> buildAll(new CarverFactory(this), carverRegistry, abstractConfigLoader.load(streams, CarverTemplate::new), main)).close() .open("biomes", ".yml").then(streams -> buildAll(new BiomeFactory(this), biomeRegistry, abstractConfigLoader.load(streams, () -> new BiomeTemplate(this, main)), main)).close()
.open("biomes").then(streams -> buildAll(new BiomeFactory(this), biomeRegistry, abstractConfigLoader.load(streams, () -> new BiomeTemplate(this, main)), main)).close() .open("grids", ".yml").then(streams -> buildAll(new BiomeGridFactory(), biomeGridRegistry, abstractConfigLoader.load(streams, BiomeGridTemplate::new), main)).close();
.open("grids").then(streams -> buildAll(new BiomeGridFactory(), biomeGridRegistry, abstractConfigLoader.load(streams, BiomeGridTemplate::new), main)).close();
loader.open("structures/data", ".tesf").then(streams -> streams.forEach(stream -> {
StructureScript structureScript = new StructureScript(stream, main);
scriptRegistry.add(structureScript.getId(), structureScript);
})).close();
for(UserDefinedBiome b : biomeRegistry.entries()) { for(UserDefinedBiome b : biomeRegistry.entries()) {
try { try {
Objects.requireNonNull(b.getErode()); // Throws NPE if it cannot load erosion biomes. Objects.requireNonNull(b.getErode()); // Throws NPE if it cannot load erosion biomes.
@@ -231,6 +240,10 @@ public class ConfigPack implements LoaderRegistrar {
.registerLoader(Tree.class, treeRegistry); .registerLoader(Tree.class, treeRegistry);
} }
public ScriptRegistry getScriptRegistry() {
return scriptRegistry;
}
public BiomeRegistry getBiomeRegistry() { public BiomeRegistry getBiomeRegistry() {
return biomeRegistry; return biomeRegistry;
} }

View File

@@ -25,11 +25,11 @@ public class FolderLoader extends Loader {
} }
@Override @Override
protected void load(String directory) { protected void load(String directory, String extension) {
File newPath = new File(path.toFile(), directory); File newPath = new File(path.toFile(), directory);
newPath.mkdirs(); newPath.mkdirs();
try(Stream<Path> paths = Files.walk(newPath.toPath())) { try(Stream<Path> paths = Files.walk(newPath.toPath())) {
paths.filter(Files::isRegularFile).filter(file -> file.toString().toLowerCase().endsWith(".yml")).forEach(file -> { paths.filter(Files::isRegularFile).filter(file -> file.toString().toLowerCase().endsWith(extension)).forEach(file -> {
try { try {
streams.put(newPath.toURI().relativize(file.toUri()).getPath(), new FileInputStream(file.toFile())); streams.put(newPath.toURI().relativize(file.toUri()).getPath(), new FileInputStream(file.toFile()));
} catch(FileNotFoundException e) { } catch(FileNotFoundException e) {

View File

@@ -45,14 +45,15 @@ public abstract class Loader {
* Open a subdirectory. * Open a subdirectory.
* *
* @param directory Directory to open * @param directory Directory to open
* @param extension
*/ */
public Loader open(String directory) { public Loader open(String directory, String extension) {
if(streams.size() != 0) throw new IllegalStateException("Attempted to load new directory before closing existing InputStreams"); if(streams.size() != 0) throw new IllegalStateException("Attempted to load new directory before closing existing InputStreams");
load(directory); load(directory, extension);
return this; return this;
} }
protected abstract void load(String directory); protected abstract void load(String directory, String extension);
/** /**
* Close all InputStreams opened. * Close all InputStreams opened.

View File

@@ -24,11 +24,11 @@ public class ZIPLoader extends Loader {
} }
@Override @Override
protected void load(String directory) { protected void load(String directory, String extension) {
Enumeration<? extends ZipEntry> entries = file.entries(); Enumeration<? extends ZipEntry> entries = file.entries();
while(entries.hasMoreElements()) { while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement(); ZipEntry entry = entries.nextElement();
if(!entry.isDirectory() && entry.getName().startsWith(directory) && entry.getName().endsWith(".yml")) { if(!entry.isDirectory() && entry.getName().startsWith(directory) && entry.getName().endsWith(extension)) {
try { try {
streams.put(entry.getName(), file.getInputStream(entry)); streams.put(entry.getName(), file.getInputStream(entry));
} catch(IOException e) { } catch(IOException e) {

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.registry;
import com.dfsek.terra.api.structures.script.StructureScript;
public class ScriptRegistry extends TerraRegistry<StructureScript> {
}

View File

@@ -1,3 +1,5 @@
id "testScript";
test("minecraft:green_w" + "ool", (2 * (3+1) * (2 * (1+1)))); test("minecraft:green_w" + "ool", (2 * (3+1) * (2 * (1+1))));
// //

View File

@@ -1,5 +1,7 @@
package com.dfsek.terra.bukkit.command.command.structure.load; package com.dfsek.terra.bukkit.command.command.structure.load;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.bukkit.command.DebugCommand; import com.dfsek.terra.bukkit.command.DebugCommand;
import com.dfsek.terra.bukkit.command.PlayerCommand; import com.dfsek.terra.bukkit.command.PlayerCommand;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@@ -7,9 +9,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FilenameFilter;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -20,17 +19,14 @@ public class LoadCommand extends PlayerCommand implements DebugCommand {
super(parent); super(parent);
} }
public List<String> getStructureNames() { public List<String> getStructureNames(World world) {
List<String> names = new ArrayList<>(); List<String> names = new ArrayList<>();
File structureDir = new File(getMain().getDataFolder() + File.separator + "export" + File.separator + "structures"); TerraWorld terraWorld = getMain().getWorld(world);
if(!structureDir.exists()) return Collections.emptyList();
Path structurePath = structureDir.toPath(); terraWorld.getConfig().getScriptRegistry().forEach(script -> {
names.add(script.getId());
});
FilenameFilter filter = (dir, name) -> name.endsWith(".tstructure");
for(File f : structureDir.listFiles(filter)) {
String path = structurePath.relativize(f.toPath()).toString();
names.add(path.substring(0, path.length() - 11));
}
return names; return names;
} }

View File

@@ -62,7 +62,7 @@ public class LoadFullCommand extends LoadCommand implements DebugCommand {
public List<String> getTabCompletions(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] args) { public List<String> getTabCompletions(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] args) {
switch(args.length) { switch(args.length) {
case 1: case 1:
return getStructureNames().stream().filter(string -> string.toUpperCase().startsWith(args[0].toUpperCase())).collect(Collectors.toList()); return Collections.emptyList(); //getStructureNames().stream().filter(string -> string.toUpperCase().startsWith(args[0].toUpperCase())).collect(Collectors.toList());
case 2: case 2:
return Stream.of("0", "90", "180", "270").filter(string -> string.toUpperCase().startsWith(args[1].toUpperCase())).collect(Collectors.toList()); return Stream.of("0", "90", "180", "270").filter(string -> string.toUpperCase().startsWith(args[1].toUpperCase())).collect(Collectors.toList());
} }

View File

@@ -1,24 +1,16 @@
package com.dfsek.terra.bukkit.command.command.structure.load; package com.dfsek.terra.bukkit.command.command.structure.load;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.api.math.vector.Location; import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.structures.parser.Parser;
import com.dfsek.terra.api.structures.parser.exceptions.ParseException;
import com.dfsek.terra.api.structures.parser.lang.Block;
import com.dfsek.terra.api.structures.script.builders.BlockFunctionBuilder;
import com.dfsek.terra.api.structures.script.builders.CheckFunctionBuilder;
import com.dfsek.terra.api.structures.structure.Rotation; import com.dfsek.terra.api.structures.structure.Rotation;
import com.dfsek.terra.bukkit.BukkitWorld; import com.dfsek.terra.bukkit.BukkitWorld;
import com.dfsek.terra.bukkit.command.DebugCommand; import com.dfsek.terra.bukkit.command.DebugCommand;
import org.apache.commons.io.IOUtils;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@@ -37,24 +29,13 @@ public class LoadRawCommand extends LoadCommand implements DebugCommand {
} else sign.setLine(2, data); } else sign.setLine(2, data);
} }
@Override @Override
public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String s, @NotNull String[] args) {
try { TerraWorld terraWorld = getMain().getWorld(new BukkitWorld(sender.getWorld()));
Parser parser = new Parser(IOUtils.toString(new FileInputStream(new File(getMain().getDataFolder(), "test.tesf"))));
parser.addFunction("block", new BlockFunctionBuilder(getMain()))
.addFunction("check", new CheckFunctionBuilder(getMain()));
System.out.println("Parsing..."); terraWorld.getConfig().getScriptRegistry().get(args[0]).execute(new Location(new BukkitWorld(sender.getWorld()), sender.getLocation().getX(), sender.getLocation().getY(), sender.getLocation().getZ()), Rotation.fromDegrees(90 * ThreadLocalRandom.current().nextInt(4)));
Block main = parser.parse();
System.out.println("Done parsing");
main.apply(new Location(new BukkitWorld(sender.getWorld()), sender.getLocation().getX(), sender.getLocation().getY(), sender.getLocation().getZ()), Rotation.fromDegrees(90 * ThreadLocalRandom.current().nextInt(4)));
} catch(IOException | ParseException e) {
e.printStackTrace();
}
/* /*
try { try {
WorldHandle handle = ((TerraBukkitPlugin) getMain()).getWorldHandle(); WorldHandle handle = ((TerraBukkitPlugin) getMain()).getWorldHandle();
@@ -130,7 +111,7 @@ public class LoadRawCommand extends LoadCommand implements DebugCommand {
@Override @Override
public List<String> getTabCompletions(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] args) { public List<String> getTabCompletions(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] args) {
if(args.length == 1) { if(args.length == 1) {
return getStructureNames().stream().filter(string -> string.toUpperCase().startsWith(args[0].toUpperCase())).collect(Collectors.toList()); return getStructureNames(new BukkitWorld(((Player) commandSender).getWorld())).stream().filter(string -> string.toUpperCase().startsWith(args[0].toUpperCase())).collect(Collectors.toList());
} }
return Collections.emptyList(); return Collections.emptyList();
} }