mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2025-07-03 08:25:31 +00:00
Merge pull request #80 from PolyhedralDev/ver/5.0.0
Addon loader, fleshed out addon API, and Fabric finalization
This commit is contained in:
commit
7f11373f75
1
.gitignore
vendored
1
.gitignore
vendored
@ -52,6 +52,7 @@ gradle-app.setting
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
*.hprof
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
|
25
README.md
25
README.md
@ -1,20 +1,25 @@
|
||||
# Terra
|
||||
Terra is a data-driven world generator based on [Gaea](https://github.com/PolyhedralDev/Gaea). Find out more on our
|
||||
[Spigot page](https://www.spigotmc.org/resources/85151/)!
|
||||
|
||||
Terra is an incredibly powerful free & open-source data-driven, platform-agnostic world generator. It allows you to create a world exactly
|
||||
to your specifications, with no knowledge of Java required.
|
||||
|
||||
## Downloads:
|
||||
|
||||
* Paper+ servers (Paper, Tuinity, Purpur, etc): [SpigotMC](https://www.spigotmc.org/resources/85151/)
|
||||
* Fabric: [Modrinth](https://modrinth.com/mod/terra) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/terra-world-generator)
|
||||
|
||||
## Building and running Terra
|
||||
To build, simply run `./gradlew build` on Linux/MacOS, or `gradlew.bat build` on Windows.
|
||||
This will produce a jar in `build/libs` called `Terra-[CURRENT VERSION].jar`.
|
||||
You can put this right into your plugins dir, along with the correct Gaea version.
|
||||
|
||||
To build, simply run `./gradlew build` (`gradlew.bat build` on Windows). This will produce a jar in `build/libs`
|
||||
called `Terra-[CURRENT VERSION].jar`. You can put this right into your plugins dir, along with the correct Gaea version.
|
||||
|
||||
If you would like to test it with a default server config, just run `./gradlew setupServer` or
|
||||
`./gradlew.bat setupServer` to set up the server, then `./gradlew testWithPaper` or `gradlew.bat testWithPaper` to run
|
||||
the server. If you want a clean installation of the server, re-run the `setupServer` task.
|
||||
This will download a default server config from [here](https://github.com/PolyhedralDev/WorldGenTestServer)
|
||||
`./gradlew.bat setupServer` to set up the server, then `./gradlew testWithPaper` or `gradlew.bat testWithPaper` to run the server. If you
|
||||
want a clean installation of the server, re-run the `setupServer` task. This will download a default server config
|
||||
from [here](https://github.com/PolyhedralDev/WorldGenTestServer)
|
||||
and install the server in the `target/server` directory, along with all the needed plugins.
|
||||
|
||||
**Note: You will need to adjust the `NAME` variable `bukkit.yml` of the test server if you are not using the default
|
||||
Terra config.**
|
||||
**Note: You will need to adjust the `NAME` variable `bukkit.yml` of the test server if you are not using the default Terra config.**
|
||||
|
||||
## Contributing
|
||||
Contributions are welcome! If you want to see a feature in Terra, please, open an issue, or implement it yourself and
|
||||
|
@ -1,6 +1,6 @@
|
||||
import com.dfsek.terra.getGitHash
|
||||
|
||||
val versionObj = Version("4", "3", "0", true)
|
||||
val versionObj = Version("5", "0", "0", true)
|
||||
|
||||
allprojects {
|
||||
version = versionObj
|
||||
|
@ -6,7 +6,9 @@ import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.filter
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.language.jvm.tasks.ProcessResources
|
||||
|
||||
fun Project.configureCompilation() {
|
||||
configure<JavaPluginConvention> {
|
||||
@ -21,6 +23,15 @@ fun Project.configureCompilation() {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<ProcessResources> {
|
||||
include("**/*.*")
|
||||
filter<org.apache.tools.ant.filters.ReplaceTokens>(
|
||||
"tokens" to mapOf(
|
||||
"VERSION" to project.version.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
tasks.withType<Javadoc> {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
@ -18,10 +18,13 @@ dependencies {
|
||||
"shadedApi"("net.jafama:jafama:2.3.2")
|
||||
"shadedApi"("org.yaml:snakeyaml:1.27")
|
||||
"shadedApi"("org.ow2.asm:asm:9.0")
|
||||
"shadedApi"("commons-io:commons-io:2.6")
|
||||
|
||||
"compileOnly"("com.googlecode.json-simple:json-simple:1.1")
|
||||
|
||||
"shadedApi"("com.google.guava:guava:30.0-jre")
|
||||
"compileOnly"("com.google.guava:guava:30.0-jre")
|
||||
|
||||
"testImplementation"("com.google.guava:guava:30.0-jre")
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
@ -0,0 +1,61 @@
|
||||
package com.dfsek.terra.addon;
|
||||
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.addons.annotations.Addon;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class AddonClassLoader extends URLClassLoader {
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
public AddonClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super(urls, parent);
|
||||
}
|
||||
|
||||
public AddonClassLoader(URL[] urls) {
|
||||
super(urls);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Set<Class<? extends TerraAddon>> fetchAddonClasses(File file) throws IOException {
|
||||
JarFile jarFile = new JarFile(file);
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
|
||||
AddonClassLoader loader = new AddonClassLoader(new URL[] {file.toURI().toURL()}, AddonClassLoader.class.getClassLoader());
|
||||
|
||||
Set<Class<? extends TerraAddon>> set = new HashSet<>();
|
||||
while(entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
|
||||
if(entry.isDirectory() || !entry.getName().endsWith(".class")) continue;
|
||||
String className = entry.getName().substring(0, entry.getName().length() - 6).replace('/', '.');
|
||||
|
||||
try {
|
||||
Class<?> clazz = loader.loadClass(className);
|
||||
|
||||
Addon addon = clazz.getAnnotation(Addon.class);
|
||||
|
||||
if(addon == null) continue;
|
||||
|
||||
if(!TerraAddon.class.isAssignableFrom(clazz))
|
||||
throw new IllegalArgumentException("Addon class \"" + clazz + "\" must extend TerraAddon.");
|
||||
|
||||
set.add((Class<? extends TerraAddon>) clazz);
|
||||
} catch(ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e); // this should literally never happen, if it does something is very wrong
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
42
common/src/main/java/com/dfsek/terra/addon/AddonPool.java
Normal file
42
common/src/main/java/com/dfsek/terra/addon/AddonPool.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.dfsek.terra.addon;
|
||||
|
||||
import com.dfsek.terra.addon.exception.AddonLoadException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AddonPool {
|
||||
private final Map<String, PreLoadAddon> pool = new HashMap<>();
|
||||
|
||||
public void add(PreLoadAddon addon) throws AddonLoadException {
|
||||
if(pool.containsKey(addon.getId())) {
|
||||
String message = "Duplicate addon ID: " +
|
||||
addon.getId() + "; original ID from file: " +
|
||||
pool.get(addon.getId()).getFile().getAbsolutePath() +
|
||||
", class: " +
|
||||
pool.get(addon.getId()).getAddonClass().getCanonicalName() +
|
||||
"Duplicate ID from file: " +
|
||||
addon.getFile().getAbsolutePath() +
|
||||
", class: " +
|
||||
addon.getAddonClass().getCanonicalName();
|
||||
throw new AddonLoadException(message);
|
||||
}
|
||||
pool.put(addon.getId(), addon);
|
||||
}
|
||||
|
||||
public PreLoadAddon get(String id) {
|
||||
return pool.get(id);
|
||||
}
|
||||
|
||||
public void buildAll() throws AddonLoadException {
|
||||
for(PreLoadAddon value : pool.values()) {
|
||||
value.rebuildDependencies(this, value, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<PreLoadAddon> getAddons() {
|
||||
return new HashSet<>(pool.values());
|
||||
}
|
||||
}
|
58
common/src/main/java/com/dfsek/terra/addon/PreLoadAddon.java
Normal file
58
common/src/main/java/com/dfsek/terra/addon/PreLoadAddon.java
Normal file
@ -0,0 +1,58 @@
|
||||
package com.dfsek.terra.addon;
|
||||
|
||||
import com.dfsek.terra.addon.exception.AddonLoadException;
|
||||
import com.dfsek.terra.addon.exception.CircularDependencyException;
|
||||
import com.dfsek.terra.addon.exception.DependencyMissingException;
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.addons.annotations.Addon;
|
||||
import com.dfsek.terra.api.addons.annotations.Depends;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PreLoadAddon {
|
||||
private final List<PreLoadAddon> depends = new ArrayList<>();
|
||||
private final Class<? extends TerraAddon> addonClass;
|
||||
private final String id;
|
||||
private final String[] dependencies;
|
||||
private final File file;
|
||||
|
||||
public PreLoadAddon(Class<? extends TerraAddon> addonClass, File file) {
|
||||
this.addonClass = addonClass;
|
||||
this.id = addonClass.getAnnotation(Addon.class).value();
|
||||
this.file = file;
|
||||
Depends depends = addonClass.getAnnotation(Depends.class);
|
||||
this.dependencies = depends == null ? new String[] {} : depends.value();
|
||||
}
|
||||
|
||||
public List<PreLoadAddon> getDepends() {
|
||||
return depends;
|
||||
}
|
||||
|
||||
public void rebuildDependencies(AddonPool pool, PreLoadAddon origin, boolean levelG1) throws AddonLoadException {
|
||||
if(this.equals(origin) && !levelG1)
|
||||
throw new CircularDependencyException("Detected circular dependency in addon \"" + id + "\", dependencies: " + Arrays.toString(dependencies));
|
||||
|
||||
for(String dependency : dependencies) {
|
||||
PreLoadAddon preLoadAddon = pool.get(dependency);
|
||||
if(preLoadAddon == null)
|
||||
throw new DependencyMissingException("Dependency " + dependency + " was not found. Please install " + dependency + " to use " + id + ".");
|
||||
depends.add(preLoadAddon);
|
||||
preLoadAddon.rebuildDependencies(pool, origin, false);
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Class<? extends TerraAddon> getAddonClass() {
|
||||
return addonClass;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addon.exception;
|
||||
|
||||
public class AddonLoadException extends Exception {
|
||||
private static final long serialVersionUID = -4949084729296580176L;
|
||||
|
||||
public AddonLoadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AddonLoadException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addon.exception;
|
||||
|
||||
public class CircularDependencyException extends AddonLoadException {
|
||||
private static final long serialVersionUID = 7398510879124125121L;
|
||||
|
||||
public CircularDependencyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CircularDependencyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addon.exception;
|
||||
|
||||
public class DependencyMissingException extends AddonLoadException {
|
||||
private static final long serialVersionUID = -8419489102208521583L;
|
||||
|
||||
public DependencyMissingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DependencyMissingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
67
common/src/main/java/com/dfsek/terra/api/TerraPlugin.java
Normal file
67
common/src/main/java/com/dfsek/terra/api/TerraPlugin.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.dfsek.terra.api;
|
||||
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.event.EventManager;
|
||||
import com.dfsek.terra.api.platform.handle.ItemHandle;
|
||||
import com.dfsek.terra.api.platform.handle.WorldHandle;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.dfsek.terra.api.registry.CheckedRegistry;
|
||||
import com.dfsek.terra.api.registry.LockedRegistry;
|
||||
import com.dfsek.terra.api.util.logging.DebugLogger;
|
||||
import com.dfsek.terra.api.util.logging.Logger;
|
||||
import com.dfsek.terra.config.PluginConfig;
|
||||
import com.dfsek.terra.config.lang.Language;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Represents a Terra mod/plugin instance.
|
||||
*/
|
||||
public interface TerraPlugin extends LoaderRegistrar {
|
||||
WorldHandle getWorldHandle();
|
||||
|
||||
TerraWorld getWorld(World world);
|
||||
|
||||
Logger logger();
|
||||
|
||||
PluginConfig getTerraConfig();
|
||||
|
||||
File getDataFolder();
|
||||
|
||||
boolean isDebug();
|
||||
|
||||
Language getLanguage();
|
||||
|
||||
CheckedRegistry<ConfigPack> getConfigRegistry();
|
||||
|
||||
LockedRegistry<TerraAddon> getAddons();
|
||||
|
||||
boolean reload();
|
||||
|
||||
ItemHandle getItemHandle();
|
||||
|
||||
void saveDefaultConfig();
|
||||
|
||||
String platformName();
|
||||
|
||||
DebugLogger getDebugLogger();
|
||||
|
||||
EventManager getEventManager();
|
||||
|
||||
default String getVersion() {
|
||||
return "@VERSION@";
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a task that may or may not be thread safe, depending on platform.
|
||||
* <p>
|
||||
* Allows platforms to define what code is safe to be run asynchronously.
|
||||
*
|
||||
* @param task Task to be run.
|
||||
*/
|
||||
default void runPossiblyUnsafeTask(Runnable task) {
|
||||
task.run();
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.dfsek.terra.api.addons;
|
||||
|
||||
|
||||
import com.dfsek.terra.api.addons.annotations.Addon;
|
||||
import com.dfsek.terra.api.addons.annotations.Author;
|
||||
import com.dfsek.terra.api.addons.annotations.Version;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents an entry point for an addon. Implementations must be annotated with {@link Addon}.
|
||||
*/
|
||||
public abstract class TerraAddon {
|
||||
/**
|
||||
* Gets the version of this addon.
|
||||
*
|
||||
* @return Addon version.
|
||||
*/
|
||||
public final @NotNull String getVersion() {
|
||||
Version version = getClass().getAnnotation(Version.class);
|
||||
return version == null ? "0.1.0" : version.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the author of this addon.
|
||||
*
|
||||
* @return Addon author.
|
||||
*/
|
||||
public final @NotNull String getAuthor() {
|
||||
Author author = getClass().getAnnotation(Author.class);
|
||||
return author == null ? "Anon Y. Mous" : author.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name (ID) of this addon.
|
||||
*
|
||||
* @return Addon ID.
|
||||
*/
|
||||
public final @NotNull String getName() {
|
||||
Addon addon = getClass().getAnnotation(Addon.class);
|
||||
if(addon == null)
|
||||
throw new IllegalStateException("Addon annotation not present"); // This should never happen; the presence of this annotation is checked by the addon loader.
|
||||
return addon.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked immediately after an addon is loaded.
|
||||
*/
|
||||
public abstract void initialize();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies that the annotated class is an entry point for a Terra addon.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Addon {
|
||||
/**
|
||||
* @return The ID of the addon.
|
||||
*/
|
||||
@NotNull String value();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Optional annotation that specifies the author of an addon.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Author {
|
||||
/**
|
||||
* @return Name of the addon author.
|
||||
*/
|
||||
@NotNull String value();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Optional annotation that specifies dependencies of an addon.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Depends {
|
||||
/**
|
||||
* @return All addons this addon is dependent upon.
|
||||
*/
|
||||
@NotNull String[] value();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Optional annotation that specifies the version of an addon.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Version {
|
||||
/**
|
||||
* @return Version of the addon.
|
||||
*/
|
||||
@NotNull String value();
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.command.exception.CommandException;
|
||||
import com.dfsek.terra.api.command.exception.MalformedCommandException;
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CommandManager {
|
||||
void execute(String command, CommandSender sender, List<String> args) throws CommandException;
|
||||
|
||||
void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException;
|
||||
|
||||
List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException;
|
||||
|
||||
int getMaxArgumentDepth();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public interface CommandTemplate {
|
||||
void execute(CommandSender sender);
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ExecutionState {
|
||||
private final Set<String> switches = new HashSet<>();
|
||||
private final Map<String, String> args = new HashMap<>();
|
||||
private final CommandSender sender;
|
||||
|
||||
protected ExecutionState(CommandSender sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
protected void addSwitch(String flag) {
|
||||
switches.add(flag);
|
||||
}
|
||||
|
||||
protected void addArgument(String arg, String value) {
|
||||
args.put(arg, value);
|
||||
}
|
||||
|
||||
public String getArgument(String argument) {
|
||||
return args.get(argument);
|
||||
}
|
||||
|
||||
public boolean hasSwitch(String flag) {
|
||||
return switches.contains(flag);
|
||||
}
|
||||
|
||||
public CommandSender getSender() {
|
||||
return sender;
|
||||
}
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.command.annotation.Argument;
|
||||
import com.dfsek.terra.api.command.annotation.Command;
|
||||
import com.dfsek.terra.api.command.annotation.Subcommand;
|
||||
import com.dfsek.terra.api.command.annotation.Switch;
|
||||
import com.dfsek.terra.api.command.annotation.inject.ArgumentTarget;
|
||||
import com.dfsek.terra.api.command.annotation.inject.SwitchTarget;
|
||||
import com.dfsek.terra.api.command.annotation.type.DebugCommand;
|
||||
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
|
||||
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
|
||||
import com.dfsek.terra.api.command.arg.ArgumentParser;
|
||||
import com.dfsek.terra.api.command.exception.CommandException;
|
||||
import com.dfsek.terra.api.command.exception.ExecutionException;
|
||||
import com.dfsek.terra.api.command.exception.InvalidArgumentsException;
|
||||
import com.dfsek.terra.api.command.exception.MalformedCommandException;
|
||||
import com.dfsek.terra.api.command.exception.SwitchFormatException;
|
||||
import com.dfsek.terra.api.command.tab.TabCompleter;
|
||||
import com.dfsek.terra.api.injection.Injector;
|
||||
import com.dfsek.terra.api.injection.exception.InjectionException;
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
import com.dfsek.terra.api.platform.entity.Player;
|
||||
import com.dfsek.terra.api.util.ReflectionUtil;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TerraCommandManager implements CommandManager {
|
||||
private final Map<String, CommandHolder> commands = new HashMap<>();
|
||||
private final Injector<TerraPlugin> pluginInjector;
|
||||
private final TerraPlugin main;
|
||||
|
||||
public TerraCommandManager(TerraPlugin main) {
|
||||
this.main = main;
|
||||
this.pluginInjector = new Injector<>(main);
|
||||
pluginInjector.addExplicitTarget(TerraPlugin.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String commandName, CommandSender sender, List<String> argsIn) throws CommandException {
|
||||
if(!commands.containsKey(commandName)) throw new InvalidArgumentsException("No such command \"" + commandName + "\"");
|
||||
execute(commands.get(commandName), sender, new ArrayList<>(argsIn));
|
||||
}
|
||||
|
||||
private void execute(CommandHolder commandHolder, CommandSender sender, List<String> args) throws CommandException {
|
||||
Class<? extends CommandTemplate> commandClass = commandHolder.clazz;
|
||||
|
||||
if(commandClass.isAnnotationPresent(DebugCommand.class) && !main.isDebug()) {
|
||||
sender.sendMessage("Command must be executed with debug mode enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(commandClass.isAnnotationPresent(PlayerCommand.class) && !(sender instanceof Player)) {
|
||||
sender.sendMessage("Command must be executed by player.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !TerraWorld.isTerraWorld(((Player) sender).getWorld()))) {
|
||||
sender.sendMessage("Command must be executed in a Terra world.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> ogArgs = new ArrayList<>(args);
|
||||
|
||||
ExecutionState state = new ExecutionState(sender);
|
||||
|
||||
if(!commandClass.isAnnotationPresent(Command.class)) {
|
||||
invoke(commandClass, state, commandHolder);
|
||||
return;
|
||||
}
|
||||
|
||||
Command command = commandClass.getAnnotation(Command.class);
|
||||
|
||||
if(command.arguments().length == 0 && command.subcommands().length == 0) {
|
||||
if(args.isEmpty()) {
|
||||
invoke(commandClass, state, commandHolder);
|
||||
return;
|
||||
} else throw new InvalidArgumentsException("Expected 0 arguments, found " + args.size());
|
||||
}
|
||||
|
||||
if(!args.isEmpty() && commandHolder.subcommands.containsKey(args.get(0))) {
|
||||
String c = args.get(0);
|
||||
args.remove(0);
|
||||
execute(commandHolder.subcommands.get(c), sender, args);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean req = true;
|
||||
for(Argument argument : command.arguments()) {
|
||||
if(!req && argument.required()) {
|
||||
throw new MalformedCommandException("Required arguments must come first! Arguments: " + Arrays.toString(command.arguments()));
|
||||
}
|
||||
req = argument.required();
|
||||
|
||||
if(args.isEmpty()) {
|
||||
if(req) throw new InvalidArgumentsException("Invalid arguments: " + ogArgs + ", usage: " + command.usage());
|
||||
break;
|
||||
}
|
||||
|
||||
String arg = args.get(0);
|
||||
|
||||
if(arg.startsWith("-")) { // switches have started.
|
||||
if(req) throw new InvalidArgumentsException("Switches must come after arguments.");
|
||||
break;
|
||||
}
|
||||
|
||||
state.addArgument(argument.value(), args.remove(0));
|
||||
}
|
||||
|
||||
while(!args.isEmpty()) {
|
||||
String aSwitch = args.remove(0);
|
||||
if(!aSwitch.startsWith("-")) throw new SwitchFormatException("Invalid switch \"" + aSwitch + "\"");
|
||||
|
||||
String val = aSwitch.substring(1); // remove dash
|
||||
|
||||
if(!commandHolder.switches.containsKey(val)) throw new SwitchFormatException("No such switch \"" + aSwitch + "\"");
|
||||
|
||||
state.addSwitch(commandHolder.switches.get(val));
|
||||
}
|
||||
|
||||
invoke(commandClass, state, commandHolder);
|
||||
}
|
||||
|
||||
private void invoke(Class<? extends CommandTemplate> clazz, ExecutionState state, CommandHolder holder) throws CommandException {
|
||||
try {
|
||||
CommandTemplate template = clazz.getConstructor().newInstance();
|
||||
|
||||
pluginInjector.inject(template);
|
||||
|
||||
for(Field field : ReflectionUtil.getFields(clazz)) {
|
||||
if(field.isAnnotationPresent(ArgumentTarget.class)) {
|
||||
ArgumentTarget argumentTarget = field.getAnnotation(ArgumentTarget.class);
|
||||
if(!holder.argumentMap.containsKey(argumentTarget.value())) {
|
||||
throw new MalformedCommandException("Argument Target specifies nonexistent argument \"" + argumentTarget.value() + "\"");
|
||||
}
|
||||
|
||||
String argument = argumentTarget.value();
|
||||
|
||||
ArgumentParser<?> argumentParser = holder.argumentMap.get(argumentTarget.value()).argumentParser().getConstructor().newInstance();
|
||||
|
||||
pluginInjector.inject(argumentParser);
|
||||
|
||||
field.setAccessible(true);
|
||||
String value = state.getArgument(argument);
|
||||
|
||||
if(value == null) value = holder.argumentMap.get(argumentTarget.value()).defaultValue();
|
||||
|
||||
field.set(template, argumentParser.parse(state.getSender(), value));
|
||||
}
|
||||
if(field.isAnnotationPresent(SwitchTarget.class)) {
|
||||
SwitchTarget switchTarget = field.getAnnotation(SwitchTarget.class);
|
||||
if(!holder.switches.containsValue(switchTarget.value())) {
|
||||
System.out.println(holder.switches);
|
||||
throw new MalformedCommandException("Switch Target specifies nonexistent switch \"" + switchTarget.value() + "\"");
|
||||
}
|
||||
|
||||
if(!(field.getType() == boolean.class)) {
|
||||
throw new MalformedCommandException("Switch Target must be of type boolean.");
|
||||
}
|
||||
|
||||
field.setAccessible(true);
|
||||
field.setBoolean(template, state.hasSwitch(switchTarget.value()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
template.execute(state.getSender());
|
||||
} catch(Throwable e) {
|
||||
throw new ExecutionException("Failed to execute command: " + e.getMessage(), e);
|
||||
}
|
||||
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
|
||||
throw new MalformedCommandException("Unable to reflectively instantiate command: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
|
||||
commands.put(name, new CommandHolder(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException {
|
||||
if(args.isEmpty()) return new ArrayList<>(commands.keySet()).stream().sorted(String::compareTo).collect(Collectors.toList());
|
||||
if(!commands.containsKey(command)) return Collections.emptyList();
|
||||
return tabComplete(commands.get(command), sender, new ArrayList<>(args)).stream().filter(s -> s.toLowerCase().startsWith(args.get(args.size() - 1).toLowerCase())).sorted(String::compareTo).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxArgumentDepth() {
|
||||
int max = 0;
|
||||
for(CommandHolder value : commands.values()) {
|
||||
max = FastMath.max(getMaxArgumentDepth(value), max);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private int getMaxArgumentDepth(CommandHolder holder) {
|
||||
int max = 0;
|
||||
max = FastMath.max(holder.arguments.size() + holder.switchList.size(), max);
|
||||
for(CommandHolder value : holder.subcommands.values()) {
|
||||
max = FastMath.max(max, getMaxArgumentDepth(value) + 1);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private List<String> tabComplete(CommandHolder holder, CommandSender sender, List<String> args) throws CommandException {
|
||||
if(args.isEmpty()) return Collections.emptyList();
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if(args.size() == 1) {
|
||||
completions.addAll(holder.subcommands.keySet());
|
||||
}
|
||||
|
||||
if(holder.subcommands.containsKey(args.get(0))) {
|
||||
List<String> newArgs = new ArrayList<>(args);
|
||||
newArgs.remove(0);
|
||||
completions.addAll(tabComplete(holder.subcommands.get(args.get(0)), sender, newArgs));
|
||||
}
|
||||
try {
|
||||
if(args.size() <= holder.arguments.size()) {
|
||||
TabCompleter completer = holder.arguments.get(args.size() - 1).tabCompleter().getConstructor().newInstance();
|
||||
pluginInjector.inject(completer);
|
||||
completions.addAll(completer.complete(sender));
|
||||
}
|
||||
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
|
||||
throw new MalformedCommandException("Unable to reflectively instantiate tab-completer: ", e);
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-processes command metadata.
|
||||
*/
|
||||
private static final class CommandHolder {
|
||||
private final Class<? extends CommandTemplate> clazz;
|
||||
private final Map<String, CommandHolder> subcommands = new HashMap<>();
|
||||
private final Map<String, String> switches = new HashMap<>();
|
||||
private final List<Argument> arguments;
|
||||
private final List<Switch> switchList;
|
||||
private final Map<String, Argument> argumentMap = new HashMap<>();
|
||||
|
||||
private CommandHolder(Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
|
||||
this.clazz = clazz;
|
||||
if(clazz.isAnnotationPresent(Command.class)) {
|
||||
Command command = clazz.getAnnotation(Command.class);
|
||||
for(Subcommand subcommand : command.subcommands()) {
|
||||
if(subcommands.containsKey(subcommand.value()))
|
||||
throw new MalformedCommandException("Duplicate subcommand: " + subcommand);
|
||||
CommandHolder holder = new CommandHolder(subcommand.clazz());
|
||||
subcommands.put(subcommand.value(), holder);
|
||||
for(String alias : subcommand.aliases()) {
|
||||
subcommands.put(alias, holder);
|
||||
}
|
||||
}
|
||||
for(Switch aSwitch : command.switches()) {
|
||||
if(switches.containsKey(aSwitch.value())) throw new MalformedCommandException("Duplicate switch: " + aSwitch);
|
||||
switches.put(aSwitch.value(), aSwitch.value());
|
||||
for(String alias : aSwitch.aliases()) {
|
||||
switches.put(alias, aSwitch.value());
|
||||
}
|
||||
}
|
||||
for(Argument argument : command.arguments()) {
|
||||
if(argumentMap.containsKey(argument.value())) throw new MalformedCommandException("Duplicate argument: " + argument);
|
||||
argumentMap.put(argument.value(), argument);
|
||||
}
|
||||
arguments = Arrays.asList(command.arguments());
|
||||
switchList = Arrays.asList(command.switches());
|
||||
} else {
|
||||
arguments = Collections.emptyList();
|
||||
switchList = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import com.dfsek.terra.api.command.arg.ArgumentParser;
|
||||
import com.dfsek.terra.api.command.arg.StringArgumentParser;
|
||||
import com.dfsek.terra.api.command.tab.NothingCompleter;
|
||||
import com.dfsek.terra.api.command.tab.TabCompleter;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Argument {
|
||||
String value();
|
||||
|
||||
boolean required() default true;
|
||||
|
||||
Class<? extends TabCompleter> tabCompleter() default NothingCompleter.class;
|
||||
|
||||
Class<? extends ArgumentParser<?>> argumentParser() default StringArgumentParser.class;
|
||||
|
||||
String defaultValue() default "";
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Command {
|
||||
Argument[] arguments() default {};
|
||||
|
||||
Switch[] switches() default {};
|
||||
|
||||
Subcommand[] subcommands() default {};
|
||||
|
||||
String usage() default "";
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import com.dfsek.terra.api.command.CommandTemplate;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface Subcommand {
|
||||
String value();
|
||||
|
||||
String[] aliases() default {};
|
||||
|
||||
Class<? extends CommandTemplate> clazz();
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface Switch {
|
||||
String value();
|
||||
|
||||
String[] aliases() default {};
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.command.annotation.inject;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ArgumentTarget {
|
||||
String value();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.command.annotation.inject;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface SwitchTarget {
|
||||
String value();
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation.type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Command may only be executed with debug mode enabled.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DebugCommand {
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation.type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks command as player-only
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PlayerCommand {
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation.type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Command may only be executed in a Terra world.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WorldCommand {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public interface ArgumentParser<T> {
|
||||
T parse(CommandSender sender, String arg);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public class DoubleArgumentParser implements ArgumentParser<Double> {
|
||||
@Override
|
||||
public Double parse(CommandSender sender, String arg) {
|
||||
return arg == null ? null : Double.parseDouble(arg);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public class IntegerArgumentParser implements ArgumentParser<Integer> {
|
||||
@Override
|
||||
public Integer parse(CommandSender sender, String arg) {
|
||||
return arg == null ? null : Integer.parseInt(arg);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public class StringArgumentParser implements ArgumentParser<String> {
|
||||
@Override
|
||||
public String parse(CommandSender sender, String arg) {
|
||||
return arg;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public abstract class CommandException extends Exception {
|
||||
private static final long serialVersionUID = -2955328495045879822L;
|
||||
|
||||
public CommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public class ExecutionException extends CommandException {
|
||||
private static final long serialVersionUID = -6345523475880607959L;
|
||||
|
||||
public ExecutionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExecutionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public class InvalidArgumentsException extends CommandException {
|
||||
private static final long serialVersionUID = 7563619667472569824L;
|
||||
|
||||
public InvalidArgumentsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidArgumentsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
/**
|
||||
* Thrown when command is incorrectly defined.
|
||||
*/
|
||||
public class MalformedCommandException extends CommandException {
|
||||
private static final long serialVersionUID = -5417760860407895496L;
|
||||
|
||||
public MalformedCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MalformedCommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public class SwitchFormatException extends CommandException {
|
||||
private static final long serialVersionUID = -965858989317844628L;
|
||||
|
||||
public SwitchFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SwitchFormatException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.tab;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class NothingCompleter implements TabCompleter {
|
||||
@Override
|
||||
public List<String> complete(CommandSender sender) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.dfsek.terra.api.command.tab;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TabCompleter {
|
||||
List<String> complete(CommandSender sender);
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package com.dfsek.terra.api.core;
|
||||
|
||||
import com.dfsek.terra.api.LoaderRegistrar;
|
||||
import com.dfsek.terra.api.core.event.EventManager;
|
||||
import com.dfsek.terra.api.platform.handle.ItemHandle;
|
||||
import com.dfsek.terra.api.platform.handle.WorldHandle;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.dfsek.terra.config.PluginConfig;
|
||||
import com.dfsek.terra.config.lang.Language;
|
||||
import com.dfsek.terra.debug.DebugLogger;
|
||||
import com.dfsek.terra.registry.ConfigRegistry;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public interface TerraPlugin extends LoaderRegistrar {
|
||||
WorldHandle getWorldHandle();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
TerraWorld getWorld(World world);
|
||||
|
||||
Logger getLogger();
|
||||
|
||||
PluginConfig getTerraConfig();
|
||||
|
||||
File getDataFolder();
|
||||
|
||||
boolean isDebug();
|
||||
|
||||
Language getLanguage();
|
||||
|
||||
ConfigRegistry getRegistry();
|
||||
|
||||
void reload();
|
||||
|
||||
ItemHandle getItemHandle();
|
||||
|
||||
void saveDefaultConfig();
|
||||
|
||||
String platformName();
|
||||
|
||||
|
||||
DebugLogger getDebugLogger();
|
||||
|
||||
EventManager getEventManager();
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
|
||||
public interface EventListener {
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
|
||||
public interface EventManager {
|
||||
/**
|
||||
* Call an event, and return the execution status.
|
||||
* @param event Event to pass to all registered EventListeners.
|
||||
* @return False if the event is cancellable and has been cancelled, otherwise true.
|
||||
*/
|
||||
boolean callEvent(Event event);
|
||||
|
||||
void registerListener(EventListener listener);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events;
|
||||
|
||||
/**
|
||||
* Events that implement this interface may be cancelled.
|
||||
*
|
||||
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
|
||||
*/
|
||||
public interface Cancellable extends Event {
|
||||
boolean isCancelled();
|
||||
void setCancelled();
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events;
|
||||
|
||||
public interface Event {
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events.config;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
public abstract class ConfigPackLoadEvent implements Event {
|
||||
private final ConfigPack pack;
|
||||
|
||||
public ConfigPackLoadEvent(ConfigPack pack) {
|
||||
this.pack = pack;
|
||||
}
|
||||
|
||||
public ConfigPack getPack() {
|
||||
return pack;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.event;
|
||||
|
||||
import com.dfsek.terra.api.event.events.Event;
|
||||
|
||||
/**
|
||||
* Marker interface for a class that contains event listener methods.
|
||||
*
|
||||
* @see Event
|
||||
* @see EventManager
|
||||
*/
|
||||
public interface EventListener {
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.dfsek.terra.api.event;
|
||||
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.event.events.Event;
|
||||
|
||||
/**
|
||||
* Manages event registration and triggering.
|
||||
*/
|
||||
public interface EventManager {
|
||||
/**
|
||||
* Call an event, and return the execution status.
|
||||
*
|
||||
* @param event Event to pass to all registered EventListeners.
|
||||
* @return False if the event is cancellable and has been cancelled, otherwise true.
|
||||
*/
|
||||
boolean callEvent(Event event);
|
||||
|
||||
/**
|
||||
* Register an {@link EventListener} under an {@link TerraAddon}.
|
||||
*
|
||||
* @param addon Addon to register listener for.
|
||||
* @param listener Listener to register.
|
||||
*/
|
||||
void registerListener(TerraAddon addon, EventListener listener);
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
package com.dfsek.terra.api.event;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.core.event.annotations.Priority;
|
||||
import com.dfsek.terra.api.core.event.events.Cancellable;
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.event.annotations.Global;
|
||||
import com.dfsek.terra.api.event.annotations.Priority;
|
||||
import com.dfsek.terra.api.event.events.Cancellable;
|
||||
import com.dfsek.terra.api.event.events.Event;
|
||||
import com.dfsek.terra.api.event.events.PackEvent;
|
||||
import com.dfsek.terra.api.util.ReflectionUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@ -29,19 +32,30 @@ public class TerraEventManager implements EventManager {
|
||||
public boolean callEvent(Event event) {
|
||||
listeners.getOrDefault(event.getClass(), Collections.emptyList()).forEach(listenerHolder -> {
|
||||
try {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
if(event instanceof PackEvent && !listenerHolder.global) {
|
||||
PackEvent packEvent = (PackEvent) event;
|
||||
if(packEvent
|
||||
.getPack()
|
||||
.getTemplate()
|
||||
.getAddons()
|
||||
.contains(listenerHolder.addon)) {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
}
|
||||
} else {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
}
|
||||
} catch(InvocationTargetException e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
e.getTargetException().printStackTrace(new PrintWriter(writer));
|
||||
main.getLogger().warning("Exception occurred during event handling:");
|
||||
main.getLogger().warning(writer.toString());
|
||||
main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
main.logger().warning("Exception occurred during event handling:");
|
||||
main.logger().warning(writer.toString());
|
||||
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
} catch(Exception e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(writer));
|
||||
main.getLogger().warning("Exception occurred during event handling:");
|
||||
main.getLogger().warning(writer.toString());
|
||||
main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
main.logger().warning("Exception occurred during event handling:");
|
||||
main.logger().warning(writer.toString());
|
||||
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -51,7 +65,7 @@ public class TerraEventManager implements EventManager {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void registerListener(EventListener listener) {
|
||||
public void registerListener(TerraAddon addon, EventListener listener) {
|
||||
Class<? extends EventListener> listenerClass = listener.getClass();
|
||||
Method[] methods = ReflectionUtil.getMethods(listenerClass);
|
||||
|
||||
@ -68,7 +82,7 @@ public class TerraEventManager implements EventManager {
|
||||
|
||||
List<ListenerHolder> holders = listeners.computeIfAbsent((Class<? extends Event>) eventParam, e -> new ArrayList<>());
|
||||
|
||||
holders.add(new ListenerHolder(method, listener, priority));
|
||||
holders.add(new ListenerHolder(method, listener, priority, addon, method.getAnnotation(Global.class) != null));
|
||||
|
||||
holders.sort(Comparator.comparingInt(ListenerHolder::getPriority)); // Sort priorities.
|
||||
}
|
||||
@ -78,11 +92,15 @@ public class TerraEventManager implements EventManager {
|
||||
private final Method method;
|
||||
private final EventListener listener;
|
||||
private final int priority;
|
||||
private final TerraAddon addon;
|
||||
private final boolean global;
|
||||
|
||||
private ListenerHolder(Method method, EventListener listener, int priority) {
|
||||
private ListenerHolder(Method method, EventListener listener, int priority, TerraAddon addon, boolean global) {
|
||||
this.method = method;
|
||||
this.listener = listener;
|
||||
this.priority = priority;
|
||||
this.addon = addon;
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
@ -0,0 +1,17 @@
|
||||
package com.dfsek.terra.api.event.annotations;
|
||||
|
||||
import com.dfsek.terra.api.event.events.PackEvent;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies that an event handler is to handle all {@link PackEvent}s, regardless of whether the pack
|
||||
* depends on the addon's listener.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Global {
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.api.core.event.annotations;
|
||||
package com.dfsek.terra.api.event.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -12,11 +12,11 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Priority {
|
||||
/**
|
||||
* Highest possible priority. Listeners with this priority will always be invoked first.
|
||||
* Highest possible priority. Listeners with this priority will always be invoked last.
|
||||
*/
|
||||
int HIGHEST = Integer.MAX_VALUE;
|
||||
/**
|
||||
* Lowest possible priority. Listeners with this priority will always be invoked last.
|
||||
* Lowest possible priority. Listeners with this priority will always be invoked first.
|
||||
*/
|
||||
int LOWEST = Integer.MIN_VALUE;
|
||||
/**
|
@ -0,0 +1,22 @@
|
||||
package com.dfsek.terra.api.event.events;
|
||||
|
||||
/**
|
||||
* Events that implement this interface may be cancelled.
|
||||
* <p>
|
||||
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
|
||||
*/
|
||||
public interface Cancellable extends Event {
|
||||
/**
|
||||
* Get the cancellation status of the event.
|
||||
*
|
||||
* @return Whether event is cancelled.
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* Set the cancellation status of the event.
|
||||
*
|
||||
* @param cancelled Whether event is cancelled.
|
||||
*/
|
||||
void setCancelled(boolean cancelled);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.dfsek.terra.api.event.events;
|
||||
|
||||
/**
|
||||
* An event that addons may listen to.
|
||||
*/
|
||||
public interface Event {
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.event.events;
|
||||
|
||||
import com.dfsek.terra.api.event.annotations.Global;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
/**
|
||||
* An event with functionality directly linked to a {@link ConfigPack}.
|
||||
* <p>
|
||||
* PackEvents are only invoked when the pack specifies the addon in its
|
||||
* {@code addon} key (or when the listener is annotated {@link Global}).
|
||||
*/
|
||||
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
|
||||
public interface PackEvent extends Event {
|
||||
/**
|
||||
* Get the {@link ConfigPack} associated with this event.
|
||||
*
|
||||
* @return ConfigPack associated with the event.
|
||||
*/
|
||||
ConfigPack getPack();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.event.events.config;
|
||||
|
||||
import com.dfsek.terra.api.event.events.PackEvent;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
/**
|
||||
* An event related to the loading process of config packs.
|
||||
*/
|
||||
public abstract class ConfigPackLoadEvent implements PackEvent {
|
||||
private final ConfigPack pack;
|
||||
|
||||
public ConfigPackLoadEvent(ConfigPack pack) {
|
||||
this.pack = pack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigPack getPack() {
|
||||
return pack;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.api.core.event.events.config;
|
||||
package com.dfsek.terra.api.event.events.config;
|
||||
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.api.core.event.events.config;
|
||||
package com.dfsek.terra.api.event.events.config;
|
||||
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
@ -1,6 +1,9 @@
|
||||
package com.dfsek.terra.api.core.event.events.world;
|
||||
package com.dfsek.terra.api.event.events.world;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.api.event.events.Event;
|
||||
import com.dfsek.terra.api.event.events.PackEvent;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
import com.dfsek.terra.config.pack.WorldConfig;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
/**
|
||||
@ -16,4 +19,8 @@ public class TerraWorldLoadEvent implements Event {
|
||||
public TerraWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public WorldConfig getPack() {
|
||||
return world.getConfig();
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.dfsek.terra.api.injection;
|
||||
|
||||
import com.dfsek.terra.api.injection.annotations.Inject;
|
||||
import com.dfsek.terra.api.injection.exception.InjectionException;
|
||||
import com.dfsek.terra.api.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Dynamic dependency injector.
|
||||
* <p>
|
||||
* Stores an object to inject, and injects it into objects passed to {@link #inject(Object)}.
|
||||
*
|
||||
* @param <T> Type of object to inject.
|
||||
*/
|
||||
public class Injector<T> {
|
||||
private final T value;
|
||||
private final Set<Class<? extends T>> targets = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Instantiate an Injector with a value to inject
|
||||
*
|
||||
* @param value Value to inject
|
||||
*/
|
||||
public Injector(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an explicit class as a target. Useful for applications where subclasses may cause issues with DI.
|
||||
*
|
||||
* @param target Target class type.
|
||||
*/
|
||||
public void addExplicitTarget(Class<? extends T> target) {
|
||||
targets.add(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the stored object into an object.
|
||||
* <p>
|
||||
* Injects the stored object into any non-static, non-final fields
|
||||
* annotated with {@link Inject},
|
||||
* with type matching the stored object or any explicit targets
|
||||
* ({@link #addExplicitTarget(Class)}.
|
||||
*
|
||||
* @param object Object to inject into
|
||||
* @throws InjectionException If:
|
||||
* <ul>
|
||||
* <li>Matching field annotated with {@link Inject} is final</li>
|
||||
* <li>Matching field annotated with {@link Inject} is static</li>
|
||||
* <li>A reflective access exception occurs</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void inject(Object object) throws InjectionException {
|
||||
for(Field field : ReflectionUtil.getFields(object.getClass())) {
|
||||
Inject inject = field.getAnnotation(Inject.class);
|
||||
if(inject == null) continue;
|
||||
if(value.getClass().equals(field.getType()) || targets.contains(field.getType())) {
|
||||
int mod = field.getModifiers();
|
||||
if(Modifier.isFinal(mod)) {
|
||||
throw new InjectionException("Attempted to inject final field: " + field);
|
||||
}
|
||||
if(Modifier.isStatic(mod)) {
|
||||
throw new InjectionException("Attempted to inject static field: " + field);
|
||||
}
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
field.set(object, value);
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new InjectionException("Failed to inject field: " + field, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.injection.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies that a field is a target for dependency injection.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Inject {
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.api.injection.exception;
|
||||
|
||||
import com.dfsek.terra.api.injection.Injector;
|
||||
|
||||
/**
|
||||
* Thrown when dynamic dependency injection cannot be completed by an {@link Injector}.
|
||||
*/
|
||||
public class InjectionException extends Exception {
|
||||
private static final long serialVersionUID = -6929631447064215387L;
|
||||
|
||||
public InjectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InjectionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.dfsek.terra.api.math;
|
||||
|
||||
import com.dfsek.terra.api.util.FastRandom;
|
||||
import com.dfsek.terra.world.generation.math.Sampler;
|
||||
import com.dfsek.terra.world.generation.math.samplers.Sampler;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -1,96 +0,0 @@
|
||||
package com.dfsek.terra.api.math;
|
||||
|
||||
import com.dfsek.terra.api.math.noise.NoiseSampler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ProbabilityCollection<E> {
|
||||
private final Set<E> cont = new HashSet<>();
|
||||
private Object[] array = new Object[0];
|
||||
private int size;
|
||||
|
||||
public com.dfsek.terra.api.math.ProbabilityCollection<E> add(E item, int probability) {
|
||||
if(!cont.contains(item)) size++;
|
||||
cont.add(item);
|
||||
int oldLength = array.length;
|
||||
Object[] newArray = new Object[array.length + probability];
|
||||
System.arraycopy(array, 0, newArray, 0, array.length); // Expand array.
|
||||
array = newArray;
|
||||
for(int i = oldLength; i < array.length; i++) array[i] = item;
|
||||
return this;
|
||||
}
|
||||
|
||||
public E get(Random r) {
|
||||
if(array.length == 0) return null;
|
||||
return (E) array[r.nextInt(array.length)];
|
||||
}
|
||||
|
||||
public E get(NoiseSampler n, double x, double y, double z) {
|
||||
if(array.length == 0) return null;
|
||||
return (E) array[MathUtil.normalizeIndex(n.getNoise(x, y, z), array.length)];
|
||||
}
|
||||
|
||||
public E get(NoiseSampler n, double x, double z) {
|
||||
if(array.length == 0) return null;
|
||||
return (E) array[MathUtil.normalizeIndex(n.getNoise(x, z), array.length)];
|
||||
}
|
||||
|
||||
public int getTotalProbability() {
|
||||
return array.length;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Set<E> getContents() {
|
||||
return new HashSet<>(cont);
|
||||
}
|
||||
|
||||
public static final class Singleton<T> extends ProbabilityCollection<T> {
|
||||
private final T single;
|
||||
|
||||
public Singleton(T single) {
|
||||
this.single = single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProbabilityCollection<T> add(T item, int probability) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(Random r) {
|
||||
return single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(NoiseSampler n, double x, double y, double z) {
|
||||
return single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(NoiseSampler n, double x, double z) {
|
||||
return single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalProbability() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> getContents() {
|
||||
return Collections.singleton(single);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ public class Range implements Iterable<Integer> {
|
||||
return max;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range setMax(int max) {
|
||||
public Range setMax(int max) {
|
||||
this.max = max;
|
||||
return this;
|
||||
}
|
||||
@ -33,7 +33,7 @@ public class Range implements Iterable<Integer> {
|
||||
return min;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range setMin(int min) {
|
||||
public Range setMin(int min) {
|
||||
this.min = min;
|
||||
return this;
|
||||
}
|
||||
@ -42,35 +42,35 @@ public class Range implements Iterable<Integer> {
|
||||
return max - min;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range multiply(int mult) {
|
||||
public Range multiply(int mult) {
|
||||
min *= mult;
|
||||
max *= mult;
|
||||
return this;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range reflect(int pt) {
|
||||
return new com.dfsek.terra.api.math.Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
|
||||
public Range reflect(int pt) {
|
||||
return new Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
|
||||
}
|
||||
|
||||
public int get(Random r) {
|
||||
return r.nextInt((max - min) + 1) + min;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range intersects(com.dfsek.terra.api.math.Range other) {
|
||||
public Range intersects(com.dfsek.terra.api.math.Range other) {
|
||||
try {
|
||||
return new com.dfsek.terra.api.math.Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
|
||||
return new Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
|
||||
} catch(IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range add(int add) {
|
||||
public Range add(int add) {
|
||||
this.min += add;
|
||||
this.max += add;
|
||||
return this;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range sub(int sub) {
|
||||
public Range sub(int sub) {
|
||||
this.min -= sub;
|
||||
this.max -= sub;
|
||||
return this;
|
||||
@ -89,7 +89,7 @@ public class Range implements Iterable<Integer> {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof com.dfsek.terra.api.math.Range)) return false;
|
||||
com.dfsek.terra.api.math.Range other = (com.dfsek.terra.api.math.Range) obj;
|
||||
Range other = (com.dfsek.terra.api.math.Range) obj;
|
||||
return other.getMin() == this.getMin() && other.getMax() == this.getMax();
|
||||
}
|
||||
|
||||
@ -100,10 +100,10 @@ public class Range implements Iterable<Integer> {
|
||||
}
|
||||
|
||||
private static class RangeIterator implements Iterator<Integer> {
|
||||
private final com.dfsek.terra.api.math.Range m;
|
||||
private final Range m;
|
||||
private Integer current;
|
||||
|
||||
public RangeIterator(com.dfsek.terra.api.math.Range m) {
|
||||
public RangeIterator(Range m) {
|
||||
this.m = m;
|
||||
current = m.getMin();
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sampler implementation using Paralithic expression
|
||||
* Sampler3D implementation using Paralithic expression
|
||||
*/
|
||||
public class ExpressionSampler implements NoiseSampler {
|
||||
private final Expression expression;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.dfsek.terra.api.math.noise.samplers.noise;
|
||||
|
||||
/**
|
||||
* Sampler implementation that returns a constant.
|
||||
* Sampler3D implementation that returns a constant.
|
||||
*/
|
||||
public class ConstantSampler extends NoiseFunction {
|
||||
private final double constant;
|
||||
|
@ -0,0 +1,105 @@
|
||||
package com.dfsek.terra.api.math.noise.samplers.noise;
|
||||
|
||||
import com.dfsek.terra.api.math.noise.samplers.noise.random.WhiteNoiseSampler;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
public class GaborNoiseSampler extends NoiseFunction {
|
||||
private final WhiteNoiseSampler rand;
|
||||
private double k = 1.0;
|
||||
private double a = 0.1;
|
||||
private double f0 = 0.625;
|
||||
private double kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / a);
|
||||
private double omega0 = Math.PI * 0.25;
|
||||
private boolean isotropic = true;
|
||||
private double impulsesPerKernel = 64d;
|
||||
private double impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
|
||||
|
||||
private double impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
|
||||
private double g = FastMath.exp(-impulsesPerCell);
|
||||
|
||||
|
||||
public GaborNoiseSampler(int seed) {
|
||||
super(seed);
|
||||
rand = new WhiteNoiseSampler(seed);
|
||||
}
|
||||
|
||||
public void setIsotropic(boolean isotropic) {
|
||||
this.isotropic = isotropic;
|
||||
}
|
||||
|
||||
public void setImpulsesPerKernel(double impulsesPerKernel) {
|
||||
this.impulsesPerKernel = impulsesPerKernel;
|
||||
recalculateRadiusAndDensity();
|
||||
}
|
||||
|
||||
public void setA(double a) {
|
||||
this.a = a;
|
||||
recalculateRadiusAndDensity();
|
||||
}
|
||||
|
||||
public void setFrequency0(double f0) {
|
||||
this.f0 = f0;
|
||||
}
|
||||
|
||||
public void setRotation(double omega0) {
|
||||
this.omega0 = Math.PI * omega0;
|
||||
}
|
||||
|
||||
public void setDeviation(double k) {
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
private void recalculateRadiusAndDensity() {
|
||||
kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / this.a);
|
||||
impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
|
||||
impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
|
||||
g = FastMath.exp(-impulsesPerCell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double z) {
|
||||
return gaborNoise(seed, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y, double z) {
|
||||
return gaborNoise(seed, x, z);
|
||||
}
|
||||
|
||||
private double gaborNoise(int seed, double x, double y) {
|
||||
x /= kernelRadius;
|
||||
y /= kernelRadius;
|
||||
int xi = fastFloor(x);
|
||||
int yi = fastFloor(y);
|
||||
double xf = x - xi;
|
||||
double yf = y - yi;
|
||||
double noise = 0;
|
||||
for(int dx = -1; dx <= 1; dx++) {
|
||||
for(int dz = -1; dz <= 1; dz++) {
|
||||
noise += calculateCell(seed, xi + dx, yi + dz, xf - dx, yf - dz);
|
||||
}
|
||||
}
|
||||
return noise;
|
||||
}
|
||||
|
||||
private double calculateCell(int seed, int xi, int yi, double x, double y) {
|
||||
long mashedSeed = murmur64(31L * xi + yi) + seed;
|
||||
|
||||
double gaussianSource = (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
|
||||
int impulses = 0;
|
||||
while(gaussianSource > g) {
|
||||
impulses++;
|
||||
gaussianSource *= (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
|
||||
}
|
||||
|
||||
double noise = 0;
|
||||
for(int i = 0; i < impulses; i++) {
|
||||
noise += rand.getNoiseRaw(mashedSeed++) * gabor(isotropic ? (rand.getNoiseRaw(mashedSeed++) + 1) * Math.PI : omega0, x * kernelRadius, y * kernelRadius);
|
||||
}
|
||||
return noise;
|
||||
}
|
||||
|
||||
private double gabor(double omega_0, double x, double y) {
|
||||
return k * (FastMath.exp(-Math.PI * (a * a) * (x * x + y * y)) * fastCos(2 * Math.PI * f0 * (x * fastCos(omega_0) + y * fastSin(omega_0))));
|
||||
}
|
||||
}
|
@ -22,6 +22,8 @@ public abstract class NoiseFunction implements NoiseSampler {
|
||||
return f >= 0 ? (int) f : (int) f - 1;
|
||||
}
|
||||
|
||||
static final int precision = 100;
|
||||
|
||||
protected static int hash(int seed, int xPrimed, int yPrimed, int zPrimed) {
|
||||
int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed;
|
||||
|
||||
@ -36,6 +38,8 @@ public abstract class NoiseFunction implements NoiseSampler {
|
||||
return hash;
|
||||
}
|
||||
|
||||
static final int modulus = 360 * precision;
|
||||
|
||||
protected static int fastRound(double f) {
|
||||
return f >= 0 ? (int) (f + 0.5f) : (int) (f - 0.5);
|
||||
}
|
||||
@ -73,6 +77,48 @@ public abstract class NoiseFunction implements NoiseSampler {
|
||||
return FastMath.sqrt(f);
|
||||
}
|
||||
|
||||
static final double[] sin = new double[360 * 100]; // lookup table
|
||||
|
||||
static {
|
||||
for(int i = 0; i < sin.length; i++) {
|
||||
sin[i] = (float) Math.sin((double) (i) / (precision));
|
||||
}
|
||||
}
|
||||
|
||||
protected static int fastCeil(double f) {
|
||||
int i = (int) f;
|
||||
if(i < f) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Murmur64 hashing function
|
||||
*
|
||||
* @param h Input value
|
||||
* @return Hashed value
|
||||
*/
|
||||
protected static long murmur64(long h) {
|
||||
h ^= h >>> 33;
|
||||
h *= 0xff51afd7ed558ccdL;
|
||||
h ^= h >>> 33;
|
||||
h *= 0xc4ceb9fe1a85ec53L;
|
||||
h ^= h >>> 33;
|
||||
return h;
|
||||
}
|
||||
|
||||
private static double sinLookup(int a) {
|
||||
return a >= 0 ? sin[a % (modulus)] : -sin[-a % (modulus)];
|
||||
}
|
||||
|
||||
protected static double fastSin(double a) {
|
||||
return sinLookup((int) (a * precision + 0.5f));
|
||||
}
|
||||
|
||||
protected static double fastCos(double a) {
|
||||
return sinLookup((int) ((a + Math.PI / 2) * precision + 0.5f));
|
||||
}
|
||||
|
||||
|
||||
public void setSeed(int seed) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
@ -12,38 +12,41 @@ public class WhiteNoiseSampler extends NoiseFunction {
|
||||
super(seed);
|
||||
}
|
||||
|
||||
public double getNoiseRaw(long seed) {
|
||||
return (Double.longBitsToDouble((murmur64(seed) & 0x000fffffffffffffL) | POSITIVE_POW1) - 1.5) * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y) {
|
||||
long hashX = Double.doubleToRawLongBits(x) ^ seed;
|
||||
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
|
||||
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
|
||||
long base = (murmur64(hash) & 0x000fffffffffffffL)
|
||||
| POSITIVE_POW1; // Sign and exponent
|
||||
return (Double.longBitsToDouble(base) - 1.5) * 2;
|
||||
return (getNoiseUnmapped(seed, x, y) - 1.5) * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y, double z) {
|
||||
return (getNoiseUnmapped(seed, x, y, z) - 1.5) * 2;
|
||||
}
|
||||
|
||||
public double getNoiseUnmapped(int seed, double x, double y, double z) {
|
||||
long base = ((randomBits(seed, x, y, z)) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
|
||||
return Double.longBitsToDouble(base);
|
||||
}
|
||||
|
||||
public double getNoiseUnmapped(int seed, double x, double y) {
|
||||
long base = (randomBits(seed, x, y) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
|
||||
return Double.longBitsToDouble(base);
|
||||
}
|
||||
|
||||
public long randomBits(int seed, double x, double y, double z) {
|
||||
long hashX = Double.doubleToRawLongBits(x) ^ seed;
|
||||
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
|
||||
long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z);
|
||||
long base = ((murmur64(hash)) & 0x000fffffffffffffL)
|
||||
| POSITIVE_POW1; // Sign and exponent
|
||||
return (Double.longBitsToDouble(base) - 1.5) * 2;
|
||||
return murmur64(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Murmur64 hashing function
|
||||
*
|
||||
* @param h Input value
|
||||
* @return Hashed value
|
||||
*/
|
||||
private static long murmur64(long h) {
|
||||
h ^= h >>> 33;
|
||||
h *= 0xff51afd7ed558ccdL;
|
||||
h ^= h >>> 33;
|
||||
h *= 0xc4ceb9fe1a85ec53L;
|
||||
h ^= h >>> 33;
|
||||
return h;
|
||||
public long randomBits(int seed, double x, double y) {
|
||||
long hashX = Double.doubleToRawLongBits(x) ^ seed;
|
||||
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
|
||||
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
|
||||
return murmur64(hash);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,243 @@
|
||||
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
|
||||
|
||||
public class SimplexSampler extends SimplexStyleSampler {
|
||||
private static final Double2[] GRAD_2D = {
|
||||
new Double2(-1, -1), new Double2(1, -1), new Double2(-1, 1), new Double2(1, 1),
|
||||
new Double2(0, -1), new Double2(-1, 0), new Double2(0, 1), new Double2(1, 0),
|
||||
};
|
||||
private static final Double3[] GRAD_3D = {
|
||||
new Double3(1, 1, 0), new Double3(-1, 1, 0), new Double3(1, -1, 0), new Double3(-1, -1, 0),
|
||||
new Double3(1, 0, 1), new Double3(-1, 0, 1), new Double3(1, 0, -1), new Double3(-1, 0, -1),
|
||||
new Double3(0, 1, 1), new Double3(0, -1, 1), new Double3(0, 1, -1), new Double3(0, -1, -1),
|
||||
new Double3(1, 1, 0), new Double3(0, -1, 1), new Double3(-1, 1, 0), new Double3(0, -1, -1),
|
||||
};
|
||||
|
||||
private static final double F2 = 1.0 / 2.0;
|
||||
private static final double F3 = (1.0 / 3.0);
|
||||
private static final double G2 = 1.0 / 4.0;
|
||||
private static final double G3 = (1.0 / 6.0);
|
||||
private static final double G33 = G3 * 3 - 1;
|
||||
|
||||
private static final int X_PRIME = 1619;
|
||||
private static final int Y_PRIME = 31337;
|
||||
private static final int Z_PRIME = 6971;
|
||||
|
||||
|
||||
public SimplexSampler(int seed) {
|
||||
super(seed);
|
||||
}
|
||||
|
||||
private static double gradCoord3D(int seed, int x, int y, int z, double xd, double yd, double zd) {
|
||||
int hash = seed;
|
||||
hash ^= X_PRIME * x;
|
||||
hash ^= Y_PRIME * y;
|
||||
hash ^= Z_PRIME * z;
|
||||
|
||||
hash = hash * hash * hash * 60493;
|
||||
hash = (hash >> 13) ^ hash;
|
||||
|
||||
Double3 g = GRAD_3D[hash & 15];
|
||||
|
||||
return xd * g.x + yd * g.y + zd * g.z;
|
||||
}
|
||||
|
||||
private static double gradCoord2D(int seed, int x, int y, double xd, double yd) {
|
||||
int hash = seed;
|
||||
hash ^= X_PRIME * x;
|
||||
hash ^= Y_PRIME * y;
|
||||
|
||||
hash = hash * hash * hash * 60493;
|
||||
hash = (hash >> 13) ^ hash;
|
||||
|
||||
Double2 g = GRAD_2D[hash & 7];
|
||||
|
||||
return xd * g.x + yd * g.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y) {
|
||||
double t = (x + y) * F2;
|
||||
int i = fastFloor(x + t);
|
||||
int j = fastFloor(y + t);
|
||||
|
||||
t = (i + j) * G2;
|
||||
double X0 = i - t;
|
||||
double Y0 = j - t;
|
||||
|
||||
double x0 = x - X0;
|
||||
double y0 = y - Y0;
|
||||
|
||||
int i1, j1;
|
||||
if(x0 > y0) {
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
} else {
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
}
|
||||
|
||||
double x1 = x0 - i1 + G2;
|
||||
double y1 = y0 - j1 + G2;
|
||||
double x2 = x0 - 1 + F2;
|
||||
double y2 = y0 - 1 + F2;
|
||||
|
||||
double n0, n1, n2;
|
||||
|
||||
t = 0.5 - x0 * x0 - y0 * y0;
|
||||
if(t < 0) {
|
||||
n0 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n0 = t * t * gradCoord2D(seed, i, j, x0, y0);
|
||||
}
|
||||
|
||||
t = 0.5 - x1 * x1 - y1 * y1;
|
||||
if(t < 0) {
|
||||
n1 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n1 = t * t * gradCoord2D(seed, i + i1, j + j1, x1, y1);
|
||||
}
|
||||
|
||||
t = 0.5 - x2 * x2 - y2 * y2;
|
||||
if(t < 0) {
|
||||
n2 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n2 = t * t * gradCoord2D(seed, i + 1, j + 1, x2, y2);
|
||||
}
|
||||
|
||||
return 50 * (n0 + n1 + n2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y, double z) {
|
||||
double t = (x + y + z) * F3;
|
||||
int i = fastFloor(x + t);
|
||||
int j = fastFloor(y + t);
|
||||
int k = fastFloor(z + t);
|
||||
|
||||
t = (i + j + k) * G3;
|
||||
double x0 = x - (i - t);
|
||||
double y0 = y - (j - t);
|
||||
double z0 = z - (k - t);
|
||||
|
||||
int i1, j1, k1;
|
||||
int i2, j2, k2;
|
||||
|
||||
if(x0 >= y0) {
|
||||
if(y0 >= z0) {
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 1;
|
||||
k2 = 0;
|
||||
} else if(x0 >= z0) {
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 0;
|
||||
k2 = 1;
|
||||
} else // x0 < z0
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 0;
|
||||
k1 = 1;
|
||||
i2 = 1;
|
||||
j2 = 0;
|
||||
k2 = 1;
|
||||
}
|
||||
} else // x0 < y0
|
||||
{
|
||||
if(y0 < z0) {
|
||||
i1 = 0;
|
||||
j1 = 0;
|
||||
k1 = 1;
|
||||
i2 = 0;
|
||||
j2 = 1;
|
||||
k2 = 1;
|
||||
} else if(x0 < z0) {
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
k1 = 0;
|
||||
i2 = 0;
|
||||
j2 = 1;
|
||||
k2 = 1;
|
||||
} else // x0 >= z0
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 1;
|
||||
k2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
double x1 = x0 - i1 + G3;
|
||||
double y1 = y0 - j1 + G3;
|
||||
double z1 = z0 - k1 + G3;
|
||||
double x2 = x0 - i2 + F3;
|
||||
double y2 = y0 - j2 + F3;
|
||||
double z2 = z0 - k2 + F3;
|
||||
double x3 = x0 + G33;
|
||||
double y3 = y0 + G33;
|
||||
double z3 = z0 + G33;
|
||||
|
||||
double n0, n1, n2, n3;
|
||||
|
||||
t = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
|
||||
if(t < 0) n0 = 0;
|
||||
else {
|
||||
t *= t;
|
||||
n0 = t * t * gradCoord3D(seed, i, j, k, x0, y0, z0);
|
||||
}
|
||||
|
||||
t = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
|
||||
if(t < 0) {
|
||||
n1 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n1 = t * t * gradCoord3D(seed, i + i1, j + j1, k + k1, x1, y1, z1);
|
||||
}
|
||||
|
||||
t = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
|
||||
if(t < 0) {
|
||||
n2 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n2 = t * t * gradCoord3D(seed, i + i2, j + j2, k + k2, x2, y2, z2);
|
||||
}
|
||||
|
||||
t = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
|
||||
if(t < 0) {
|
||||
n3 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n3 = t * t * gradCoord3D(seed, i + 1, j + 1, k + 1, x3, y3, z3);
|
||||
}
|
||||
|
||||
return 32 * (n0 + n1 + n2 + n3);
|
||||
}
|
||||
|
||||
private static class Double2 {
|
||||
public final double x, y;
|
||||
|
||||
public Double2(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Double3 {
|
||||
public final double x, y, z;
|
||||
|
||||
public Double3(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.dfsek.terra.api.math.paralithic.noise;
|
||||
|
||||
import com.dfsek.terra.api.math.noise.NoiseSampler;
|
||||
import com.dfsek.terra.util.hash.HashMapDoubleDouble;
|
||||
import com.dfsek.terra.api.util.hash.HashMapDoubleDouble;
|
||||
|
||||
|
||||
|
||||
|
@ -170,4 +170,9 @@ public class Location implements Cloneable {
|
||||
public String toString() {
|
||||
return "[" + world + ": (" + getX() + ", " + getY() + ", " + getZ() + ")]";
|
||||
}
|
||||
|
||||
public Location multiply(double v) {
|
||||
vector.multiply(v);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
package com.dfsek.terra.api.platform;
|
||||
|
||||
import com.dfsek.terra.api.platform.entity.Entity;
|
||||
|
||||
public interface Player extends Entity {
|
||||
}
|
@ -11,7 +11,9 @@ public interface Block extends Handle {
|
||||
|
||||
BlockState getState();
|
||||
|
||||
Block getRelative(BlockFace face);
|
||||
default Block getRelative(BlockFace face) {
|
||||
return getRelative(face, 1);
|
||||
}
|
||||
|
||||
Block getRelative(BlockFace face, int len);
|
||||
|
||||
@ -19,7 +21,9 @@ public interface Block extends Handle {
|
||||
|
||||
Location getLocation();
|
||||
|
||||
MaterialData getType();
|
||||
default BlockType getType() {
|
||||
return getBlockData().getBlockType();
|
||||
}
|
||||
|
||||
int getX();
|
||||
|
||||
|
@ -3,11 +3,16 @@ package com.dfsek.terra.api.platform.block;
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
public interface BlockData extends Cloneable, Handle {
|
||||
MaterialData getMaterial();
|
||||
|
||||
boolean matches(MaterialData materialData);
|
||||
BlockType getBlockType();
|
||||
|
||||
boolean matches(BlockData other);
|
||||
|
||||
BlockData clone();
|
||||
|
||||
String getAsString();
|
||||
|
||||
boolean isAir();
|
||||
|
||||
boolean isStructureVoid();
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.dfsek.terra.api.platform.block;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
public interface BlockType extends Handle {
|
||||
BlockData getDefaultData();
|
||||
|
||||
boolean isSolid();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.dfsek.terra.api.platform.block;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
public interface MaterialData extends Handle {
|
||||
boolean matches(MaterialData other);
|
||||
|
||||
boolean matches(BlockData other);
|
||||
|
||||
boolean isSolid();
|
||||
|
||||
boolean isAir();
|
||||
|
||||
double getMaxDurability();
|
||||
|
||||
BlockData createBlockData();
|
||||
}
|
@ -3,7 +3,12 @@ package com.dfsek.terra.api.platform.entity;
|
||||
import com.dfsek.terra.api.math.vector.Location;
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
|
||||
public interface Entity extends Handle, CommandSender {
|
||||
Location getLocation();
|
||||
|
||||
void setLocation(Location location);
|
||||
|
||||
World getWorld();
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
package com.dfsek.terra.api.platform.entity;
|
||||
|
||||
public interface Player extends Entity {
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package com.dfsek.terra.api.platform.handle;
|
||||
|
||||
import com.dfsek.terra.api.platform.block.MaterialData;
|
||||
import com.dfsek.terra.api.platform.inventory.ItemStack;
|
||||
import com.dfsek.terra.api.platform.inventory.Item;
|
||||
import com.dfsek.terra.api.platform.inventory.item.Enchantment;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface ItemHandle {
|
||||
ItemStack newItemStack(MaterialData material, int amount);
|
||||
|
||||
Item createItem(String data);
|
||||
|
||||
Enchantment getEnchantment(String id);
|
||||
|
||||
|
@ -1,23 +1,26 @@
|
||||
package com.dfsek.terra.api.platform.handle;
|
||||
|
||||
import com.dfsek.terra.api.platform.block.Block;
|
||||
import com.dfsek.terra.api.math.vector.Location;
|
||||
import com.dfsek.terra.api.platform.block.BlockData;
|
||||
import com.dfsek.terra.api.platform.block.MaterialData;
|
||||
import com.dfsek.terra.api.platform.entity.EntityType;
|
||||
import com.dfsek.terra.api.platform.entity.Player;
|
||||
import com.dfsek.terra.api.util.generic.pair.Pair;
|
||||
|
||||
/**
|
||||
* Interface to be implemented for world manipulation.
|
||||
*/
|
||||
public interface WorldHandle {
|
||||
void setBlockData(Block block, BlockData data, boolean physics);
|
||||
|
||||
BlockData getBlockData(Block block);
|
||||
|
||||
MaterialData getType(Block block);
|
||||
|
||||
BlockData createBlockData(String data);
|
||||
|
||||
MaterialData createMaterialData(String data);
|
||||
|
||||
EntityType getEntity(String id);
|
||||
|
||||
/**
|
||||
* Get the locations selected by a player. (Usually via WorldEdit)
|
||||
*
|
||||
* @param player Player to get locations for
|
||||
* @return Pair of locations.
|
||||
*/
|
||||
default Pair<Location, Location> getSelectedLocation(Player player) {
|
||||
throw new UnsupportedOperationException("Cannot get selection on this platform.");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.platform.inventory;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
/**
|
||||
* An inventory item.
|
||||
*/
|
||||
public interface Item extends Handle {
|
||||
ItemStack newItemStack(int amount);
|
||||
|
||||
double getMaxDurability();
|
||||
}
|
@ -1,17 +1,14 @@
|
||||
package com.dfsek.terra.api.platform.inventory;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
import com.dfsek.terra.api.platform.block.MaterialData;
|
||||
import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
|
||||
|
||||
public interface ItemStack extends Handle, Cloneable {
|
||||
public interface ItemStack extends Handle {
|
||||
int getAmount();
|
||||
|
||||
void setAmount(int i);
|
||||
|
||||
MaterialData getType();
|
||||
|
||||
ItemStack clone();
|
||||
Item getType();
|
||||
|
||||
ItemMeta getItemMeta();
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
/**
|
||||
* API for platform implementations. Mostly interfaces to be implemented by platform delegates.
|
||||
* <p>
|
||||
* Interfaces in this package generally should <b>not</b> be implemented by addons.
|
||||
*/
|
||||
package com.dfsek.terra.api.platform;
|
||||
|
@ -25,11 +25,19 @@ public interface World extends Handle {
|
||||
|
||||
Chunk getChunkAt(int x, int z);
|
||||
|
||||
default Chunk getChunkAt(Location location) {
|
||||
return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||
}
|
||||
|
||||
File getWorldFolder();
|
||||
|
||||
Block getBlockAt(int x, int y, int z);
|
||||
|
||||
Block getBlockAt(Location l);
|
||||
default Block getBlockAt(Location l) {
|
||||
return getBlockAt(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
||||
}
|
||||
|
||||
Entity spawnEntity(Location location, EntityType entityType);
|
||||
|
||||
int getMinHeight();
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.platform.world.generator;
|
||||
|
||||
import com.dfsek.terra.api.platform.world.ChunkAccess;
|
||||
|
||||
public interface ChunkData extends ChunkAccess {
|
||||
/**
|
||||
* Get the maximum height for the chunk.
|
||||
* <p>
|
||||
* Setting blocks at or above this height will do nothing.
|
||||
*
|
||||
* @return the maximum height
|
||||
*/
|
||||
int getMaxHeight();
|
||||
}
|
@ -1,43 +1,7 @@
|
||||
package com.dfsek.terra.api.platform.world.generator;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
import com.dfsek.terra.api.platform.world.BiomeGrid;
|
||||
import com.dfsek.terra.api.platform.world.ChunkAccess;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public interface ChunkGenerator extends Handle {
|
||||
boolean isParallelCapable();
|
||||
|
||||
boolean shouldGenerateCaves();
|
||||
|
||||
boolean shouldGenerateDecorations();
|
||||
|
||||
boolean shouldGenerateMobs();
|
||||
|
||||
boolean shouldGenerateStructures();
|
||||
|
||||
ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome);
|
||||
|
||||
|
||||
List<BlockPopulator> getDefaultPopulators(World world);
|
||||
|
||||
@Nullable
|
||||
TerraChunkGenerator getTerraGenerator();
|
||||
|
||||
interface ChunkData extends ChunkAccess {
|
||||
/**
|
||||
* Get the maximum height for the chunk.
|
||||
* <p>
|
||||
* Setting blocks at or above this height will do nothing.
|
||||
*
|
||||
* @return the maximum height
|
||||
*/
|
||||
int getMaxHeight();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package com.dfsek.terra.api.platform.world.generator;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
|
||||
|
||||
public interface GeneratorWrapper extends Handle {
|
||||
@Override
|
||||
TerraChunkGenerator getHandle();
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
package com.dfsek.terra.api.registry;
|
||||
|
||||
import com.dfsek.tectonic.exception.LoadException;
|
||||
import com.dfsek.tectonic.loading.ConfigLoader;
|
||||
import com.dfsek.terra.registry.OpenRegistry;
|
||||
import com.dfsek.terra.registry.exception.DuplicateEntryException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Wrapper for a registry that ensures checked additions.
|
||||
*
|
||||
* @param <T> Type in registry
|
||||
*/
|
||||
public class CheckedRegistry<T> implements Registry<T> {
|
||||
private final OpenRegistry<T> registry;
|
||||
|
||||
public CheckedRegistry(OpenRegistry<T> registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to this registry, checking whether it is present first.
|
||||
*
|
||||
* @param identifier Identifier to assign value.
|
||||
* @param value Value to add.
|
||||
* @throws DuplicateEntryException If an entry with the same identifier is already present.
|
||||
*/
|
||||
public void add(String identifier, T value) throws DuplicateEntryException {
|
||||
registry.addChecked(identifier, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to the registry, without checking presence beforehand.
|
||||
* <p>
|
||||
* Use of this method is generally discouraged, as it is bad practice to overwrite registry values.
|
||||
*
|
||||
* @param identifier Identifier to assign value.
|
||||
* @param value Value to add.
|
||||
* @deprecated Use of {@link #add(String, Object)} is encouraged.
|
||||
*/
|
||||
@Deprecated
|
||||
public void addUnchecked(String identifier, T value) {
|
||||
registry.add(identifier, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(String identifier) {
|
||||
return registry.get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String identifier) {
|
||||
return registry.contains(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<T> consumer) {
|
||||
registry.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<String, T> consumer) {
|
||||
registry.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> entries() {
|
||||
return registry.entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keys() {
|
||||
return registry.keys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T load(Type t, Object c, ConfigLoader loader) throws LoadException {
|
||||
return registry.load(t, c, loader);
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.dfsek.terra.api.registry;
|
||||
|
||||
import com.dfsek.tectonic.exception.LoadException;
|
||||
import com.dfsek.tectonic.loading.ConfigLoader;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Wrapper for a registry that forbids all write access.
|
||||
*
|
||||
* @param <T> Type in registry
|
||||
*/
|
||||
public class LockedRegistry<T> implements Registry<T> {
|
||||
private final Registry<T> registry;
|
||||
|
||||
public LockedRegistry(Registry<T> registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(String identifier) {
|
||||
return registry.get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String identifier) {
|
||||
return registry.contains(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<T> consumer) {
|
||||
registry.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<String, T> consumer) {
|
||||
registry.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> entries() {
|
||||
return registry.entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keys() {
|
||||
return registry.keys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T load(Type t, Object c, ConfigLoader loader) throws LoadException {
|
||||
return registry.load(t, c, loader);
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.dfsek.terra.api.registry;
|
||||
|
||||
import com.dfsek.tectonic.loading.TypeLoader;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface Registry<T> extends TypeLoader<T> {
|
||||
/**
|
||||
* Get a value from the registry.
|
||||
*
|
||||
* @param identifier Identifier of value.
|
||||
* @return Value matching the identifier, {@code null} if no value is present.
|
||||
*/
|
||||
T get(String identifier);
|
||||
|
||||
/**
|
||||
* Check if the registry contains a value.
|
||||
*
|
||||
* @param identifier Identifier of value.
|
||||
* @return Whether the registry contains the value.
|
||||
*/
|
||||
boolean contains(String identifier);
|
||||
|
||||
/**
|
||||
* Perform the given action for every value in the registry.
|
||||
*
|
||||
* @param consumer Action to perform on value.
|
||||
*/
|
||||
void forEach(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Perform an action for every key-value pair in the registry.
|
||||
*
|
||||
* @param consumer Action to perform on pair.
|
||||
*/
|
||||
void forEach(BiConsumer<String, T> consumer);
|
||||
|
||||
/**
|
||||
* Get the entries of this registry as a {@link Set}.
|
||||
*
|
||||
* @return Set containing all entries.
|
||||
*/
|
||||
Set<T> entries();
|
||||
|
||||
/**
|
||||
* Get all the keys in this registry.
|
||||
*
|
||||
* @return Keys in registry
|
||||
*/
|
||||
Set<String> keys();
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.dfsek.terra.api.structures.loot;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.platform.block.MaterialData;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.platform.inventory.Item;
|
||||
import com.dfsek.terra.api.platform.inventory.ItemStack;
|
||||
import com.dfsek.terra.api.structures.loot.functions.AmountFunction;
|
||||
import com.dfsek.terra.api.structures.loot.functions.DamageFunction;
|
||||
@ -19,10 +19,9 @@ import java.util.Random;
|
||||
* Representation of a single item entry within a Loot Table pool.
|
||||
*/
|
||||
public class Entry {
|
||||
private final MaterialData item;
|
||||
private final Item item;
|
||||
private final long weight;
|
||||
private final List<LootFunction> functions = new GlueList<>();
|
||||
private final TerraPlugin main;
|
||||
|
||||
/**
|
||||
* Instantiates an Entry from a JSON representation.
|
||||
@ -30,9 +29,8 @@ public class Entry {
|
||||
* @param entry The JSON Object to instantiate from.
|
||||
*/
|
||||
public Entry(JSONObject entry, TerraPlugin main) {
|
||||
this.main = main;
|
||||
String id = entry.get("name").toString();
|
||||
this.item = main.getWorldHandle().createMaterialData(id);
|
||||
this.item = main.getItemHandle().createItem(id);
|
||||
|
||||
long weight1;
|
||||
try {
|
||||
@ -85,7 +83,7 @@ public class Entry {
|
||||
* @return ItemStack - The ItemStack with all functions applied.
|
||||
*/
|
||||
public ItemStack getItem(Random r) {
|
||||
ItemStack item = main.getItemHandle().newItemStack(this.item, 1);
|
||||
ItemStack item = this.item.newItemStack(1);
|
||||
for(LootFunction f : functions) {
|
||||
item = f.apply(item, r);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.dfsek.terra.api.structures.loot;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.platform.inventory.Inventory;
|
||||
import com.dfsek.terra.api.platform.inventory.ItemStack;
|
||||
import com.dfsek.terra.api.util.GlueList;
|
||||
@ -58,7 +58,8 @@ public class LootTable {
|
||||
for(ItemStack stack : loot) {
|
||||
int attempts = 0;
|
||||
while(stack.getAmount() != 0 && attempts < 10) {
|
||||
ItemStack newStack = stack.clone();
|
||||
ItemStack newStack = stack.getType().newItemStack(stack.getAmount());
|
||||
newStack.setItemMeta(stack.getItemMeta());
|
||||
newStack.setAmount(1);
|
||||
int slot = r.nextInt(i.getSize());
|
||||
ItemStack slotItem = i.getItem(slot);
|
||||
@ -66,7 +67,8 @@ public class LootTable {
|
||||
i.setItem(slot, newStack);
|
||||
stack.setAmount(stack.getAmount() - 1);
|
||||
} else if(slotItem.getType().equals(newStack.getType())) {
|
||||
ItemStack dep = newStack.clone();
|
||||
ItemStack dep = newStack.getType().newItemStack(newStack.getAmount());
|
||||
dep.setItemMeta(newStack.getItemMeta());
|
||||
dep.setAmount(newStack.getAmount() + slotItem.getAmount());
|
||||
i.setItem(slot, dep);
|
||||
stack.setAmount(stack.getAmount() - 1);
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.dfsek.terra.api.structures.loot;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.math.ProbabilityCollection;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.platform.inventory.ItemStack;
|
||||
import com.dfsek.terra.api.util.GlueList;
|
||||
import com.dfsek.terra.api.util.collections.ProbabilityCollection;
|
||||
import net.jafama.FastMath;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
@ -33,8 +33,11 @@ public class DamageFunction implements LootFunction {
|
||||
*/
|
||||
@Override
|
||||
public ItemStack apply(ItemStack original, Random r) {
|
||||
if(original == null) return null;
|
||||
ItemMeta meta = original.getItemMeta();
|
||||
if(!(meta instanceof Damageable)) return original;
|
||||
double itemDurability = (r.nextDouble() * (max - min)) + min;
|
||||
Damageable damage = (Damageable) original.getItemMeta();
|
||||
Damageable damage = (Damageable) meta;
|
||||
damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability()));
|
||||
original.setItemMeta((ItemMeta) damage);
|
||||
return original;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.dfsek.terra.api.structures.loot.functions;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.platform.inventory.ItemStack;
|
||||
import com.dfsek.terra.api.platform.inventory.item.Enchantment;
|
||||
import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
|
||||
@ -35,6 +35,8 @@ public class EnchantFunction implements LootFunction {
|
||||
*/
|
||||
@Override
|
||||
public ItemStack apply(ItemStack original, Random r) {
|
||||
if(original.getItemMeta() == null) return original;
|
||||
|
||||
double enchant = (r.nextDouble() * (max - min)) + min;
|
||||
List<Enchantment> possible = new GlueList<>();
|
||||
for(Enchantment ench : main.getItemHandle().getEnchantments()) {
|
||||
@ -55,7 +57,7 @@ public class EnchantFunction implements LootFunction {
|
||||
try {
|
||||
meta.addEnchantment(chosen, FastMath.max(lvl, 1));
|
||||
} catch(IllegalArgumentException e) {
|
||||
main.getLogger().warning("Attempted to enchant " + original.getType() + " with " + chosen + " at level " + FastMath.max(lvl, 1) + ", but an unexpected exception occurred! Usually this is caused by a misbehaving enchantment plugin.");
|
||||
main.logger().warning("Attempted to enchant " + original.getType() + " with " + chosen + " at level " + FastMath.max(lvl, 1) + ", but an unexpected exception occurred! Usually this is caused by a misbehaving enchantment plugin.");
|
||||
}
|
||||
}
|
||||
original.setItemMeta(meta);
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.dfsek.terra.api.structures.script;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.math.vector.Location;
|
||||
import com.dfsek.terra.api.platform.world.Chunk;
|
||||
import com.dfsek.terra.api.structures.parser.Parser;
|
||||
@ -28,10 +28,9 @@ import com.dfsek.terra.api.structures.structure.Rotation;
|
||||
import com.dfsek.terra.api.structures.structure.buffer.Buffer;
|
||||
import com.dfsek.terra.api.structures.structure.buffer.DirectBuffer;
|
||||
import com.dfsek.terra.api.structures.structure.buffer.StructureBuffer;
|
||||
import com.dfsek.terra.registry.FunctionRegistry;
|
||||
import com.dfsek.terra.registry.config.FunctionRegistry;
|
||||
import com.dfsek.terra.registry.config.LootRegistry;
|
||||
import com.dfsek.terra.registry.config.ScriptRegistry;
|
||||
import com.dfsek.terra.world.generation.math.SamplerCache;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import net.jafama.FastMath;
|
||||
@ -47,9 +46,9 @@ public class StructureScript {
|
||||
private final String id;
|
||||
private final Cache<Location, StructureBuffer> cache;
|
||||
private final TerraPlugin main;
|
||||
String tempID;
|
||||
private String tempID;
|
||||
|
||||
public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, SamplerCache cache, FunctionRegistry functionRegistry) throws ParseException {
|
||||
public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, FunctionRegistry functionRegistry) throws ParseException {
|
||||
Parser parser;
|
||||
try {
|
||||
parser = new Parser(IOUtils.toString(inputStream));
|
||||
@ -57,8 +56,10 @@ public class StructureScript {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
functionRegistry.forEach(parser::registerFunction); // Register registry functions.
|
||||
|
||||
parser.registerFunction("block", new BlockFunctionBuilder(main))
|
||||
.registerFunction("check", new CheckFunctionBuilder(main, cache))
|
||||
.registerFunction("check", new CheckFunctionBuilder(main))
|
||||
.registerFunction("structure", new StructureFunctionBuilder(registry, main))
|
||||
.registerFunction("randomInt", new RandomFunctionBuilder())
|
||||
.registerFunction("recursions", new RecursionsFunctionBuilder())
|
||||
@ -86,8 +87,6 @@ public class StructureScript {
|
||||
.registerFunction("max", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.max(number.doubleValue(), number2.doubleValue())))
|
||||
.registerFunction("min", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue())));
|
||||
|
||||
functionRegistry.forEach(parser::registerFunction); // Register registry functions.
|
||||
|
||||
block = parser.parse();
|
||||
this.id = parser.getID();
|
||||
tempID = id;
|
||||
@ -149,7 +148,7 @@ public class StructureScript {
|
||||
try {
|
||||
return !block.apply(arguments).getLevel().equals(Block.ReturnLevel.FAIL);
|
||||
} catch(RuntimeException e) {
|
||||
main.getLogger().severe("Failed to generate structure at " + arguments.getBuffer().getOrigin() + ": " + e.getMessage());
|
||||
main.logger().severe("Failed to generate structure at " + arguments.getBuffer().getOrigin() + ": " + e.getMessage());
|
||||
main.getDebugLogger().stack(e);
|
||||
return false;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.dfsek.terra.api.structures.script.builders;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.structures.parser.lang.Returnable;
|
||||
import com.dfsek.terra.api.structures.parser.lang.functions.FunctionBuilder;
|
||||
import com.dfsek.terra.api.structures.script.functions.BiomeFunction;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.dfsek.terra.api.structures.script.builders;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.structures.parser.exceptions.ParseException;
|
||||
import com.dfsek.terra.api.structures.parser.lang.Returnable;
|
||||
import com.dfsek.terra.api.structures.parser.lang.constants.BooleanConstant;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user